Files
openclaw/src/test-utils/bundled-plugin-public-surface.ts
openperf 9a03fe8181 fix(facade-runtime): add recursion guard to facade module loader to prevent infinite stack overflow
Place a sentinel object in the loadedFacadeModules cache before the Jiti
sync load begins.  Re-entrant calls (caused by circular facade references
from constant exports evaluated at module-evaluation time) now receive the
sentinel instead of recursing infinitely.  Once the real module finishes
loading, Object.assign() back-fills the sentinel so any references
captured during the circular load phase see the final exports.

The Jiti load is wrapped in try/catch: on failure the sentinel is removed
from the cache so that subsequent retry attempts re-execute the load
instead of silently returning an empty object.  The function returns the
sentinel (not the raw loaded module) to guarantee a single object identity
for all callers, including those that captured a reference during the
circular load phase.

Also tightens the generic constraint from <T> to <T extends object> so
Object.assign() is type-safe, and propagates the constraint to the
test-utils callers in bundled-plugin-public-surface.ts.

Fixes #57394
2026-03-30 13:48:21 +05:30

60 lines
1.9 KiB
TypeScript

import path from "node:path";
import { fileURLToPath } from "node:url";
import { loadBundledPluginPublicSurfaceModuleSync } from "../plugin-sdk/facade-runtime.js";
import {
findBundledPluginMetadataById,
type BundledPluginMetadata,
} from "../plugins/bundled-plugin-metadata.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
const OPENCLAW_PACKAGE_ROOT =
resolveLoaderPackageRoot({
modulePath: fileURLToPath(import.meta.url),
moduleUrl: import.meta.url,
}) ?? fileURLToPath(new URL("../..", import.meta.url));
function findBundledPluginMetadata(pluginId: string): BundledPluginMetadata {
const metadata = findBundledPluginMetadataById(pluginId);
if (!metadata) {
throw new Error(`Unknown bundled plugin id: ${pluginId}`);
}
return metadata;
}
export function loadBundledPluginPublicSurfaceSync<T extends object>(params: {
pluginId: string;
artifactBasename: string;
}): T {
const metadata = findBundledPluginMetadata(params.pluginId);
return loadBundledPluginPublicSurfaceModuleSync<T>({
dirName: metadata.dirName,
artifactBasename: params.artifactBasename,
});
}
export function loadBundledPluginTestApiSync<T extends object>(pluginId: string): T {
return loadBundledPluginPublicSurfaceSync<T>({
pluginId,
artifactBasename: "test-api.js",
});
}
export function resolveRelativeBundledPluginPublicModuleId(params: {
fromModuleUrl: string;
pluginId: string;
artifactBasename: string;
}): string {
const metadata = findBundledPluginMetadata(params.pluginId);
const fromFilePath = fileURLToPath(params.fromModuleUrl);
const targetPath = path.resolve(
OPENCLAW_PACKAGE_ROOT,
"extensions",
metadata.dirName,
params.artifactBasename,
);
const relativePath = path
.relative(path.dirname(fromFilePath), targetPath)
.replaceAll(path.sep, "/");
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
}