refactor: share facade resolution helpers

This commit is contained in:
Peter Steinberger
2026-04-20 15:29:05 +01:00
parent f163432674
commit eddfffebe8
4 changed files with 163 additions and 172 deletions

View File

@@ -16,10 +16,7 @@ import {
loadPluginManifestRegistry,
type PluginManifestRecord,
} from "../plugins/manifest-registry.js";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
} from "../plugins/public-surface-runtime.js";
import { resolveRegistryPluginModuleLocationFromRecords } from "./facade-resolution-shared.js";
const ALWAYS_ALLOWED_RUNTIME_DIR_NAMES = new Set([
"image-generation-core",
@@ -171,30 +168,11 @@ export function resolveRegistryPluginModuleLocation(params: {
cacheKey: params.resolutionKey,
...(params.env ? { env: params.env } : {}),
});
type RegistryRecord = (typeof registry)[number];
const tiers: Array<(plugin: RegistryRecord) => boolean> = [
(plugin) => plugin.id === params.dirName,
(plugin) => path.basename(plugin.rootDir) === params.dirName,
(plugin) => plugin.channels.includes(params.dirName),
];
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const matchFn of tiers) {
for (const record of registry.filter(matchFn)) {
const rootDir = path.resolve(record.rootDir);
const builtCandidate = path.join(rootDir, artifactBasename);
if (fs.existsSync(builtCandidate)) {
return { modulePath: builtCandidate, boundaryRoot: rootDir };
}
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.join(rootDir, `${sourceBaseName}${ext}`);
if (fs.existsSync(sourceCandidate)) {
return { modulePath: sourceCandidate, boundaryRoot: rootDir };
}
}
}
}
return null;
return resolveRegistryPluginModuleLocationFromRecords({
registry,
dirName: params.dirName,
artifactBasename: params.artifactBasename,
});
}
function readBundledPluginManifestRecordFromDir(params: {

View File

@@ -9,11 +9,12 @@ import {
type PluginJitiLoaderCache,
type PluginJitiLoaderFactory,
} from "../plugins/jiti-loader-cache.js";
import {
resolveBundledPluginSourcePublicSurfacePath,
resolveBundledPluginPublicSurfacePath,
} from "../plugins/public-surface-runtime.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
import {
createFacadeResolutionKey as createFacadeResolutionKeyShared,
resolveBundledFacadeModuleLocation,
resolveCachedFacadeModuleLocation,
} from "./facade-resolution-shared.js";
const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url);
@@ -58,7 +59,7 @@ function createFacadeResolutionKey(params: {
env?: NodeJS.ProcessEnv;
}): string {
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
return createFacadeResolutionKeyShared({ ...params, bundledPluginsDir });
}
function resolveFacadeModuleLocationUncached(params: {
@@ -67,48 +68,12 @@ function resolveFacadeModuleLocationUncached(params: {
env?: NodeJS.ProcessEnv;
}): { modulePath: string; boundaryRoot: string } | null {
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
if (preferSource) {
const modulePath =
resolveBundledPluginSourcePublicSurfacePath({
...params,
sourceRoot: bundledPluginsDir ?? path.resolve(getOpenClawPackageRoot(), "extensions"),
}) ??
resolveBundledPluginPublicSurfacePath({
rootDir: getOpenClawPackageRoot(),
env: params.env,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
dirName: params.dirName,
artifactBasename: params.artifactBasename,
});
if (modulePath) {
return {
modulePath,
boundaryRoot:
bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep)
? path.resolve(bundledPluginsDir)
: getOpenClawPackageRoot(),
};
}
return null;
}
const modulePath = resolveBundledPluginPublicSurfacePath({
rootDir: getOpenClawPackageRoot(),
env: params.env,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
dirName: params.dirName,
artifactBasename: params.artifactBasename,
return resolveBundledFacadeModuleLocation({
...params,
currentModulePath: CURRENT_MODULE_PATH,
packageRoot: getOpenClawPackageRoot(),
bundledPluginsDir,
});
if (!modulePath) {
return null;
}
return {
modulePath,
boundaryRoot:
bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep)
? path.resolve(bundledPluginsDir)
: getOpenClawPackageRoot(),
};
}
function resolveFacadeModuleLocation(params: {
@@ -116,13 +81,11 @@ function resolveFacadeModuleLocation(params: {
artifactBasename: string;
env?: NodeJS.ProcessEnv;
}): { modulePath: string; boundaryRoot: string } | null {
const key = createFacadeResolutionKey(params);
if (cachedFacadeModuleLocationsByKey.has(key)) {
return cachedFacadeModuleLocationsByKey.get(key) ?? null;
}
const resolved = resolveFacadeModuleLocationUncached(params);
cachedFacadeModuleLocationsByKey.set(key, resolved);
return resolved;
return resolveCachedFacadeModuleLocation({
cache: cachedFacadeModuleLocationsByKey,
key: createFacadeResolutionKey(params),
resolve: () => resolveFacadeModuleLocationUncached(params),
});
}
function getJiti(modulePath: string) {

View File

@@ -0,0 +1,121 @@
import fs from "node:fs";
import path from "node:path";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
resolveBundledPluginPublicSurfacePath,
resolveBundledPluginSourcePublicSurfacePath,
} from "../plugins/public-surface-runtime.js";
export type FacadeModuleLocationLike = {
modulePath: string;
boundaryRoot: string;
};
type FacadeRegistryRecordLike = {
id: string;
rootDir: string;
channels: readonly string[];
};
export function createFacadeResolutionKey(params: {
dirName: string;
artifactBasename: string;
bundledPluginsDir?: string | null;
}): string {
return `${params.dirName}::${params.artifactBasename}::${
params.bundledPluginsDir ? path.resolve(params.bundledPluginsDir) : "<default>"
}`;
}
export function resolveCachedFacadeModuleLocation<TLocation>(params: {
cache: Map<string, TLocation | null>;
key: string;
resolve: () => TLocation | null;
}): TLocation | null {
if (params.cache.has(params.key)) {
return params.cache.get(params.key) ?? null;
}
const resolved = params.resolve();
params.cache.set(params.key, resolved);
return resolved;
}
export function resolveFacadeBoundaryRoot(params: {
modulePath: string;
bundledPluginsDir?: string | null;
packageRoot: string;
}): string {
if (!params.bundledPluginsDir) {
return params.packageRoot;
}
const resolvedBundledPluginsDir = path.resolve(params.bundledPluginsDir);
return params.modulePath.startsWith(`${resolvedBundledPluginsDir}${path.sep}`)
? resolvedBundledPluginsDir
: params.packageRoot;
}
export function resolveBundledFacadeModuleLocation(params: {
currentModulePath: string;
packageRoot: string;
dirName: string;
artifactBasename: string;
env?: NodeJS.ProcessEnv;
bundledPluginsDir?: string | null;
}): FacadeModuleLocationLike | null {
const preferSource = !params.currentModulePath.includes(`${path.sep}dist${path.sep}`);
const publicSurfaceParams = {
rootDir: params.packageRoot,
env: params.env,
...(params.bundledPluginsDir ? { bundledPluginsDir: params.bundledPluginsDir } : {}),
dirName: params.dirName,
artifactBasename: params.artifactBasename,
};
const modulePath = preferSource
? (resolveBundledPluginSourcePublicSurfacePath({
dirName: params.dirName,
artifactBasename: params.artifactBasename,
sourceRoot: params.bundledPluginsDir ?? path.resolve(params.packageRoot, "extensions"),
}) ?? resolveBundledPluginPublicSurfacePath(publicSurfaceParams))
: resolveBundledPluginPublicSurfacePath(publicSurfaceParams);
return modulePath
? {
modulePath,
boundaryRoot: resolveFacadeBoundaryRoot({
modulePath,
bundledPluginsDir: params.bundledPluginsDir,
packageRoot: params.packageRoot,
}),
}
: null;
}
export function resolveRegistryPluginModuleLocationFromRecords(params: {
registry: readonly FacadeRegistryRecordLike[];
dirName: string;
artifactBasename: string;
}): FacadeModuleLocationLike | null {
const tiers: Array<(plugin: FacadeRegistryRecordLike) => boolean> = [
(plugin) => plugin.id === params.dirName,
(plugin) => path.basename(plugin.rootDir) === params.dirName,
(plugin) => plugin.channels.includes(params.dirName),
];
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const matchFn of tiers) {
for (const record of params.registry.filter(matchFn)) {
const rootDir = path.resolve(record.rootDir);
const builtCandidate = path.join(rootDir, artifactBasename);
if (fs.existsSync(builtCandidate)) {
return { modulePath: builtCandidate, boundaryRoot: rootDir };
}
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.join(rootDir, `${sourceBaseName}${ext}`);
if (fs.existsSync(sourceCandidate)) {
return { modulePath: sourceCandidate, boundaryRoot: rootDir };
}
}
}
}
return null;
}

View File

@@ -1,4 +1,3 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -7,13 +6,6 @@ import {
getCachedPluginJitiLoader,
type PluginJitiLoaderCache,
} from "../plugins/jiti-loader-cache.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
resolveBundledPluginSourcePublicSurfacePath,
resolveBundledPluginPublicSurfacePath,
} from "../plugins/public-surface-runtime.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
import {
loadBundledPluginPublicSurfaceModuleSync as loadBundledPluginPublicSurfaceModuleSyncLight,
@@ -21,6 +13,12 @@ import {
resetFacadeLoaderStateForTest,
type FacadeModuleLocation,
} from "./facade-loader.js";
import {
createFacadeResolutionKey as createFacadeResolutionKeyShared,
resolveBundledFacadeModuleLocation,
resolveCachedFacadeModuleLocation,
resolveRegistryPluginModuleLocationFromRecords,
} from "./facade-resolution-shared.js";
export {
createLazyFacadeArrayValue,
createLazyFacadeObjectValue,
@@ -61,38 +59,7 @@ function createFacadeResolutionKey(params: {
env?: NodeJS.ProcessEnv;
}): string {
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
}
function resolveRegistryPluginModuleLocationFromRegistry(params: {
registry: readonly Pick<PluginManifestRecord, "id" | "rootDir" | "channels">[];
dirName: string;
artifactBasename: string;
}): { modulePath: string; boundaryRoot: string } | null {
type RegistryRecord = (typeof params.registry)[number];
const tiers: Array<(plugin: RegistryRecord) => boolean> = [
(plugin) => plugin.id === params.dirName,
(plugin) => path.basename(plugin.rootDir) === params.dirName,
(plugin) => plugin.channels.includes(params.dirName),
];
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
const sourceBaseName = artifactBasename.replace(/\.js$/u, "");
for (const matchFn of tiers) {
for (const record of params.registry.filter(matchFn)) {
const rootDir = path.resolve(record.rootDir);
const builtCandidate = path.join(rootDir, artifactBasename);
if (fs.existsSync(builtCandidate)) {
return { modulePath: builtCandidate, boundaryRoot: rootDir };
}
for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) {
const sourceCandidate = path.join(rootDir, `${sourceBaseName}${ext}`);
if (fs.existsSync(sourceCandidate)) {
return { modulePath: sourceCandidate, boundaryRoot: rootDir };
}
}
}
}
return null;
return createFacadeResolutionKeyShared({ ...params, bundledPluginsDir });
}
function resolveRegistryPluginModuleLocation(params: {
@@ -106,56 +73,20 @@ function resolveRegistryPluginModuleLocation(params: {
});
}
function resolveFacadeBoundaryRoot(modulePath: string, bundledPluginsDir: string | undefined) {
if (!bundledPluginsDir) {
return OPENCLAW_PACKAGE_ROOT;
}
const resolvedBundledPluginsDir = path.resolve(bundledPluginsDir);
return modulePath.startsWith(`${resolvedBundledPluginsDir}${path.sep}`)
? resolvedBundledPluginsDir
: OPENCLAW_PACKAGE_ROOT;
}
function resolveFacadeModuleLocationUncached(params: {
dirName: string;
artifactBasename: string;
env?: NodeJS.ProcessEnv;
}): { modulePath: string; boundaryRoot: string } | null {
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
if (preferSource) {
const modulePath =
resolveBundledPluginSourcePublicSurfacePath({
...params,
sourceRoot: bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"),
}) ??
resolveBundledPluginPublicSurfacePath({
rootDir: OPENCLAW_PACKAGE_ROOT,
env: params.env,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
dirName: params.dirName,
artifactBasename: params.artifactBasename,
});
if (modulePath) {
return {
modulePath,
boundaryRoot: resolveFacadeBoundaryRoot(modulePath, bundledPluginsDir),
};
}
return resolveRegistryPluginModuleLocation(params);
}
const modulePath = resolveBundledPluginPublicSurfacePath({
rootDir: OPENCLAW_PACKAGE_ROOT,
env: params.env,
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
dirName: params.dirName,
artifactBasename: params.artifactBasename,
const bundledLocation = resolveBundledFacadeModuleLocation({
...params,
currentModulePath: CURRENT_MODULE_PATH,
packageRoot: OPENCLAW_PACKAGE_ROOT,
bundledPluginsDir,
});
if (modulePath) {
return {
modulePath,
boundaryRoot: resolveFacadeBoundaryRoot(modulePath, bundledPluginsDir),
};
if (bundledLocation) {
return bundledLocation;
}
return resolveRegistryPluginModuleLocation(params);
}
@@ -165,13 +96,11 @@ function resolveFacadeModuleLocation(params: {
artifactBasename: string;
env?: NodeJS.ProcessEnv;
}): { modulePath: string; boundaryRoot: string } | null {
const key = createFacadeResolutionKey(params);
if (cachedFacadeModuleLocationsByKey.has(key)) {
return cachedFacadeModuleLocationsByKey.get(key) ?? null;
}
const resolved = resolveFacadeModuleLocationUncached(params);
cachedFacadeModuleLocationsByKey.set(key, resolved);
return resolved;
return resolveCachedFacadeModuleLocation({
cache: cachedFacadeModuleLocationsByKey,
key: createFacadeResolutionKey(params),
resolve: () => resolveFacadeModuleLocationUncached(params),
});
}
type BundledPluginPublicSurfaceParams = {
@@ -324,7 +253,7 @@ export function resetFacadeRuntimeStateForTest(): void {
export const __testing = {
loadFacadeModuleAtLocationSync,
resolveRegistryPluginModuleLocationFromRegistry,
resolveRegistryPluginModuleLocationFromRegistry: resolveRegistryPluginModuleLocationFromRecords,
resolveFacadeModuleLocation,
evaluateBundledPluginPublicSurfaceAccess: ((
...args: Parameters<