harmony-plugin-manager
Version:
A comprehensive TypeScript library for generating harmonious color palettes with WCAG 2.1 accessibility compliance
24 lines (23 loc) • 29 kB
JavaScript
/*!
* harmony-plugin-manager v1.0.0
* A comprehensive TypeScript library for generating harmonious color palettes with WCAG 2.1 accessibility compliance
*
* Copyright (c) 2025 Khaled Sameer <khaled.smq@hotmail.com>
* Licensed under MIT
*
* Repository: https://github.com/KhaledSMQ/harmony-plugin-manager.git
*
* Built: 2025-06-13T17:20:53.177Z
*/
class BasePlugin{async initialize(e){e.logger.debug(`Initializing plugin: ${this.metadata.name}`)}async cleanup(e){e.logger.debug(`Cleaning up plugin: ${this.metadata.name}`)}async onError(e,t){t.logger.error(`Plugin ${this.metadata.name} error: ${e.message}`,{pluginName:this.metadata.name,error:e.message,stack:e.stack})}logInfo(e,t,r){e.logger.info(`[${this.metadata.name}] ${t}`,r)}logWarning(e,t,r){e.logger.warn(`[${this.metadata.name}] ${t}`,r),e.addWarning(`PLUGIN_${this.metadata.name.toUpperCase()}`,t,r)}logError(e,t,r){e.logger.error(`[${this.metadata.name}] ${t}`,r)}}
/*!
* harmony-pipeline v1.0.1
* A robust TypeScript pipeline execution library with stage-based processing, dependency resolution, and comprehensive error handling
*
* Copyright (c) 2025 Khaled Sameer <khaled.smq@hotmail.com>
* Licensed under MIT
*
* Repository: https://github.com/KhaledSMQ/harmony-pipeline.git
*
* Built: 2025-06-12T11:21:14.415Z
*/const deepFreeze=e=>(Object.keys(e).forEach((t=>{"object"!=typeof e[t]||Object.isFrozen(e[t])||deepFreeze(e[t])})),Object.freeze(e)),e=deepFreeze({debug(){},info(){},warn(){},error(){}});class PipelineCancellationError extends Error{constructor(e,t){super(e),this.reason=t,this.name="PipelineCancellationError"}}class EventEmitter{constructor(){this.listeners={}}on(e,t){const r=this.listeners[e]??[];return this.listeners[e]=[...r,t],()=>this.off(e,t)}off(e,t){const r=this.listeners[e];r&&(this.listeners[e]=r.filter((e=>e!==t)))}emit(e,t){const r=this.listeners[e];r&&r.forEach((e=>{try{e(t)}catch(e){console.error("Event listener error:",e)}}))}}class CancellableProcessorExecution{constructor(e,t){this.processor=e,this.signal=t,this.checkInterval=50}async execute(e,t){if(this.signal?.aborted)throw this.createCancellationError();const r=Promise.resolve(this.processor.process(e,t,this.signal));if(!this.signal)return r;const n=this.createCancellationChecker();try{const e=await Promise.race([r,n]);if("CANCELLED"===e)throw this.createCancellationError();return e}finally{this.cleanupCancellationChecker()}}createCancellationChecker(){return new Promise((e=>{const checkCancellation=()=>{this.signal?.aborted?e("CANCELLED"):this.cancellationTimeout=setTimeout(checkCancellation,this.checkInterval)};this.cancellationTimeout=setTimeout(checkCancellation,this.checkInterval)}))}cleanupCancellationChecker(){this.cancellationTimeout&&(clearTimeout(this.cancellationTimeout),this.cancellationTimeout=void 0)}createCancellationError(){const e=this.signal?.reason;return e instanceof Error&&e.message.includes("timeout")?new PipelineCancellationError(`Processor '${this.processor.name}' was cancelled due to timeout`,"timeout"):new PipelineCancellationError(`Processor '${this.processor.name}' was cancelled`,"signal")}}class PipelineExecutor{constructor(){this.processors=new Map,this.stages=new Map,this.stageProcessors=new Map,this.events=new EventEmitter,this.setupProcessors=new Set,this.on=this.events.on.bind(this.events)}register(...e){for(const t of e)this.validateAndRegisterProcessor(t);return this}async execute(e,t,r={}){const n=this.initializeExecution(r),i=this.setupWarningCollection(n);try{this.events.emit("pipelineStart",void 0),this.checkForCancellation(n.combinedSignal),await this.ensureProcessorsSetup(t,n.combinedSignal);const r=await this.executeStages(e,t,n);return this.events.emit("pipelineEnd",r),r}catch(e){const t=this.processExecutionError(e);t instanceof PipelineCancellationError&&this.events.emit("cancelled",{reason:t.reason,message:t.message});const r=this.createPipelineResult(void 0,{...n,errors:[...n.errors,t]});return this.events.emit("pipelineEnd",r),r}finally{i()}}getRegisteredProcessorNames(){return Array.from(this.processors.keys())}getStageExecutionOrder(){return this.resolveStageExecutionOrder().map((e=>e.name))}validateAndRegisterProcessor(e){if(this.processors.has(e.name))throw new Error(`Processor '${e.name}' is already registered. Each processor must have a unique name within the executor.`);this.processors.set(e.name,e),this.stages.set(e.stage.name,e.stage);const t=this.stageProcessors.get(e.stage.name)??[];t.push(e),this.stageProcessors.set(e.stage.name,t)}initializeExecution(t){const{stopOnError:r=!0,concurrency:n=1,signal:i,timeoutMs:s,logger:a=e}=t;return{startTime:Date.now(),warnings:[],errors:[],stageOutcomes:[],stopOnError:r,concurrency:n,combinedSignal:this.createCombinedAbortSignal(i,s),logger:a}}createCombinedAbortSignal(e,t){const r=[];if(e&&r.push(e),null!=t){const e=new AbortController;setTimeout((()=>{e.abort(new Error(`Pipeline timeout of ${t}ms exceeded`))}),t),r.push(e.signal)}return this.mergeAbortSignals(...r)}setupWarningCollection(e){return this.on("warning",(t=>e.warnings.push(t)))}async ensureProcessorsSetup(e,t){const r=[];for(const n of this.processors.values())if(n.setup&&!this.setupProcessors.has(n.name)){this.checkForCancellation(t);const i=Promise.resolve(n.setup(e)).then((()=>{this.setupProcessors.add(n.name)}));r.push(i)}r.length>0&&await Promise.all(r)}async executeStages(e,t,r){let n=e;for(const e of this.resolveStageExecutionOrder()){if(this.checkForCancellation(r.combinedSignal),this.shouldSkipStage(e,t,r))continue;this.events.emit("stageStart",{stage:e.name});const i=await this.executeStage(e,n,t,r);r.stageOutcomes.push(...i.outcomes);for(const e of i.outcomes)for(const t of e.warnings)this.events.emit("warning",t);if(i.error){if(r.errors.push(i.error),r.stopOnError)break}else n=i.output;this.events.emit("stageEnd",{stage:e.name})}return this.createPipelineResult(n,r)}shouldSkipStage(e,t,r){return!(!e.canExecute||e.canExecute(t)||(r.logger.debug(`Skipping stage '${e.name}' (canExecute returned false)`),0))}async executeStage(e,t,r,n){const i=this.stageProcessors.get(e.name)??[];return 0===i.length?{outcomes:[],output:t,error:void 0}:n.concurrency<=1?this.executeProcessorsSequentially(i,t,r,n):this.executeProcessorsConcurrently(i,t,r,n)}async executeProcessorsSequentially(e,t,r,n){const i=[];let s,a=t;for(const t of e){this.checkForCancellation(n.combinedSignal);const e=await this.executeProcessor(t,a,r,n);if(i.push(e),"ok"===e.kind)a=e.output;else if(s=e.error,n.stopOnError)break}return{outcomes:i,output:a,error:s}}async executeProcessorsConcurrently(e,t,r,n){const i=e.map((e=>this.executeProcessor(e,t,r,n))),s=await Promise.all(i),a=s.find((e=>"err"===e.kind))?.error,o=s.filter((e=>"ok"===e.kind)).pop();return{outcomes:s,output:o?o.output:t,error:a}}async executeProcessor(e,t,r,n){const i=Date.now(),s=r._getWarnings().length;this.events.emit("processorStart",{stage:e.stage.name,processor:e.name});try{const a=new CancellableProcessorExecution(e,n.combinedSignal),o=await a.execute(t,r),c=Date.now()-i,l=r._getWarnings().slice(s),u={kind:"ok",stageName:e.stage.name,processorName:e.name,output:o,warnings:l,elapsed:c};return this.events.emit("processorEnd",{stage:e.stage.name,processor:e.name,outcome:u}),u}catch(t){const a=this.processExecutionError(t);try{await(e.onError?.(a,r))}catch(t){n.logger.error(`Error handler for processor '${e.name}' failed`,t)}const o=Date.now()-i,c=r._getWarnings().slice(s),l={kind:"err",stageName:e.stage.name,processorName:e.name,error:a,warnings:c,elapsed:o};return this.events.emit("error",a),this.events.emit("processorEnd",{stage:e.stage.name,processor:e.name,outcome:l}),l}}processExecutionError(e){return e instanceof PipelineCancellationError||e instanceof Error?e:new Error(String(e))}resolveStageExecutionOrder(){const e=new Map;return this.stages.forEach(((t,r)=>{e.set(r,t.dependencies??[])})),this.topologicalSort(e)}topologicalSort(e){const t=[],r=new Set,n=new Set,visit=i=>{if(n.has(i))throw new Error(`Circular dependency detected in stage dependencies involving '${i}'. Ensure stage dependencies form a valid DAG (Directed Acyclic Graph).`);!r.has(i)&&e.has(i)&&(n.add(i),(e.get(i)??[]).forEach(visit),n.delete(i),r.add(i),t.push(i))};return Array.from(e.keys()).forEach(visit),t.map((e=>this.stages.get(e)))}mergeAbortSignals(...e){const t=e.filter((e=>null!=e));if(0===t.length)return;if(1===t.length)return t[0];const r=new AbortController;return t.forEach((e=>{e.aborted?r.abort(e.reason):e.addEventListener("abort",(()=>{return t=e.reason,void(r.signal.aborted||r.abort(t));var t}),{once:!0})})),r.signal}checkForCancellation(e){if(e?.aborted){const t=e.reason;if(t instanceof Error&&t.message.includes("timeout"))throw new PipelineCancellationError("Pipeline execution timed out","timeout");throw new PipelineCancellationError("Pipeline execution was cancelled","signal")}}createPipelineResult(e,t){const r=t.errors.length>0,n=t.errors.some((e=>e instanceof PipelineCancellationError));let i;if(n){const e=t.errors.find((e=>e instanceof PipelineCancellationError));i=e?.reason}return{success:!r,output:r?void 0:e,errors:Object.freeze([...t.errors]),warnings:Object.freeze([...t.warnings]),stages:Object.freeze([...t.stageOutcomes]),executionTime:Date.now()-t.startTime,wasCancelled:n,cancellationReason:i}}}class PipelineBuilder{constructor(){this.registeredProcessors=new Map}withProcessor(...e){return this.validateAndRegisterProcessors(e),this}withProcessors(e){return this.withProcessor(...e)}withProcessorIf(e,t){return e&&this.withProcessor(t),this}withProcessorFactory(e){return this.withProcessor(e())}getRegisteredProcessorNames(){return Array.from(this.registeredProcessors.keys())}getStageExecutionOrder(){return this.createExecutorInstance().getStageExecutionOrder()}validate(){this.validateProcessorCount(),this.validateDependencyGraph()}build(e={}){return(e.validate??1)&&this.validate(),this.createExecutorInstance()}clone(){const e=new PipelineBuilder;return this.copyProcessorsTo(e),e}validateAndRegisterProcessors(e){for(const t of e)this.validateProcessorNotDuplicate(t),this.registeredProcessors.set(t.name,t)}validateProcessorNotDuplicate(e){if(this.registeredProcessors.has(e.name))throw new Error(`Processor '${e.name}' is already registered. Each processor must have a unique name within the builder.`)}createExecutorInstance(){const e=new PipelineExecutor,t=Array.from(this.registeredProcessors.values());return t.length>0&&e.register(...t),e}validateProcessorCount(){if(0===this.registeredProcessors.size)throw new Error("Pipeline must have at least one processor")}validateDependencyGraph(){try{this.validateMissingDependencies(),this.getStageExecutionOrder()}catch(e){const t=e instanceof Error?e.message:String(e);throw new Error(t)}}validateMissingDependencies(){const e=new Set,t=new Set;for(const r of this.registeredProcessors.values())e.add(r.stage.name),r.stage.dependencies&&r.stage.dependencies.forEach((e=>t.add(e)));const r=Array.from(t).filter((t=>!e.has(t)));if(r.length>0)throw new Error(`Missing stages for dependencies: ${r.join(", ")}`)}copyProcessorsTo(e){for(const t of this.registeredProcessors.values())e.registeredProcessors.set(t.name,t)}}class PluginProcessor{constructor(e,t=[]){this.plugin=e,this.dependencies=t,this.version="1.0.0",this.name=`plugin-${e.metadata.name}`,this.stage=function(e,t={}){return Object.freeze({name:e,dependencies:t?.dependencies?Object.freeze([...t.dependencies]):[],canExecute:t.canExecute?t.canExecute:()=>!0})}(`stage-${e.metadata.name}`,{dependencies:t.map((e=>`stage-${e}`)),canExecute:e=>this.canExecute(e)})}canExecute(e){const t=e.retrieve("failed-plugins")||new Set;return!this.dependencies.some((e=>t.has(e)))}async setup(e){await this.plugin.initialize(e)}async process(e,t){try{return await this.plugin.execute(e,t)}catch(e){throw this.markPluginAsFailed(t),e}}async teardown(e){await this.plugin.cleanup(e)}async onError(e,t){await this.plugin.onError(e,t)}markPluginAsFailed(e){const t=e.retrieve("failed-plugins")||new Set;t.add(this.plugin.metadata.name),e.share("failed-plugins",t)}}class PluginFactory{static create(e,t,r){return new class extends BasePlugin{constructor(){super(...arguments),this.metadata=e}async execute(e,r){return t(e,r)}async initialize(e){await super.initialize(e),r?.initialize&&await r.initialize(e)}async cleanup(e){r?.cleanup&&await r.cleanup(e),await super.cleanup(e)}async onError(e,t){r?.onError?await r.onError(e,t):await super.onError(e,t)}}}static batch(e,t,r){const n=r?.batchSize||10,i=r?.parallel||!1;return this.create({name:`${e}-batch`,version:r?.version||"1.0.0",description:r?.description||`Batch processing plugin for ${e}`,tags:["batch","processing"]},(async(e,r)=>{const s=[];for(let t=0;t<e.length;t+=n)s.push(e.slice(t,t+n));if(i)return(await Promise.all(s.map((e=>t(e,r))))).flat();{const e=[];for(const n of s){const i=await t(n,r);e.push(...i)}return e}}))}static conditional(e,t,r,n){return this.create({name:`${e}-conditional`,version:n?.version||"1.0.0",description:n?.description||`Conditional execution plugin for ${e}`,tags:["conditional","control-flow"]},(async(e,n)=>await r.condition(e,n)?await t(e,n):(r.onSkip&&await r.onSkip(e,n),void 0!==r.skipValue?r.skipValue:e)))}static withRetry(e,t,r,n){return this.create({name:`${e}-retry`,version:n?.version||"1.0.0",description:n?.description||`Retry wrapper plugin for ${e}`,tags:["retry","resilience"]},(async(e,n)=>{let i;for(let s=1;s<=r.maxAttempts;s++)try{return await t(e,n)}catch(e){if(i=e,r.retryOn&&!r.retryOn(i))throw i;if(s===r.maxAttempts)throw i;r.onRetry&&await r.onRetry(s,i,n),r.backoffMs&&await new Promise((e=>setTimeout(e,r.backoffMs*s)))}throw i}))}static withCache(e,t,r,n){const i=new Map,s=r.maxSize||1e3,a=r.ttlMs||3e5;return this.create({name:`${e}-cached`,version:n?.version||"1.0.0",description:n?.description||`Cached execution plugin for ${e}`,tags:["cache","performance"]},(async(e,n)=>{const o=r.keyGenerator(e,n),c=Date.now(),l=i.get(o);if(l&&c-l.timestamp<a)return n.logger.debug(`Cache hit for key: ${o}`),l.value;const u=await t(e,n);if(i.size>=s){const e=i.keys().next().value;e&&i.delete(e)}return i.set(o,{value:u,timestamp:c}),n.logger.debug(`Cached result for key: ${o}`),u}),{cleanup:()=>{i.clear()}})}static withThrottle(e,t,r,n){let i=0;const s=[],processQueue=async()=>{if(i>=r.maxConcurrent||0===s.length)return;const e=s.shift();i++;try{const r=await t(e.input,e.context);e.resolve(r)}catch(t){e.reject(t)}finally{i--,processQueue()}};return this.create({name:`${e}-throttled`,version:n?.version||"1.0.0",description:n?.description||`Throttled execution plugin for ${e}`,tags:["throttle","concurrency"]},(async(e,t)=>new Promise(((n,i)=>{const a=r.queueSize||100;s.length>=a?i(new Error(`Queue is full (max: ${a})`)):(s.push({input:e,context:t,resolve:n,reject:i}),r.timeout&&setTimeout((()=>{const e=s.findIndex((e=>e.resolve===n));-1!==e&&(s.splice(e,1),i(new Error("Request timeout")))}),r.timeout),processQueue())}))))}static compose(e,t,r){return this.create({name:`${e}-composed`,version:r?.version||"1.0.0",description:r?.description||`Composed pipeline plugin for ${e}`,tags:["composition","pipeline"],dependencies:t.map((e=>e.metadata.name))},(async(e,n)=>{let i=e;for(const e of t)try{i=await e.execute(i,n)}catch(t){if(!1!==r?.failFast)throw t;n.addWarning("PLUGIN_FAILED",`Plugin ${e.metadata.name} failed in composition`,{error:t.message})}return i}))}static parallel(e,t,r){return this.create({name:`${e}-parallel`,version:r?.version||"1.0.0",description:r?.description||`Parallel execution plugin for ${e}`,tags:["parallel","concurrent"],dependencies:t.map((e=>e.metadata.name))},(async(e,n)=>{const i=t.map((async t=>{try{return await t.execute(e,n)}catch(e){if(r?.failFast)throw e;return n.addWarning("PLUGIN_FAILED",`Plugin ${t.metadata.name} failed in parallel execution`,{error:e.message}),null}})),s=(await Promise.all(i)).filter((e=>null!==e));return r?.aggregator?r.aggregator(s):1===s.length?s[0]:s}))}static validation(e,t,r){return this.create({name:`${e}-validator`,version:r?.version||"1.0.0",description:r?.description||`Validation plugin for ${e}`,tags:["validation"]},(async(e,n)=>{try{return await t(e,n)}catch(t){if(!1!==r?.stopOnError)throw t;return n.addWarning("VALIDATION_FAILED",`Validation failed: ${t.message}`),e}}))}static transform(e,t,r){return this.create({name:`${e}-transformer`,version:r?.version||"1.0.0",description:r?.description||`Transformation plugin for ${e}`,dependencies:r?.dependencies,tags:["transformation"]},t)}static middleware(e,t,r){return this.create({name:`${e}-middleware`,version:r?.version||"1.0.0",description:r?.description||`Middleware plugin for ${e}`,tags:["middleware"]},(async(e,r)=>t(e,r,(async()=>e))))}static withMonitoring(e,t,r){return this.create({name:`${e}-monitored`,version:r?.version||"1.0.0",description:r?.description||`Monitored execution plugin for ${e}`,tags:["monitoring","observability"]},(async(e,n)=>{const i=Date.now();r?.onStart&&await r.onStart(e,n);try{const s=await t(e,n),a=Date.now()-i;return r?.onSuccess&&await r.onSuccess(s,a,n),s}catch(e){const t=Date.now()-i;throw r?.onError&&await r.onError(e,t,n),e}}))}static withCircuitBreaker(e,t,r){let n=0,i=0,s=!1;const a=r.monitoringWindowMs||6e4;return this.create({name:`${e}-circuit-breaker`,version:r?.version||"1.0.0",description:r?.description||`Circuit breaker plugin for ${e}`,tags:["circuit-breaker","resilience"]},(async(o,c)=>{const l=Date.now();if(l-i>a&&(n=0,s=!1),s&&l-i>r.resetTimeoutMs&&(s=!1,n=0,c.logger.info(`Circuit breaker reset for ${e}`)),s)throw new Error(`Circuit breaker is open for ${e}`);try{const e=await t(o,c);return n=0,e}catch(t){throw n++,i=l,n>=r.failureThreshold&&(s=!0,c.logger.warn(`Circuit breaker opened for ${e} after ${n} failures`)),t}}))}static withRateLimit(e,t,r){const n=r.burstSize||r.requestsPerSecond,i=r.queueSize||100,s=1e3/r.requestsPerSecond;let a=n,o=Date.now();const c=[],processQueue=async()=>{for((()=>{const e=Date.now(),t=e-o,r=Math.floor(t/s);r>0&&(a=Math.min(n,a+r),o=e)})();a>0&&c.length>0;){const e=c.shift();a--;try{const r=await t(e.input,e.context);e.resolve(r)}catch(t){e.reject(t)}}c.length>0&&setTimeout(processQueue,s)};return this.create({name:`${e}-rate-limited`,version:r?.version||"1.0.0",description:r?.description||`Rate limited plugin for ${e}`,tags:["rate-limiting","throttling"]},(async(e,t)=>new Promise(((r,n)=>{c.length>=i?n(new Error(`Rate limit queue is full (max: ${i})`)):(c.push({input:e,context:t,resolve:r,reject:n}),processQueue())}))))}}class PluginContextImpl{constructor(e,t){this.baseContext=e,this.pluginSettings=t,this._shared=new Map}get executionId(){return this.baseContext.executionId}get startTime(){return this.baseContext.startTime}get metadata(){return this.baseContext.metadata}get logger(){return this.baseContext.logger}addWarning(e,t,r){this.baseContext.addWarning(e,t,r)}_getWarnings(){return this.baseContext._getWarnings()}get settings(){return this.pluginSettings}get shared(){return this._shared}getSettings(e){return this.pluginSettings.get(e)}share(e,t){this._shared.set(e,t)}retrieve(e){return this._shared.get(e)}hasShared(e){return this._shared.has(e)}deleteShared(e){return this._shared.delete(e)}clearShared(){this._shared.clear()}getSharedKeys(){return Array.from(this._shared.keys())}}class ContextFactory{static create(t,r){const n=function(t,r=e){const n=[],i="undefined"!=typeof crypto&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).substring(2,15)+Math.random().toString(36).substring(2,15);return Object.freeze({executionId:i,startTime:Date.now(),metadata:deepFreeze(t),logger:r,addWarning(e,t,i){const s={code:e,message:t,details:i,timestamp:Date.now()};n.push(s),r.warn(`${e}: ${t}`,i)},_getWarnings:()=>deepFreeze([...n])})}(t);return new PluginContextImpl(n,r)}static createWithSettings(e,t){const r=new Map(t.map((e=>[e.name,e.settings])));return this.create(e,r)}}class PluginRegistration{constructor(e,t,r=Date.now()){this.plugin=e,this.settings=t,this.registeredAt=r}get name(){return this.plugin.metadata.name}get isEnabled(){return this.settings.enabled}get priority(){return this.settings.priority}get dependencies(){return this.plugin.metadata.dependencies}get version(){return this.plugin.metadata.version}get description(){return this.plugin.metadata.description}get tags(){return this.plugin.metadata.tags||[]}withSettings(e){const t={...this.settings,...e};return new PluginRegistration(this.plugin,t,this.registeredAt)}isCompatibleWith(e){return this.version===e}}class DependencyResolver{static resolve(e){return this.validateDependencies(e),this.topologicalSort(e)}static validateDependencies(e){const t=new Set(e.map((e=>e.name)));for(const r of e)this.validatePluginDependencies(r,t,e)}static validatePluginDependencies(e,t,r){const n=e.dependencies||[];for(const i of n){if(!t.has(i))throw new Error(`Plugin '${e.name}' depends on '${i}' which is not registered`);const n=r.find((e=>e.name===i));if(n&&!n.isEnabled)throw new Error(`Plugin '${e.name}' depends on '${i}' which is disabled`)}}static topologicalSort(e){const t=[],r=new Set,n=new Set,i=new Map(e.map((e=>[e.name,e]))),visit=e=>{if(n.has(e.name))throw new Error(`Circular dependency detected involving plugin '${e.name}'`);if(r.has(e.name))return;n.add(e.name);const s=e.dependencies||[];for(const e of s){const t=i.get(e);t?.isEnabled&&visit(t)}n.delete(e.name),r.add(e.name),t.push(e)},s=this.sortByPriority(e.filter((e=>e.isEnabled)));for(const e of s)visit(e);return t}static sortByPriority(e){return e.sort(((e,t)=>t.priority-e.priority))}static validateCircularDependencies(e){const t=new Map;for(const r of e)t.set(r.name,r.dependencies?.slice()||[]);const r=new Set,n=new Set,hasCycle=e=>{if(n.has(e))return!0;if(r.has(e))return!1;r.add(e),n.add(e);const i=t.get(e)||[];for(const e of i)if(hasCycle(e))return!0;return n.delete(e),!1};for(const[e]of t)if(hasCycle(e))throw new Error(`Circular dependency detected starting from plugin '${e}'`)}}class PluginManager{constructor(){this.registrations=new Map,this.built=!1}register(e,t={}){this.ensureNotBuilt("register plugins"),this.validatePluginUniqueness(e.metadata.name);const r=this.createPluginSettings(t),n=new PluginRegistration(e,r);return this.registrations.set(e.metadata.name,n),this}unregister(e){return this.ensureNotBuilt("unregister plugins"),this.registrations.delete(e),this}configure(e,t){this.ensureNotBuilt("configure plugins");const r=this.getRegistration(e).withSettings(t);return this.registrations.set(e,r),this}getPlugin(e){return this.registrations.get(e)}getPluginNames(){return Array.from(this.registrations.keys())}hasPlugin(e){return this.registrations.has(e)}getEnabledPlugins(){return Array.from(this.registrations.values()).filter((e=>e.isEnabled))}getPluginsByTag(e){return Array.from(this.registrations.values()).filter((t=>t.tags.includes(e)))}build(){this.ensureNotAlreadyBuilt();const e=DependencyResolver.resolve(Array.from(this.registrations.values()));return this.pipeline=this.createPipeline(e),this.built=!0,this}async execute(e,t,r){this.ensureBuilt();const n=this.createExecutionContext(t),i=Date.now(),s=await this.pipeline.execute(e,n,r),a=Date.now()-i;return this.createManagerResult(s,a)}validatePluginUniqueness(e){if(this.registrations.has(e))throw new Error(`Plugin '${e}' is already registered`)}createPluginSettings(e){return{enabled:!0,priority:0,config:{},...e}}getRegistration(e){const t=this.registrations.get(e);if(!t)throw new Error(`Plugin '${e}' not found`);return t}createPipeline(e){const t=new PipelineBuilder;if(!t)throw new Error("Failed to create pipeline builder");for(const r of e){const e=new PluginProcessor(r.plugin,r.dependencies);t.withProcessor(e)}const r=t.build();if(!r)throw new Error("Failed to build pipeline");return r}createExecutionContext(e){const t=new Map(Array.from(this.registrations.values()).map((e=>[e.name,e.settings])));return ContextFactory.create(e,t)}createManagerResult(e,t){const r=e.stages.map((e=>{const t=e.processorName.replace("plugin-","");return"ok"===e.kind?{pluginName:t,success:!0,output:e.output,duration:e.elapsed,warnings:e.warnings.map((e=>e.message))}:{pluginName:t,success:!1,error:e.error,duration:e.elapsed,warnings:e.warnings.map((e=>e.message))}}));return{success:e.success,results:r,duration:t,errors:e.errors,warnings:e.warnings.map((e=>e.message))}}ensureNotBuilt(e){if(this.built)throw new Error(`Cannot ${e} after manager is built`)}ensureNotAlreadyBuilt(){if(this.built)throw new Error("Plugin manager is already built")}ensureBuilt(){if(!this.built||!this.pipeline)throw new Error("Plugin manager must be built before execution")}}class PluginManagerBuilder{constructor(){this.manager=new PluginManager}plugin(e,t){return this.manager.register(e,t),this}plugins(e){return e.forEach((({plugin:e,settings:t})=>{this.manager.register(e,t)})),this}when(e,t,r){return e&&this.manager.register(t,r),this}group(e){return e.items.forEach((({plugin:t,settings:r={}})=>{const n={...r,config:{...r.config,tags:[...r.config?.tags||[],e.tag]}};this.manager.register(t,n)})),this}withPriority(e,t,r){const n={...r,priority:e};return this.manager.register(t,n),this}disabled(e,t){const r={...t,enabled:!1};return this.manager.register(e,r),this}build(){return this.manager.build()}validate(){if(0===this.manager.getPluginNames().length)throw new Error("No plugins registered in the manager");return this}clone(){const e=new PluginManagerBuilder;for(const t of this.manager.getPluginNames()){const r=this.manager.getPlugin(t);r&&e.manager.register(r.plugin,r.settings)}return e}}class ManagerFactory{static create(){return new PluginManager}static builder(){return new PluginManagerBuilder}static typed(e,t){return{create:()=>new PluginManager,builder:()=>new PluginManagerBuilder}}}class ValidationUtils{static validatePluginMetadata(e){if(this.validateRequiredStringField(e.name,"Plugin name"),this.validateRequiredStringField(e.version,"Plugin version"),!this.isValidSemVer(e.version))throw new Error(`Plugin version '${e.version}' is not a valid semantic version`);e.dependencies&&this.validateDependencies(e.dependencies),e.tags&&this.validateTags(e.tags)}static validatePluginSettings(e){if("boolean"!=typeof e.enabled)throw new Error("Plugin enabled setting must be a boolean");if("number"!=typeof e.priority)throw new Error("Plugin priority must be a number");if(e.config&&"object"!=typeof e.config)throw new Error("Plugin config must be an object")}static validatePluginName(e){if(!e||"string"!=typeof e)throw new Error("Plugin name must be a non-empty string");if(!/^[a-zA-Z0-9-_]+$/.test(e))throw new Error("Plugin name can only contain alphanumeric characters, hyphens, and underscores")}static validateRequiredStringField(e,t){if(!e||"string"!=typeof e)throw new Error(`${t} is required and must be a string`)}static isValidSemVer(e){return/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*))?(?:\+([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*))?$/.test(e)}static validateDependencies(e){if(!Array.isArray(e))throw new Error("Plugin dependencies must be an array");for(const t of e)this.validatePluginName(t)}static validateTags(e){if(!Array.isArray(e))throw new Error("Plugin tags must be an array");for(const t of e)if(!t||"string"!=typeof t)throw new Error("Plugin tags must be non-empty strings")}}class PluginError extends Error{constructor(e,t,r){super(e),this.pluginName=t,this.originalError=r,this.name="PluginError"}}class PluginRegistrationError extends Error{constructor(e,t){super(e),this.pluginName=t,this.name="PluginRegistrationError"}}class PluginDependencyError extends Error{constructor(e,t,r){super(e),this.pluginName=t,this.missingDependency=r,this.name="PluginDependencyError"}}class PluginValidationError extends Error{constructor(e,t,r){super(e),this.pluginName=t,this.field=r,this.name="PluginValidationError"}}class ErrorUtils{static wrapPluginError(e,t){return e instanceof PluginError?e:new PluginError(`Plugin '${t}' failed: ${e.message}`,t,e)}static isPluginError(e){return e instanceof PluginError}static extractPluginName(e){if(this.isPluginError(e))return e.pluginName}static createRegistrationError(e,t){return new PluginRegistrationError(`Failed to register plugin '${e}': ${t}`,e)}static createDependencyError(e,t){return new PluginDependencyError(`Plugin '${e}' has unmet dependency: '${t}'`,e,t)}static createValidationError(e,t,r){return new PluginValidationError(`Plugin '${e}' validation failed for field '${t}': ${r}`,e,t)}}function createPluginManager(){return ManagerFactory.create()}function createPluginBuilder(){return ManagerFactory.builder()}function simplePlugin(e,t,r){return PluginFactory.create(e,t,r)}export{BasePlugin,ContextFactory,DependencyResolver,ErrorUtils,ManagerFactory,BasePlugin as Plugin,PluginContextImpl,PluginDependencyError,PluginError,PluginFactory,PluginManager,PluginManagerBuilder,PluginProcessor,PluginRegistration,PluginRegistrationError,PluginValidationError,ValidationUtils,createPluginBuilder,createPluginManager,simplePlugin};