pdf-tei-editor / app /src /modules /plugin-context.js
cmboulanger's picture
disaster-recovery deploy of pdf-tei-editor
6a49f21 verified
Raw
History Blame Contribute Delete
4.99 kB
/**
* PluginContext - Facade providing clean interface between plugins and application
* @import { ApplicationState } from '../state.js'
* @import { Application } from '../modules/application.js'
* @import { InvokeOptions, InvocationResult } from '../modules/plugin-manager.js'
*/
/**
* PluginContext provides a controlled interface for plugins to interact with application services
* without creating tight coupling to the full Application class
*/
export class PluginContext {
constructor(application) {
this.#application = application;
}
/** @type {Application} */
#application;
//
// State management methods
//
/**
* Update application state
* @param {Partial<ApplicationState>} changes - Changes to apply
* @returns {Promise<ApplicationState>} New state after changes applied
*/
async updateState(changes, secondArg=undefined) {
if (secondArg !== undefined) {
throw new Error('PluginContext.updateState() now takes only one parameter (changes). Remove the currentState parameter.');
}
return await this.#application.updateState(changes);
}
/**
* Schedule a state change to run after the current propagation cycle completes.
*
* Use this only from async work triggered by onStateUpdate (e.g. after an API call
* whose result affects state). Synchronous onStateUpdate handlers must remain pure
* observers and use dispatchStateChange() via their Plugin base class.
*
* @param {Partial<ApplicationState>} changes
* @returns {Promise<ApplicationState>}
*/
scheduleStateChange(changes) {
return this.#application.scheduleStateChange(changes);
}
/**
* Update extension properties in state
* @param {Object} extChanges - Extension properties to update
* @returns {Promise<ApplicationState>} New state after changes applied
*/
async updateStateExt(extChanges, secondArg) {
if (secondArg !== undefined) {
throw new Error('PluginContext.updateStateExt() now takes only one parameter (extChanges). Remove the currentState parameter.');
}
return await this.#application.updateStateExt(extChanges);
}
/**
* Check if specific state properties have changed
* @param {ApplicationState} state - Current state
* @param {...string} keys - Keys to check for changes
* @returns {boolean} True if any keys have changed
*/
hasStateChanged(state, ...keys) {
return this.#application.getStateManager().hasStateChanged(state, ...keys);
}
/**
* Get all property names that have changed from previous state
* @param {ApplicationState} state - Current state
* @returns {Array<keyof ApplicationState>} Array of changed property names
*/
getChangedStateKeys(state) {
return this.#application.getStateManager().getChangedStateKeys(state);
}
/**
* Get previous value of a state property
* @param {ApplicationState} state - Current state
* @param {string} propertyName - Property name to get previous value for
* @returns {*} Previous value or undefined
*/
getPreviousStateValue(state, propertyName) {
return this.#application.getStateManager().getPreviousStateValue(state, propertyName);
}
/**
* Get the previous state object
* @param {ApplicationState} state - Current state
* @returns {ApplicationState|undefined} Previous state or undefined
*/
getPreviousState(state) {
return this.#application.getStateManager().getPreviousState(state);
}
//
// Plugin invocation methods (for inter-plugin communication)
//
/**
* Invoke an endpoint on all plugins that implement it, in dependency order.
* By default, throws on the first error encountered, and returns the value of the
* first fulfilled promise (or synchronous function). For other options, see {InvokeOptions}
*
* @param {string} endpoint - Endpoint to invoke
* @param {*|Array} [args] - Arguments to pass to endpoint functions. If array, spread as parameters; if not array, pass as single parameter
* @param {InvokeOptions} [options] - Optional configuration for this invocation
* @returns {Promise<InvocationResult[] | any[] | any>} Result formatted depending on options.result
*/
async invokePluginEndpoint(endpoint, args = [], options = {throws:true, result:'full'}) {
return await this.#application.invokePluginEndpoint(endpoint, args, options);
}
//
// Dependency injection
//
/**
* Get the public API of a registered plugin by name.
* Mirrors the backend `context.get_dependency(id)` pattern.
* @param {string} name - Plugin name
* @returns {any} The plugin's public API
*/
getDependency(name) {
return this.#application.getDependency(name);
}
//
// Utility methods plugins might need
//
/**
* Get current application state (read-only access)
* @returns {ApplicationState|null} Current application state
*/
getCurrentState() {
return this.#application.getCurrentState();
}
}
export default PluginContext;