mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-20 21:51:28 +00:00
fix(plugin-sdk): resolve facade post-load re-entry (#61286)
This commit is contained in:
@@ -75,6 +75,7 @@ afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
clearRuntimeConfigSnapshot();
|
||||
resetFacadeRuntimeStateForTest();
|
||||
vi.doUnmock("../plugins/manifest-registry.js");
|
||||
delete (globalThis as typeof globalThis & Record<string, unknown>)[FACADE_RUNTIME_GLOBAL];
|
||||
if (originalBundledPluginsDir === undefined) {
|
||||
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
@@ -137,6 +138,56 @@ describe("plugin-sdk facade runtime", () => {
|
||||
expect(loaded.marker).toBe("circular-ok");
|
||||
});
|
||||
|
||||
it("back-fills the sentinel before post-load facade tracking re-enters", async () => {
|
||||
const dir = createBundledPluginDir("openclaw-facade-post-load-", "post-load-ok");
|
||||
const reentryMarkers: Array<string | undefined> = [];
|
||||
|
||||
vi.resetModules();
|
||||
vi.doMock("../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: vi.fn(() => {
|
||||
const load = (
|
||||
globalThis as typeof globalThis & {
|
||||
[FACADE_RUNTIME_GLOBAL]?: typeof loadBundledPluginPublicSurfaceModuleSync;
|
||||
}
|
||||
)[FACADE_RUNTIME_GLOBAL];
|
||||
if (typeof load !== "function") {
|
||||
throw new Error("missing facade runtime test loader");
|
||||
}
|
||||
const reentered = load<{ marker?: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
reentryMarkers.push(reentered.marker);
|
||||
return {
|
||||
plugins: [
|
||||
{
|
||||
id: "demo",
|
||||
rootDir: path.join(dir, "demo"),
|
||||
origin: "bundled",
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
const facadeRuntime = await import("./facade-runtime.js");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = dir;
|
||||
(globalThis as typeof globalThis & Record<string, unknown>)[FACADE_RUNTIME_GLOBAL] =
|
||||
facadeRuntime.loadBundledPluginPublicSurfaceModuleSync;
|
||||
|
||||
const loaded = facadeRuntime.loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
|
||||
expect(loaded.marker).toBe("post-load-ok");
|
||||
expect(reentryMarkers).toEqual(["post-load-ok"]);
|
||||
expect(facadeRuntime.listImportedBundledPluginFacadeIds()).toEqual(["demo"]);
|
||||
facadeRuntime.resetFacadeRuntimeStateForTest();
|
||||
vi.doUnmock("../plugins/manifest-registry.js");
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("clears the cache on load failure so retries re-execute", () => {
|
||||
const dir = createThrowingPluginDir("openclaw-facade-throw-");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = dir;
|
||||
@@ -148,7 +199,7 @@ describe("plugin-sdk facade runtime", () => {
|
||||
}),
|
||||
).toThrow("plugin load failure");
|
||||
|
||||
expect(listImportedBundledPluginFacadeIds()).toEqual(["bad"]);
|
||||
expect(listImportedBundledPluginFacadeIds()).toEqual([]);
|
||||
|
||||
// A second call must also throw (not return a stale empty sentinel).
|
||||
expect(() =>
|
||||
|
||||
@@ -365,11 +365,14 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
|
||||
|
||||
let loaded: T;
|
||||
try {
|
||||
// Track the owning plugin once module evaluation begins. Facade top-level
|
||||
// code may have already executed even if the module later throws.
|
||||
loadedFacadePluginIds.add(resolveTrackedFacadePluginId(params));
|
||||
loaded = getJiti(location.modulePath)(location.modulePath) as T;
|
||||
// Back-fill the sentinel before resolving plugin ownership. That lookup can
|
||||
// trigger config loading, plugin auto-enable, and other facade reads that
|
||||
// re-enter this loader for the same module path.
|
||||
Object.assign(sentinel, loaded);
|
||||
// Track the owning plugin after the module exports are visible through the
|
||||
// sentinel, so re-entrant callers never observe an empty facade object.
|
||||
loadedFacadePluginIds.add(resolveTrackedFacadePluginId(params));
|
||||
} catch (err) {
|
||||
loadedFacadeModules.delete(location.modulePath);
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user