@remedia/plugin-host reference
Cleanup and cancellation, the plugin-host HTTP protocol, limits, and error codes.
Use createPluginHost() for direct dispatch, lifecycle management, and HTTP handling. The host tutorial shows registration. HTTP adapters show deployment. The HTTP protocol defines remote JSON and media responses.
Host API
| Member | Contract |
|---|---|
createPluginHost<Schema>(options) | Create an in-process host. name is required. No listener starts. |
register<Schema>(definition) | Register a unique plugin ID and its operation handlers. Handlers are copied at registration. |
handle(request, options?) | Typed direct dispatch. request contains pluginId, operation, input, and optional signal. |
execute(pluginId, operation, input, options?) | Positional direct dispatch with unknown input/output types. |
manifest() | Read discovery metadata for current registrations. |
fetch(request) | Handle a Fetch API request using the HTTP protocol. |
unregister(id) | Remove dispatch access, cancel owned work, await cleanup. Returns false for an unknown ID. |
close() | Stop accepting work, cancel operations and HTTP media, and await owned cleanup. |
A successful direct result preserves its object identity. Only undefined means unhandled. null, false, functions, class instances, and streams can be returned directly. HTTP results must satisfy the JSON serialization rules.
Plugin IDs and operation names contain 1–128 ASCII letters, digits, ., _, or -, beginning with a letter or digit. Duplicate plugin IDs throw PluginHostError with duplicate_plugin. Registration requires at least one own operation and does not initialize the plugin for you.
Cleanup and cancellation
import { createPluginHost } from '@remedia/plugin-host';
const host = createPluginHost({ name: 'Cancellation' });
host.register({
id: 'example.work',
operations: {
wait: async (_input, { signal }) => {
const response = await fetch('https://provider.example.com/status', { signal });
return response.text();
},
},
dispose: async () => {
/* Close plugin-owned pools or workers here. */
},
});
const controller = new AbortController();
const pending = host.handle({
pluginId: 'example.work',
operation: 'wait',
input: null,
signal: controller.signal,
});
controller.abort(new Error('Client cancelled'));
try {
await pending;
} catch (error) {
console.error(error);
}
await host.unregister('example.work');
await host.close();Cancellation ownership
Request and optional handle(request, { signal }) cancellation are combined with host/registration cancellation. Direct cancellation rejects with the original signal reason. Already aborted requests do not invoke handlers. Unexpected direct handler errors propagate unchanged. unregister(id) immediately removes discovery/dispatch access, aborts operations and HTTP media, waits for operations and media opening to settle, releases resources, then invokes plugin disposal exactly once. Concurrent unregister calls share completion. IDs cannot be reused during disposal. Returns false for unknown IDs. Cleanup failures reject as AggregateError while other registrations still clean up.
Shutdown and cleanup failures
close() synchronously prevents new operations, cancels requests and returns one shared shutdown promise. Always await it. Handlers and factories must settle after cancellation: JavaScript cannot forcibly terminate an uncooperative promise, so shutdown waits for it. A handler owns resources it creates before throwing. Use try/finally for partial work. Successful direct results belong to the caller and are not revoked by host shutdown.
Unexpected HTTP and background cleanup failures go to onError, defaulting to console.error. Keep this callback non-throwing. HTTP errors redact original messages. Stream read failures reject the response body and are reported. After headers have been sent, middleware terminates the connection.