UNPKG

js-use-core

Version:

JavaScript Comprehensive tool library, including full screen, copy and paste functions

2 lines (1 loc) 31.3 kB
class EventEmitter{constructor(){this.events=new Map,this.maxListeners=10}on(e,t,r){if("function"!=typeof t)throw new Error("Listener must be a function");const s={listener:t,once:r?.once||!1,priority:r?.priority||0};this.events.has(e)||this.events.set(e,[]);const n=this.events.get(e);n.length,this.maxListeners;let o=!1;for(let e=0;e<n.length;e++)if(s.priority>(n[e].priority||0)){n.splice(e,0,s),o=!0;break}return o||n.push(s),this}once(e,t,r){return this.on(e,t,{once:!0,priority:r})}off(e,t){if(!this.events.has(e))return this;const r=this.events.get(e);if(t){const s=r.findIndex(e=>e.listener===t);-1!==s&&(r.splice(s,1),0===r.length&&this.events.delete(e))}else this.events.delete(e);return this}emit(e,...t){if(!this.events.has(e))return!1;const r=this.events.get(e).slice(),s=[];for(const e of r)try{e.listener.apply(this,t),e.once&&s.push(e)}catch(e){}if(s.length>0){const t=this.events.get(e);if(t){for(const e of s){const r=t.indexOf(e);-1!==r&&t.splice(r,1)}0===t.length&&this.events.delete(e)}}return!0}listenerCount(e){return this.events.get(e)?.length||0}listeners(e){return this.events.get(e)?.map(e=>e.listener)||[]}eventNames(){return Array.from(this.events.keys())}removeAllListeners(e){return e?this.events.delete(e):this.events.clear(),this}setMaxListeners(e){if(e<0||!Number.isInteger(e))throw new Error("Max listeners must be a non-negative integer");return this.maxListeners=e,this}getMaxListeners(){return this.maxListeners}prependListener(e,t){return this.on(e,t,{priority:Number.MAX_SAFE_INTEGER})}prependOnceListener(e,t){return this.once(e,t,Number.MAX_SAFE_INTEGER)}}var e,t,r,s;!function(e){e.USER_ERROR="USER_ERROR",e.SYSTEM_ERROR="SYSTEM_ERROR",e.NETWORK_ERROR="NETWORK_ERROR",e.PERMISSION_ERROR="PERMISSION_ERROR",e.CONFIG_ERROR="CONFIG_ERROR",e.VALIDATION_ERROR="VALIDATION_ERROR",e.TIMEOUT_ERROR="TIMEOUT_ERROR",e.UNSUPPORTED_ERROR="UNSUPPORTED_ERROR",e.INTERNAL_ERROR="INTERNAL_ERROR",e.EXTERNAL_ERROR="EXTERNAL_ERROR",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(e||(e={})),function(e){e.LOW="low",e.MEDIUM="medium",e.HIGH="high",e.CRITICAL="critical"}(t||(t={})),function(e){e.USER_ERROR="USER_ERROR",e.SYSTEM_ERROR="SYSTEM_ERROR",e.NETWORK_ERROR="NETWORK_ERROR",e.PERMISSION_ERROR="PERMISSION_ERROR",e.CONFIG_ERROR="CONFIG_ERROR",e.TIMEOUT_ERROR="TIMEOUT_ERROR",e.VALIDATION_ERROR="VALIDATION_ERROR",e.INTERNAL_ERROR="INTERNAL_ERROR",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(r||(r={})),function(e){e[e.DEBUG=0]="DEBUG",e[e.INFO=1]="INFO",e[e.WARN=2]="WARN",e[e.ERROR=3]="ERROR"}(s||(s={}));class Logger{constructor(e="Core",t){this.level=s.INFO,this.logs=[],this.maxLogs=1e3,this.enableConsole=!0,this.module=e,t&&(this.level=t.level??s.INFO,this.maxLogs=t.maxLogs??1e3,this.enableConsole=t.enableConsole??!0)}setLevel(e){this.level=e}getLevel(){return this.level}debug(e,t){this.log(s.DEBUG,e,t)}info(e,t){this.log(s.INFO,e,t)}warn(e,t){this.log(s.WARN,e,t)}error(e,t){this.log(s.ERROR,e,t)}log(e,t,r){if(e<this.level)return;const s={level:e,message:t,timestamp:Date.now(),module:this.module,data:r};this.logs.push(s),this.logs.length>this.maxLogs&&this.logs.shift(),this.enableConsole&&this.outputToConsole(s)}outputToConsole(e){new Date(e.timestamp).toISOString(),s[e.level],e.module,e.message,void 0!==e.data&&e.data;switch(e.level){case s.DEBUG:case s.INFO:case s.WARN:case s.ERROR:}}getLogs(){return[...this.logs]}getLogsByLevel(e){return this.logs.filter(t=>t.level===e)}getLogsByTimeRange(e,t){return this.logs.filter(r=>r.timestamp>=e&&r.timestamp<=t)}clear(){this.logs=[]}setMaxLogs(e){if(e<0)throw new Error("Max logs must be non-negative");this.maxLogs=e,this.logs.length>e&&(this.logs=this.logs.slice(-e))}setConsoleOutput(e){this.enableConsole=e}exportLogs(){return JSON.stringify(this.logs,null,2)}importLogs(e){try{const t=JSON.parse(e);Array.isArray(t)&&(this.logs=t.filter(e=>e&&"number"==typeof e.level&&"string"==typeof e.message&&"number"==typeof e.timestamp))}catch(e){this.error("Failed to import logs",e)}}createChild(e){return new Logger(`${this.module}.${e}`,{level:this.level,maxLogs:this.maxLogs,enableConsole:this.enableConsole})}}class n extends Error{constructor(e,t,r){super(t),this.name="CustomError",this.type=e,this.code=r?.code,this.context=r?.context,this.recoverable=r?.recoverable??!1,r?.cause&&(this.cause=r.cause),Error.captureStackTrace&&Error.captureStackTrace(this,n)}}class ErrorHandler{constructor(e){this.errorSolutions=new Map,this.logger=e||new Logger("ErrorHandler"),this.initializeErrorSolutions()}handleError(e,t){const r=this.classifyError(e),s={module:"Unknown",method:"Unknown",timestamp:Date.now(),userAgent:"undefined"!=typeof navigator?navigator.userAgent:"Node.js",...t},n={type:r,severity:this.getErrorSeverity(r),message:e.message,userMessage:this.getUserFriendlyMessage(e,r),originalError:e,context:s,code:this.getErrorCode(e)||this.generateErrorCode(r),recoverable:this.isRecoverableError(e),solutions:this.getErrorSolutions(e),id:this.generateErrorId(),processedAt:Date.now(),relatedErrors:[]};return this.logger.error(`[${r}] ${n.message}`,{error:e.message,stack:e.stack,context:s,recoverable:n.recoverable}),n}createError(e,t,r){const s={module:"Unknown",method:"Unknown",timestamp:Date.now(),userAgent:"undefined"!=typeof navigator?navigator.userAgent:"Node.js",...r?.context};return new n(e,t,{code:r?.code,context:s,recoverable:r?.recoverable,cause:r?.cause})}isRecoverableError(t){if(t instanceof n)return t.recoverable;switch(this.classifyError(t)){case e.NETWORK_ERROR:case e.TIMEOUT_ERROR:return!0;case e.PERMISSION_ERROR:case e.SYSTEM_ERROR:return!1;case e.USER_ERROR:case e.CONFIG_ERROR:return!0;default:return!1}}getErrorSolution(e){const t=this.getErrorCode(e);if(t&&this.errorSolutions.has(t))return this.errorSolutions.get(t);const r=this.classifyError(e);return this.getDefaultSolution(r)}getErrorSolutions(e){const t=this.getErrorSolution(e);return t?[{description:t,steps:[t],automatic:!1,priority:1}]:[]}getErrorSeverity(r){switch(r){case e.USER_ERROR:case e.CONFIG_ERROR:return t.LOW;case e.NETWORK_ERROR:case e.TIMEOUT_ERROR:return t.MEDIUM;case e.PERMISSION_ERROR:case e.VALIDATION_ERROR:return t.HIGH;case e.SYSTEM_ERROR:case e.INTERNAL_ERROR:return t.CRITICAL;default:return t.MEDIUM}}generateErrorCode(e){return`${e}_${Date.now().toString(36)}_${Math.random().toString(36).substr(2,5)}`.toUpperCase()}generateErrorId(){return`error_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}addErrorSolution(e,t){this.errorSolutions.set(e,t)}addErrorSolutions(e){for(const[t,r]of Object.entries(e))this.errorSolutions.set(t,r)}classifyError(t){if(t instanceof n)return t.type;const r=(t.message||"").toLowerCase(),s=(t.name||"").toLowerCase();return r.includes("network")||r.includes("fetch")||r.includes("xhr")||s.includes("networkerror")?e.NETWORK_ERROR:r.includes("timeout")||r.includes("timed out")||s.includes("timeouterror")?e.TIMEOUT_ERROR:r.includes("permission")||r.includes("denied")||r.includes("unauthorized")||r.includes("forbidden")||s.includes("notallowederror")?e.PERMISSION_ERROR:r.includes("not supported")||r.includes("not available")||r.includes("not implemented")||s.includes("notsupportederror")?e.SYSTEM_ERROR:r.includes("invalid")||r.includes("configuration")||r.includes("config")||s.includes("configerror")?e.CONFIG_ERROR:t instanceof TypeError||t instanceof RangeError||r.includes("invalid argument")||r.includes("invalid parameter")?e.USER_ERROR:e.UNKNOWN_ERROR}getUserFriendlyMessage(t,r){switch(r){case e.NETWORK_ERROR:return"网络连接失败,请检查网络连接后重试";case e.TIMEOUT_ERROR:return"操作超时,请稍后重试";case e.PERMISSION_ERROR:return"权限不足,请检查浏览器权限设置";case e.SYSTEM_ERROR:return"当前浏览器不支持此功能,请使用其他浏览器或升级浏览器版本";case e.CONFIG_ERROR:return"配置参数错误,请检查配置";case e.USER_ERROR:return"输入参数错误,请检查输入参数";default:return t.message||"发生未知错误"}}getErrorCode(e){if(e instanceof n)return e.code;const t=e;return t.code||t.errno||void 0}getDefaultSolution(t){switch(t){case e.NETWORK_ERROR:return"请检查网络连接,确保网络正常后重试";case e.TIMEOUT_ERROR:return"请稍后重试,或增加超时时间设置";case e.PERMISSION_ERROR:return"请在浏览器设置中允许相关权限,或使用HTTPS协议";case e.SYSTEM_ERROR:return"请使用支持此功能的现代浏览器,或升级浏览器版本";case e.CONFIG_ERROR:return"请检查配置参数是否正确,参考文档进行配置";case e.USER_ERROR:return"请检查输入参数的类型和格式是否正确";default:return null}}initializeErrorSolutions(){this.errorSolutions.set("ENOTFOUND","域名解析失败,请检查网络连接"),this.errorSolutions.set("ECONNREFUSED","连接被拒绝,请检查服务器状态"),this.errorSolutions.set("ETIMEDOUT","连接超时,请检查网络连接或稍后重试"),this.errorSolutions.set("CERT_UNTRUSTED","SSL证书不受信任,请检查证书配置"),this.errorSolutions.set("MIXED_CONTENT","混合内容错误,请使用HTTPS协议")}}class Cache{constructor(e){this.cache=new Map,this.config={maxSize:e?.maxSize??100,defaultTTL:e?.defaultTTL??3e5,enableLRU:e?.enableLRU??!0,cleanupInterval:e?.cleanupInterval??6e4},this.startCleanup()}set(e,t,r){const s=Date.now(),n={value:t,expireAt:s+(r??this.config.defaultTTL),createdAt:s,accessCount:0,lastAccessed:s};this.cache.size>=this.config.maxSize&&!this.cache.has(e)&&this.evictLRU(),this.cache.set(e,n)}get(e){const t=this.cache.get(e);if(!t)return;const r=Date.now();if(!(r>t.expireAt))return t.accessCount++,t.lastAccessed=r,t.value;this.cache.delete(e)}has(e){const t=this.cache.get(e);return!!t&&(!(Date.now()>t.expireAt)||(this.cache.delete(e),!1))}delete(e){return this.cache.delete(e)}clear(){this.cache.clear()}size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}getInfo(e){const t=this.cache.get(e);if(t){if(!(Date.now()>t.expireAt))return{...t};this.cache.delete(e)}}touch(e,t){const r=this.cache.get(e);if(!r)return!1;const s=Date.now();if(s>r.expireAt)return this.cache.delete(e),!1;const n=t??this.config.defaultTTL;return r.expireAt=s+n,r.lastAccessed=s,!0}async getOrSet(e,t,r){const s=this.get(e);if(void 0!==s)return s;const n=await t();return this.set(e,n,r),n}mset(e,t){for(const[r,s]of e)this.set(r,s,t)}mget(e){return e.map(e=>this.get(e))}mdel(e){let t=0;for(const r of e)this.delete(r)&&t++;return t}getStats(){let e=0,t=0;const r=Date.now();for(const[s,n]of this.cache.entries())e+=n.accessCount,r>n.expireAt&&t++;return{size:this.cache.size,maxSize:this.config.maxSize,hitRate:e>0?(e-t)/e:0,totalAccess:e,expiredCount:t}}cleanup(){const e=Date.now();let t=0;for(const[r,s]of this.cache.entries())e>s.expireAt&&(this.cache.delete(r),t++);return t}evictLRU(){if(!this.config.enableLRU||0===this.cache.size)return;let e=null,t=1/0;for(const[r,s]of this.cache.entries())s.lastAccessed<t&&(t=s.lastAccessed,e=r);e&&this.cache.delete(e)}startCleanup(){this.cleanupTimer&&clearInterval(this.cleanupTimer),this.cleanupTimer=setInterval(()=>{this.cleanup()},this.config.cleanupInterval)}stopCleanup(){this.cleanupTimer&&(clearInterval(this.cleanupTimer),this.cleanupTimer=void 0)}updateConfig(e){if(this.config={...this.config,...e},void 0!==e.cleanupInterval&&this.startCleanup(),void 0!==e.maxSize&&this.cache.size>e.maxSize)for(;this.cache.size>e.maxSize;)this.evictLRU()}destroy(){this.stopCleanup(),this.clear()}}class BaseManager{constructor(e,t="BaseManager"){this.initialized=!1,this.destroyed=!1,this.initializing=!1,this.options=this.mergeDefaultOptions(e),this.logger=new Logger(t,{level:this.options.debug?0:1,enableConsole:this.options.debug}),this.eventEmitter=new EventEmitter,this.errorHandler=new ErrorHandler(this.logger),this.options.cache&&(this.cache=new Cache),this.setupErrorHandling(),this.startAutoInitialization()}on(e,t,r){return this.eventEmitter.on(e,t,r),this}off(e,t){return this.eventEmitter.off(e,t),this}emit(e,...t){return this.eventEmitter.emit(e,...t)}once(e,t,r){return this.eventEmitter.once(e,t,r),this}listenerCount(e){return this.eventEmitter.listenerCount(e)}eventNames(){return this.eventEmitter.eventNames()}handleError(e,t){const r={module:this.constructor.name,method:t},s=this.errorHandler.handleError(e,r);return this.emit("error",s),s}validateInput(e,t){try{if(t.type){const r=typeof e;if(r!==t.type)throw this.errorHandler.createError("USER_ERROR",`Expected ${t.type}, got ${r}`,{context:{method:"validateInput"}})}if(t.required&&null==e)throw this.errorHandler.createError("USER_ERROR","Required parameter is missing",{context:{method:"validateInput"}});if(t.isArray&&!Array.isArray(e))throw this.errorHandler.createError("USER_ERROR","Expected array",{context:{method:"validateInput"}});if(t.properties&&"object"==typeof e&&null!==e)for(const[r,s]of Object.entries(t.properties))if(!this.validateInput(e[r],s))return!1;return!0}catch(e){return this.handleError(e,"validateInput"),!1}}async safeExecute(e,t,r){const s=r??this.options.retries??0;let n=null;for(let r=0;r<=s;r++)try{const s=new Promise((e,r)=>{setTimeout(()=>{r(this.errorHandler.createError("TIMEOUT_ERROR",`Operation timed out after ${this.options.timeout}ms`,{context:{method:t}}))},this.options.timeout)}),n=await Promise.race([e(),s]);return r>0&&this.logger.info(`Operation succeeded after ${r} retries`,{context:t}),n}catch(e){if(n=e,r===s||!this.errorHandler.isRecoverableError(n))throw this.handleError(n,t);const o=Math.min(1e3*Math.pow(2,r),5e3);this.logger.warn(`Operation failed, retrying in ${o}ms (attempt ${r+1}/${s+1})`,{context:t,error:n.message}),await new Promise(e=>setTimeout(e,o))}throw this.handleError(n,t)}getCached(e){return this.cache?.get(e)}setCached(e,t,r){this.cache?.set(e,t,r)}async getOrSetCached(e,t,r){return this.cache?this.cache.getOrSet(e,t,r):t()}async ensureInitialized(){if(this.destroyed)throw this.errorHandler.createError("SYSTEM_ERROR","Manager has been destroyed and cannot be used.",{context:{method:"ensureInitialized"}});if(!this.initialized)if(this.initPromise)try{await this.initPromise,this.initPromise=void 0}catch(e){throw this.errorHandler.createError("SYSTEM_ERROR",`Manager initialization failed: ${e.message}`,{context:{method:"ensureInitialized"}})}else if(!this.initializing){this.initializing=!0;try{await this.initialize()}finally{this.initializing=!1}}}ensureInitializedSync(){if(!this.initialized)throw this.errorHandler.createError("SYSTEM_ERROR","Manager not initialized. Operations requiring initialization should be awaited.",{context:{method:"ensureInitializedSync"}})}ensureNotDestroyed(){if(this.destroyed)throw this.errorHandler.createError("SYSTEM_ERROR","Manager has been destroyed and cannot be used.",{context:{method:"ensureNotDestroyed"}})}async ready(){await this.ensureInitialized()}getStatus(){return{initialized:this.initialized,destroyed:this.destroyed,eventListeners:this.eventNames().reduce((e,t)=>e+this.listenerCount(t),0),cacheSize:this.cache?.size(),initializing:this.initializing||!!this.initPromise}}updateOptions(e){this.options={...this.options,...e},void 0!==e.debug&&(this.logger.setLevel(e.debug?0:1),this.logger.setConsoleOutput(e.debug)),this.emit("optionsUpdated",this.options)}mergeDefaultOptions(e){return{...this.getDefaultOptions(),...e}}setupErrorHandling(){"undefined"!=typeof window?window.addEventListener("unhandledrejection",e=>{this.handleError(new Error(e.reason),"unhandledrejection")}):"undefined"!=typeof process&&process.on("unhandledRejection",e=>{this.handleError(new Error(String(e)),"unhandledRejection")})}startAutoInitialization(){this.initPromise=this.initialize().catch(e=>(this.logger.error("Auto-initialization failed:",e),Promise.reject(e))),this.initPromise.catch(()=>{})}baseDestroy(){this.destroyed||(this.emit("beforeDestroy"),this.eventEmitter.removeAllListeners(),this.cache?.destroy(),this.logger.clear(),this.destroyed=!0,this.initialized=!1,this.emit("destroyed"))}}let o=null;async function i(e){return o?e&&o.updateOptions(e):(o=new F(e),await o.ready()),o}async function a(e){const t=new F(e);return await t.ready(),t}async function c(e,t){const r=await i(t),s=(await r.check(e)).allFonts.find(t=>t.name===e);return s||{name:e,loaded:!1,status:"not-found"}}async function l(e,t){const r=await i(t);return await r.check(e)}async function d(e,t,r,s){return(await i(s)).addFont(e,t,r)}async function h(e,t){return(await i(t)).addFontFace(e)}async function u(e,t){return(await i(t)).deleteFont(e)}async function m(e){return(await i(e)).clearFonts()}function R(e){return!!document.fonts&&document.fonts.check(`12px '${e}'`)}async function g(e,t){const r=await i({timeout:t});return await r.check(e)}async function f(e,t,r,s,n){try{const o=await i();return s&&o.once("fontLoaded",t=>{t.fontName===e&&s()}),n&&o.once("fontLoadError",t=>{t.fontName===e&&n(t.error,"cors"===t.type)}),o.addFont(e,t,r)}catch(e){return n&&n(e,!1),!1}}function E(e){if(e.startsWith("/")&&!e.startsWith("//"))return!1;try{if(e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//")){const t=new URL(e.startsWith("//")?`https:${e}`:e),r=window.location;return t.hostname!==r.hostname||t.port!==r.port||t.protocol!==r.protocol}return!1}catch(e){return!0}}function p(e){if(!e)return!1;const t=e.toString().toLowerCase();return t.includes("cors")||t.includes("cross-origin")||t.includes("cross origin")||t.includes("networkerror")||t.includes("access-control-allow-origin")}async function y(e,t){const r=await i(t);return await r.addFonts(e)}async function O(e,t){const r=await i(t);return await r.preloadFonts(e)}async function S(e){return(await i(e)).getPerformanceStats()}async function w(e){(await i(e)).cleanup()}class F extends BaseManager{constructor(e={}){super(e,"FontManager"),this.addedFonts=new Set,this.loadingStates=new Map,this.systemFonts=new Set(["Arial","Helvetica","Times New Roman","Times","Courier New","Courier","Verdana","Georgia","Palatino","Garamond","Bookman","Tahoma","Trebuchet MS","Arial Black","Impact","Comic Sans MS","sans-serif","serif","monospace"])}getDefaultOptions(){return{debug:!1,timeout:3e3,retries:2,cache:!0,cacheTTL:3e5,concurrency:5,detectionThreshold:2}}async initialize(){if(!this.initialized)try{if("undefined"==typeof document)throw this.errorHandler.createError(e.SYSTEM_ERROR,"FontManager requires browser environment",{context:{method:"initialize"}});document.fonts||this.logger.warn("CSS Font Loading API not supported, falling back to basic detection"),document.fonts&&document.fonts.ready&&await document.fonts.ready,this.initialized=!0,this.emit("initialized"),this.logger.info("FontManager initialized successfully")}catch(e){throw this.handleError(e,"initialize")}}destroy(){if(!this.destroyed)try{this.clearFonts(),this.loadingStates.clear(),this.baseDestroy(),this.logger.info("FontManager destroyed successfully")}catch(e){this.handleError(e,"destroy")}}async addFont(t,r,s){return await this.ensureInitialized(),this.ensureNotDestroyed(),this.safeExecute(async()=>{if(!this.validateInput(t,{type:"string",required:!0}))throw this.errorHandler.createError(e.USER_ERROR,"Font name must be a non-empty string",{context:{method:"addFont",input:{fontName:t,url:r}}});if(!this.validateInput(r,{type:"string",required:!0}))throw this.errorHandler.createError(e.USER_ERROR,"Font URL must be a non-empty string",{context:{method:"addFont",input:{fontName:t,url:r}}});if(!document.fonts)throw this.errorHandler.createError(e.SYSTEM_ERROR,"CSS Font Loading API not supported in current environment",{context:{method:"addFont"}});if("function"!=typeof FontFace)throw this.errorHandler.createError(e.SYSTEM_ERROR,"FontFace API not supported in current environment",{context:{method:"addFont"}});this.isCrossDomainUrl(r)&&(this.logger.warn(`Loading potentially cross-origin font: ${r}`,{fontName:t,url:r,suggestion:"Ensure target server has proper CORS headers: Access-Control-Allow-Origin"}),this.emit("crossOriginWarning",{fontName:t,url:r}));const n=new FontFace(t,`url(${r})`,s),o={family:t,status:"loading",startTime:Date.now(),retryCount:0};return this.loadingStates.set(t,o),document.fonts.add(n),this.addedFonts.add(n),this.emit("fontAdded",{fontName:t,url:r,fontFace:n}),this.loadFontWithRetry(n,t,r),!0},"addFont").catch(e=>(this.logger.error(`Failed to add font ${t}`,{error:e.message,url:r}),!1))}async loadFontWithRetry(e,t,r){const s=this.loadingStates.get(t);if(s)try{await e.load(),s.status="loaded",s.endTime=Date.now(),this.logger.info(`Font ${t} loaded successfully`,{loadTime:s.endTime-s.startTime}),this.emit("fontLoaded",{fontName:t,url:r,loadTime:s.endTime-s.startTime})}catch(n){if(s.error=n,s.retryCount<this.options.retries)return s.retryCount++,s.status="loading",this.logger.warn(`Font ${t} load failed, retrying (${s.retryCount}/${this.options.retries})`,{error:n.message}),void setTimeout(()=>{this.loadFontWithRetry(e,t,r)},1e3*s.retryCount);s.status="error",s.endTime=Date.now();this.isCORSError(n)||n instanceof Error&&n.toString().includes("NetworkError")?(this.logger.error(`Font ${t} load failed: CORS error`,{url:r,suggestion:"Ensure font server has proper CORS headers: Access-Control-Allow-Origin"}),this.emit("fontLoadError",{fontName:t,url:r,error:n,type:"cors",suggestion:"Check CORS headers on font server"})):(this.logger.error(`Font ${t} load failed`,{error:n.message,url:r}),this.emit("fontLoadError",{fontName:t,url:r,error:n,type:"unknown"}))}}async check(e){return await this.ensureInitialized(),this.ensureNotDestroyed(),this.safeExecute(async()=>{const t=Date.now();let r=[];if(e)if(Array.isArray(e))r=await this.checkMultipleFonts(e);else{r=[await this.checkSingleFont(e)]}else r=await this.checkAllFonts();const s=r.filter(e=>!e.loaded),n=Date.now()-t;return this.logger.debug("Font check completed",{totalFonts:r.length,successCount:r.length-s.length,failedCount:s.length,totalTime:n}),{success:0===s.length,allFonts:r,totalLoadTime:n,...s.length>0?{failedFonts:s}:{}}},"check")}async checkMultipleFonts(e){const t=this.options.concurrency,r=[],s=Array.from(new Set(e)),n=this.sortFontsByPriority(s);for(let e=0;e<n.length;e+=t){const s=n.slice(e,e+t).map(e=>this.checkSingleFont(e).catch(t=>({name:e,loaded:!1,status:"error",error:t.message,loadTime:0}))),o=(await Promise.allSettled(s)).map(e=>"fulfilled"===e.status?e.value:e.reason);r.push(...o),e+t<n.length&&await new Promise(e=>setTimeout(e,10))}return e.map(e=>r.find(t=>t.name===e)||{name:e,loaded:!1,status:"not-found",loadTime:0})}sortFontsByPriority(e){return e.sort((e,t)=>{const r=this.systemFonts.has(e.toLowerCase()),s=this.systemFonts.has(t.toLowerCase()),n=void 0!==this.getCached(`font_check_${e}`),o=void 0!==this.getCached(`font_check_${t}`);return n&&!o?-1:!n&&o?1:r&&!s?-1:!r&&s?1:e.localeCompare(t)})}async checkSingleFont(t){const r=Date.now(),s=`font_check_${t}`,n=this.getCached(s);if(n)return this.logger.debug(`Font check cache hit for ${t}`),n;try{if(!t||"string"!=typeof t)throw this.errorHandler.createError(e.USER_ERROR,"Font name must be a non-empty string",{context:{method:"checkSingleFont",input:t}});const n=t.replace(/['"]/g,""),o=this.systemFonts.has(n.toLowerCase());let i;const a=this.loadingStates.get(t);return i=a?"loaded"===a.status?{name:t,loaded:!0,status:"loaded",loadTime:a.endTime?a.endTime-a.startTime:void 0}:"loading"===a.status?{name:t,loaded:!1,status:"loading"}:{name:t,loaded:!1,status:a.status,error:a.error?.message}:await this.performFontCheck(t,o),i.loadTime=Date.now()-r,(i.loaded||"not-available"===i.status)&&this.setCached(s,i,3e4),i}catch(e){return{name:t,loaded:!1,status:"error",error:this.handleError(e,"checkSingleFont").message,loadTime:Date.now()-r}}}async performFontCheck(e,t){try{document.fonts&&document.fonts.ready&&await document.fonts.ready;if(document.fonts&&document.fonts.check(`12px '${e}'`)){if(!t){let t=!1;if(document.fonts&&document.fonts.forEach(r=>{r.family.replace(/['"]/g,"").toLowerCase()===e.replace(/['"]/g,"").toLowerCase()&&(t=!0)}),!t){if(!await this.performFontDetectionTest(e))return{name:e,loaded:!1,status:"not-available"}}}return{name:e,loaded:!0,status:"loaded"}}return"function"==typeof FontFace?await this.checkWithFontFaceAPI(e,t):{name:e,loaded:!1,status:"unloaded"}}catch(t){return this.logger.warn(`Font check failed for ${e}`,{error:t.message}),{name:e,loaded:!1,status:"error",error:t.message}}}async checkWithFontFaceAPI(e,t){try{const r=new FontFace(e,`local(${e})`),s=new Promise((e,t)=>{setTimeout(()=>t(new Error("Font load timeout")),this.options.timeout)});if(await Promise.race([r.load(),s]).catch(()=>null)){if(!t){if(!await this.performFontDetectionTest(e))return{name:e,loaded:!1,status:"fallback-detected"}}return{name:e,loaded:!0,status:"loaded"}}return document.fonts&&document.fonts.check(`12px '${e}'`)&&t?{name:e,loaded:!0,status:"loaded"}:{name:e,loaded:!1,status:"error"}}catch(r){const s=document.fonts&&document.fonts.check(`12px '${e}'`);if(s&&!t){const t=await this.performFontDetectionTest(e);return{name:e,loaded:t,status:t?"loaded":"fallback-to-system"}}return{name:e,loaded:s||!1,status:s?"loaded":"fallback"}}}async performFontDetectionTest(e){if("undefined"==typeof document||"undefined"==typeof window)return!1;try{const t=`font_detection_${e}`,r=this.getCached(t);if(void 0!==r)return r;const s=document.createElement("div");s.style.position="absolute",s.style.left="-9999px",s.style.top="-9999px",s.style.visibility="hidden",s.style.whiteSpace="nowrap",s.style.fontSize="16px",s.style.lineHeight="1";const n=["mmmmmmmmmmlli","ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz","1234567890"];document.body.appendChild(s);let o=[];for(const t of n){s.textContent=t,s.style.fontFamily="sans-serif";const r=s.offsetWidth;s.style.fontFamily=`'${e}', sans-serif`;const n=s.offsetWidth,i=Math.abs(n-r),a=r>0?i/r*100:0;o.push(a>this.options.detectionThreshold)}document.body.removeChild(s);const i=o.filter(e=>e).length,a=i>=Math.ceil(o.length/2);return this.setCached(t,a,6e4),this.logger.debug(`Font detection test for ${e}`,{testResults:o,positiveResults:i,threshold:this.options.detectionThreshold,isLoaded:a}),a}catch(t){return this.logger.warn(`Font detection test failed for ${e}`,{error:t.message}),!1}}async checkAllFonts(){try{document.fonts&&document.fonts.ready&&await document.fonts.ready;const e=[],t=new Set;return document.fonts&&document.fonts.forEach(r=>{const s=r.family.replace(/['"]*/g,"");if(!t.has(s)){t.add(s);const r=this.loadingStates.get(s);e.push({name:s,loaded:!0,status:"loaded"===r?.status?"loaded":"system",loadTime:r?.endTime&&r?.startTime?r.endTime-r.startTime:void 0})}}),this.logger.debug(`Found ${e.length} fonts in document.fonts`),e}catch(e){return this.logger.error("Failed to check all fonts",{error:e.message}),[]}}addFontFace(t){this.ensureInitializedSync(),this.ensureNotDestroyed();try{if(!document.fonts)throw this.errorHandler.createError(e.SYSTEM_ERROR,"CSS Font Loading API not supported",{context:{method:"addFontFace"}});return document.fonts.add(t),this.addedFonts.add(t),this.emit("fontFaceAdded",{fontFace:t}),this.logger.debug(`FontFace added: ${t.family}`),!0}catch(e){return this.handleError(e,"addFontFace"),!1}}deleteFont(t){this.ensureInitializedSync(),this.ensureNotDestroyed();try{if(!document.fonts)throw this.errorHandler.createError(e.SYSTEM_ERROR,"CSS Font Loading API not supported",{context:{method:"deleteFont"}});if("string"==typeof t){const e=[];return this.addedFonts.forEach(r=>{r.family===t&&e.push(r)}),e.length>0?(e.forEach(e=>{document.fonts.delete(e),this.addedFonts.delete(e)}),this.loadingStates.delete(t),this.emit("fontDeleted",{fontName:t,count:e.length}),this.logger.debug(`Deleted ${e.length} fonts with name: ${t}`),!0):(this.logger.warn(`Font not found: ${t}`),!1)}const r=document.fonts.delete(t);return r&&(this.addedFonts.delete(t),this.loadingStates.delete(t.family),this.emit("fontDeleted",{fontFace:t}),this.logger.debug(`FontFace deleted: ${t.family}`)),r}catch(e){return this.handleError(e,"deleteFont"),!1}}clearFonts(){this.ensureNotDestroyed();try{if(!document.fonts)throw this.errorHandler.createError(e.SYSTEM_ERROR,"CSS Font Loading API not supported",{context:{method:"clearFonts"}});const t=this.addedFonts.size;return this.addedFonts.forEach(e=>{document.fonts.delete(e)}),this.addedFonts.clear(),this.loadingStates.clear(),this.emit("fontsCleared",{count:t}),this.logger.info(`Cleared ${t} fonts`),!0}catch(e){return this.handleError(e,"clearFonts"),!1}}getFontLoadState(e){return this.loadingStates.get(e)}getAllFontLoadStates(){return new Map(this.loadingStates)}async addFonts(e){await this.ensureInitialized(),this.ensureNotDestroyed();const t=[],r=Math.min(this.options.concurrency,e.length);for(let s=0;s<e.length;s+=r){const n=e.slice(s,s+r).map(async e=>{try{const t=await Promise.resolve(this.addFont(e.name,e.url,e.options));return{name:e.name,success:t}}catch(t){return{name:e.name,success:!1,error:t.message}}}),o=(await Promise.allSettled(n)).map(e=>"fulfilled"===e.status?e.value:{name:"unknown",success:!1,error:"Promise rejected"});t.push(...o),s+r<e.length&&await new Promise(e=>setTimeout(e,50))}return this.emit("batchFontsAdded",{results:t,total:e.length}),t}async preloadFonts(e){await this.ensureInitialized(),this.ensureNotDestroyed();const t=[],r=[],s=[];for(const n of e){const e=`font_check_${n}`,o=this.getCached(e);o&&(s.push(n),o.loaded?t.push(n):r.push(n))}const n=e.filter(e=>!s.includes(e));if(n.length>0){const e=await this.checkMultipleFonts(n);for(const s of e)s.loaded?t.push(s.name):r.push(s.name)}return this.emit("fontsPreloaded",{total:e.length,available:t.length,unavailable:r.length,cached:s.length}),{available:t,unavailable:r,cached:s}}cleanup(){const e=Date.now()-36e5;for(const[t,r]of this.loadingStates.entries())r.startTime<e&&("loaded"===r.status||"error"===r.status)&&this.loadingStates.delete(t);this.logger.debug("Font manager cleanup completed",{remainingStates:this.loadingStates.size})}getPerformanceStats(){const e=Array.from(this.loadingStates.values()),t=e.filter(e=>"loading"===e.status).length,r=e.filter(e=>"loaded"===e.status).length,s=e.filter(e=>"error"===e.status).length,n=e.filter(e=>e.endTime),o=n.reduce((e,t)=>e+(t.endTime-t.startTime),0),i=n.length>0?o/n.length:0,a=this.cache?.size()||0,c=e.length,l=c>0?Math.min(a/c,1):0;return{totalFontsChecked:c,cacheHitRate:Math.round(100*l)/100,averageCheckTime:Math.round(i),loadingFonts:t,loadedFonts:r,failedFonts:s}}isCrossDomainUrl(e){if(!e)return!1;if(e.startsWith("/")&&!e.startsWith("//"))return!1;try{if(e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//")){const t=new URL(e.startsWith("//")?`https:${e}`:e),r=window.location;return t.hostname!==r.hostname||t.port!==r.port||t.protocol!==r.protocol}return!1}catch(e){return!0}}isCORSError(e){if(!e)return!1;const t=e.toString().toLowerCase();return t.includes("cors")||t.includes("cross-origin")||t.includes("cross origin")||t.includes("networkerror")||t.includes("access-control-allow-origin")}}export{F as Font,F as FontManager,d as addFont,h as addFontFace,y as addFonts,c as checkFont,l as checkFonts,w as cleanupFontManager,m as clearFonts,a as createFont,F as default,u as deleteFont,S as getFontPerformanceStats,E as isCrossDomainUrl,p as isFontCORSError,R as isFontLoaded,f as loadFont,O as preloadFonts,g as waitForFonts};