every-plugin
Version:
165 lines (163 loc) • 6.41 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
const require_errors = require('./errors.cjs');
const require_plugin_service = require('./services/plugin.service.cjs');
let effect = require("effect");
let _orpc_server = require("@orpc/server");
//#region src/runtime/index.ts
var PluginRuntime = class {
__registryType;
pluginCache = /* @__PURE__ */ new Map();
constructor(runtime, registry) {
this.runtime = runtime;
this.registry = registry;
}
generateCacheKey(pluginId, config) {
return `${pluginId}:${effect.Hash.structure(config).toString()}`;
}
validatePluginId(pluginId) {
if (!(pluginId in this.registry)) return effect.Effect.fail(new require_errors.PluginRuntimeError({
pluginId: String(pluginId),
operation: "validate-plugin-id",
cause: /* @__PURE__ */ new Error(`Plugin ID '${String(pluginId)}' not found in registry.`),
retryable: false
}));
return effect.Effect.succeed(String(pluginId));
}
async runPromise(effect$1) {
const exit = await this.runtime.runPromiseExit(effect$1);
if (effect.Exit.isFailure(exit)) {
const error = effect.Cause.failureOption(exit.cause);
if (effect.Option.isSome(error)) throw error.value;
throw effect.Cause.squash(exit.cause);
}
return exit.value;
}
async usePlugin(pluginId, config, plugins) {
const cacheKey = this.generateCacheKey(pluginId, {
...config,
__plugins: plugins ?? {}
});
let cachedPlugin = this.pluginCache.get(cacheKey);
if (!cachedPlugin) {
const operation = effect.Effect.gen(this, function* () {
const pluginService = yield* require_plugin_service.PluginService;
const validatedId = yield* this.validatePluginId(pluginId);
const ctor = yield* pluginService.loadPlugin(validatedId);
const instance = yield* pluginService.instantiatePlugin(pluginId, ctor);
return yield* pluginService.initializePlugin(instance, config, plugins);
}).pipe(effect.Effect.provide(this.runtime));
cachedPlugin = effect.Effect.cached(operation).pipe(effect.Effect.flatten);
this.pluginCache.set(cacheKey, cachedPlugin);
}
const initialized = await this.runPromise(cachedPlugin);
const createClient = (context) => {
return (0, _orpc_server.createRouterClient)(initialized.plugin.createRouter(initialized.context), { context: context ?? {} });
};
return {
createClient,
router: initialized.plugin.createRouter(initialized.context),
metadata: initialized.metadata,
initialized
};
}
async loadPlugin(pluginId) {
const effect$2 = effect.Effect.gen(function* () {
return yield* (yield* require_plugin_service.PluginService).loadPlugin(pluginId);
});
return this.runPromise(effect$2);
}
async instantiatePlugin(pluginId, loadedPlugin) {
const effect$3 = effect.Effect.gen(function* () {
return yield* (yield* require_plugin_service.PluginService).instantiatePlugin(pluginId, loadedPlugin);
});
return this.runPromise(effect$3);
}
async initializePlugin(instance, config, plugins) {
const effect$4 = effect.Effect.gen(function* () {
return yield* (yield* require_plugin_service.PluginService).initializePlugin(instance, config, plugins);
});
return this.runPromise(effect$4);
}
async shutdown() {
const effect$5 = effect.Effect.gen(function* () {
yield* (yield* require_plugin_service.PluginService).cleanup();
});
await this.runPromise(effect$5);
await this.runtime.dispose();
}
async evictPlugin(pluginId, config) {
const cacheKey = this.generateCacheKey(pluginId, config);
const effect$6 = effect.Effect.gen(this, function* () {
const pluginService = yield* require_plugin_service.PluginService;
const cachedPlugin = this.pluginCache.get(cacheKey);
if (cachedPlugin) {
this.pluginCache.delete(cacheKey);
const pluginResult = yield* cachedPlugin.pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
if (pluginResult) yield* pluginService.shutdownPlugin(pluginResult).pipe(effect.Effect.catchAll((error) => effect.Effect.logWarning(`Failed to shutdown evicted plugin ${pluginId}`, error)));
}
}).pipe(effect.Effect.catchAll((error) => effect.Effect.logWarning(`Plugin eviction failed for ${pluginId}`, error)));
return this.runPromise(effect$6);
}
};
/**
* Normalizes a remote URL to ensure it points to remoteEntry.js
* If the URL doesn't end with a file extension, appends /remoteEntry.js
*/
function normalizeRemoteUrl(url) {
if (!url) return url;
if (url.endsWith(".js") || url.endsWith(".json")) return url;
return `${url.endsWith("/") ? url.slice(0, -1) : url}/remoteEntry.js`;
}
/**
* Extract plugin map (module constructors) from registry entries
*/
function extractPluginMap(registry) {
const pluginMap = {};
for (const [pluginId, entry] of Object.entries(registry)) if ("module" in entry && entry.module) pluginMap[pluginId] = entry.module;
return pluginMap;
}
/**
* Normalize registry entries - ensure remote URLs are properly formatted
*/
function normalizeRegistry(registry) {
const normalized = {};
for (const [pluginId, entry] of Object.entries(registry)) if ("module" in entry) normalized[pluginId] = {
...entry,
remote: entry.remote ? normalizeRemoteUrl(entry.remote) : void 0
};
else normalized[pluginId] = {
...entry,
remote: normalizeRemoteUrl(entry.remote)
};
return normalized;
}
/**
* Creates a plugin runtime with support for both module and remote plugin entries.
*
* @example
* ```typescript
* // With module entries (types inferred automatically)
* const runtime = createPluginRuntime({
* registry: {
* telegram: { module: TelegramPlugin },
* gopher: { remote: "https://cdn.example.com/gopher/remoteEntry.js" }
* },
* secrets: { API_KEY: "..." }
* });
*
* // Types are automatically inferred from module entries!
* const { router } = await runtime.usePlugin("telegram", config);
* ```
*/
function createPluginRuntime(config) {
const secrets = config.secrets || {};
const normalizedRegistry = normalizeRegistry(config.registry);
const pluginMap = extractPluginMap(config.registry);
const layer = require_plugin_service.PluginService.Live(normalizedRegistry, secrets, pluginMap);
return new PluginRuntime(effect.ManagedRuntime.make(layer), normalizedRegistry);
}
//#endregion
exports.PluginRuntime = PluginRuntime;
exports.createPluginRuntime = createPluginRuntime;
//# sourceMappingURL=index.cjs.map