mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:40:44 +00:00
refactor: unify plugin startup metadata planning
This commit is contained in:
@@ -43,11 +43,21 @@ vi.mock("../channels/config-presence.js", () => ({
|
||||
hasMeaningfulChannelConfig,
|
||||
}));
|
||||
|
||||
vi.mock("./manifest-registry-installed.js", () => ({
|
||||
loadPluginManifestRegistryForInstalledIndex,
|
||||
}));
|
||||
vi.mock("./manifest-registry-installed.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./manifest-registry-installed.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadPluginManifestRegistryForInstalledIndex,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./plugin-registry-snapshot.js", () => ({ loadPluginRegistrySnapshot }));
|
||||
vi.mock("./plugin-registry-snapshot.js", () => ({
|
||||
loadPluginRegistrySnapshot,
|
||||
loadPluginRegistrySnapshotWithMetadata: (params: unknown) => ({
|
||||
snapshot: loadPluginRegistrySnapshot(params),
|
||||
diagnostics: [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("./plugin-registry-contributions.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./plugin-registry-contributions.js")>();
|
||||
@@ -62,6 +72,7 @@ import {
|
||||
listConfiguredAnnounceChannelIdsForConfig,
|
||||
listConfiguredChannelIdsForReadOnlyScope,
|
||||
listExplicitConfiguredChannelIdsForConfig,
|
||||
loadGatewayStartupPluginPlan,
|
||||
resolveConfiguredChannelPresencePolicy,
|
||||
resolveConfiguredDeferredChannelPluginIds,
|
||||
resolveConfiguredChannelPluginIds,
|
||||
@@ -854,6 +865,31 @@ describe("resolveGatewayStartupPluginIds", () => {
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("loads channel, deferred, and startup plugin ids from one manifest registry", () => {
|
||||
const registry = createManifestRegistryFixture();
|
||||
const index = createInstalledPluginIndexFixture(registry);
|
||||
loadPluginRegistrySnapshot.mockReset().mockReturnValue(index);
|
||||
loadPluginManifestRegistryForInstalledIndex.mockReset().mockReturnValue(registry);
|
||||
|
||||
const plan = loadGatewayStartupPluginPlan({
|
||||
config: {
|
||||
channels: {
|
||||
"demo-channel": {
|
||||
token: "configured",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
workspaceDir: "/tmp",
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(plan.channelPluginIds).toContain("demo-channel");
|
||||
expect(plan.pluginIds).toContain("demo-channel");
|
||||
expect(plan.configuredDeferredChannelPluginIds).toEqual([]);
|
||||
expect(loadPluginRegistrySnapshot).toHaveBeenCalledOnce();
|
||||
expect(loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("does not treat explicitly disabled stale channel config as deferred startup intent", () => {
|
||||
useManifestRegistryFixture(createManifestRegistryFixtureWithWorkspaceDemoChannel());
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ export {
|
||||
resolveChannelPluginIdsFromRegistry,
|
||||
resolveConfiguredDeferredChannelPluginIds,
|
||||
resolveConfiguredDeferredChannelPluginIdsFromRegistry,
|
||||
loadGatewayStartupPluginPlan,
|
||||
resolveGatewayStartupPluginIds,
|
||||
resolveGatewayStartupPluginPlanFromRegistry,
|
||||
resolveGatewayStartupPluginIdsFromRegistry,
|
||||
type GatewayStartupPluginPlan,
|
||||
} from "./gateway-startup-plugin-ids.js";
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from "./plugin-control-plane-context.js";
|
||||
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
|
||||
|
||||
export function resolvePluginMetadataSnapshotConfigFingerprint(
|
||||
export function resolvePluginMetadataControlPlaneFingerprint(
|
||||
config?: OpenClawConfig,
|
||||
options: Omit<ResolvePluginControlPlaneContextParams, "config"> = {},
|
||||
): string {
|
||||
@@ -30,7 +30,7 @@ export function setCurrentPluginMetadataSnapshot(
|
||||
setCurrentPluginMetadataSnapshotState(
|
||||
snapshot,
|
||||
snapshot
|
||||
? resolvePluginMetadataSnapshotConfigFingerprint(options.config, {
|
||||
? resolvePluginMetadataControlPlaneFingerprint(options.config, {
|
||||
env: options.env,
|
||||
index: snapshot.index,
|
||||
policyHash: snapshot.policyHash,
|
||||
@@ -63,15 +63,12 @@ export function getCurrentPluginMetadataSnapshot(
|
||||
return undefined;
|
||||
}
|
||||
if (params.config) {
|
||||
const requestedConfigFingerprint = resolvePluginMetadataSnapshotConfigFingerprint(
|
||||
params.config,
|
||||
{
|
||||
env: params.env,
|
||||
index: snapshot.index,
|
||||
policyHash: snapshot.policyHash,
|
||||
workspaceDir: params.workspaceDir,
|
||||
},
|
||||
);
|
||||
const requestedConfigFingerprint = resolvePluginMetadataControlPlaneFingerprint(params.config, {
|
||||
env: params.env,
|
||||
index: snapshot.index,
|
||||
policyHash: snapshot.policyHash,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
if (configFingerprint && configFingerprint !== requestedConfigFingerprint) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import {
|
||||
listExplicitConfiguredChannelIdsForConfig,
|
||||
loadGatewayStartupPluginPlan,
|
||||
resolveConfiguredChannelPluginIds,
|
||||
resolveGatewayStartupPluginIds,
|
||||
} from "./channel-plugin-ids.js";
|
||||
import { normalizePluginsConfig } from "./config-state.js";
|
||||
import { passesManifestOwnerBasePolicy } from "./manifest-owner-policy.js";
|
||||
@@ -155,12 +155,12 @@ export function resolveEffectivePluginIds(params: {
|
||||
})) {
|
||||
ids.add(pluginId);
|
||||
}
|
||||
for (const pluginId of resolveGatewayStartupPluginIds({
|
||||
for (const pluginId of loadGatewayStartupPluginPlan({
|
||||
config: effectiveConfig,
|
||||
activationSourceConfig: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
})) {
|
||||
}).pluginIds) {
|
||||
ids.add(pluginId);
|
||||
}
|
||||
return [...ids].toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
@@ -15,13 +15,23 @@ import { hasExplicitChannelConfig } from "./channel-presence-policy.js";
|
||||
import { collectPluginConfigContractMatches } from "./config-contracts.js";
|
||||
import { resolveEffectivePluginActivationState } from "./config-state.js";
|
||||
import type { InstalledPluginIndexRecord } from "./installed-plugin-index.js";
|
||||
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
|
||||
import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import {
|
||||
isPluginMetadataSnapshotCompatible,
|
||||
loadPluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "./plugin-metadata-snapshot.js";
|
||||
import {
|
||||
createPluginRegistryIdNormalizer,
|
||||
normalizePluginsConfigWithRegistry,
|
||||
} from "./plugin-registry-contributions.js";
|
||||
import { loadPluginRegistrySnapshot } from "./plugin-registry-snapshot.js";
|
||||
import type { PluginRegistrySnapshot } from "./plugin-registry-snapshot.js";
|
||||
|
||||
export type GatewayStartupPluginPlan = {
|
||||
channelPluginIds: readonly string[];
|
||||
configuredDeferredChannelPluginIds: readonly string[];
|
||||
pluginIds: readonly string[];
|
||||
};
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
||||
@@ -235,19 +245,7 @@ export function resolveChannelPluginIds(params: {
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const index = loadPluginRegistrySnapshot({
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
includeDisabled: true,
|
||||
});
|
||||
return resolveChannelPluginIdsFromRegistry({ manifestRegistry });
|
||||
return [...loadGatewayStartupPluginPlan(params).channelPluginIds];
|
||||
}
|
||||
|
||||
export function resolveChannelPluginIdsFromRegistry(params: {
|
||||
@@ -262,7 +260,7 @@ export function resolveChannelPluginIdsFromRegistry(params: {
|
||||
export function resolveConfiguredDeferredChannelPluginIdsFromRegistry(params: {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index: ReturnType<typeof loadPluginRegistrySnapshot>;
|
||||
index: PluginRegistrySnapshot;
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
}): string[] {
|
||||
const configuredChannelIds = new Set(listPotentialEnabledChannelIds(params.config, params.env));
|
||||
@@ -302,33 +300,25 @@ export function resolveConfiguredDeferredChannelPluginIds(params: {
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const index = loadPluginRegistrySnapshot({
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
includeDisabled: true,
|
||||
});
|
||||
return resolveConfiguredDeferredChannelPluginIdsFromRegistry({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
index,
|
||||
manifestRegistry,
|
||||
});
|
||||
return [...loadGatewayStartupPluginPlan(params).configuredDeferredChannelPluginIds];
|
||||
}
|
||||
|
||||
export function resolveGatewayStartupPluginIdsFromRegistry(params: {
|
||||
export function resolveGatewayStartupPluginPlanFromRegistry(params: {
|
||||
config: OpenClawConfig;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index: ReturnType<typeof loadPluginRegistrySnapshot>;
|
||||
index: PluginRegistrySnapshot;
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
}): string[] {
|
||||
}): GatewayStartupPluginPlan {
|
||||
const channelPluginIds = resolveChannelPluginIdsFromRegistry({
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
});
|
||||
const configuredDeferredChannelPluginIds = resolveConfiguredDeferredChannelPluginIdsFromRegistry({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
index: params.index,
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
});
|
||||
const configuredChannelIds = new Set(listPotentialEnabledChannelIds(params.config, params.env));
|
||||
const pluginsConfig = normalizePluginsConfigWithRegistry(params.config.plugins, params.index, {
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
@@ -358,7 +348,7 @@ export function resolveGatewayStartupPluginIdsFromRegistry(params: {
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
}),
|
||||
});
|
||||
return params.index.plugins
|
||||
const pluginIds = params.index.plugins
|
||||
.filter((plugin) => {
|
||||
const manifest = findManifestPlugin(manifestLookup, plugin.pluginId);
|
||||
if (
|
||||
@@ -427,6 +417,57 @@ export function resolveGatewayStartupPluginIdsFromRegistry(params: {
|
||||
return activationState.source === "explicit" || activationState.source === "default";
|
||||
})
|
||||
.map((plugin) => plugin.pluginId);
|
||||
return {
|
||||
channelPluginIds,
|
||||
configuredDeferredChannelPluginIds,
|
||||
pluginIds,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGatewayStartupPluginIdsFromRegistry(params: {
|
||||
config: OpenClawConfig;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index: PluginRegistrySnapshot;
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
}): string[] {
|
||||
return [...resolveGatewayStartupPluginPlanFromRegistry(params).pluginIds];
|
||||
}
|
||||
|
||||
export function loadGatewayStartupPluginPlan(params: {
|
||||
config: OpenClawConfig;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index?: PluginRegistrySnapshot;
|
||||
metadataSnapshot?: PluginMetadataSnapshot;
|
||||
}): GatewayStartupPluginPlan {
|
||||
const snapshotConfig = params.activationSourceConfig ?? params.config;
|
||||
const metadataSnapshot =
|
||||
params.metadataSnapshot &&
|
||||
isPluginMetadataSnapshotCompatible({
|
||||
snapshot: params.metadataSnapshot,
|
||||
config: snapshotConfig,
|
||||
env: params.env,
|
||||
workspaceDir: params.workspaceDir,
|
||||
index: params.index,
|
||||
})
|
||||
? params.metadataSnapshot
|
||||
: loadPluginMetadataSnapshot({
|
||||
config: snapshotConfig,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
...(params.index ? { index: params.index } : {}),
|
||||
});
|
||||
return resolveGatewayStartupPluginPlanFromRegistry({
|
||||
config: params.config,
|
||||
...(params.activationSourceConfig !== undefined
|
||||
? { activationSourceConfig: params.activationSourceConfig }
|
||||
: {}),
|
||||
env: params.env,
|
||||
index: metadataSnapshot.index,
|
||||
manifestRegistry: metadataSnapshot.manifestRegistry,
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGatewayStartupPluginIds(params: {
|
||||
@@ -435,25 +476,5 @@ export function resolveGatewayStartupPluginIds(params: {
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const index = loadPluginRegistrySnapshot({
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
includeDisabled: true,
|
||||
});
|
||||
return resolveGatewayStartupPluginIdsFromRegistry({
|
||||
config: params.config,
|
||||
...(params.activationSourceConfig !== undefined
|
||||
? { activationSourceConfig: params.activationSourceConfig }
|
||||
: {}),
|
||||
env: params.env,
|
||||
index,
|
||||
manifestRegistry,
|
||||
});
|
||||
return [...loadGatewayStartupPluginPlan(params).pluginIds];
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
resolveChannelPluginIdsFromRegistry,
|
||||
resolveConfiguredDeferredChannelPluginIdsFromRegistry,
|
||||
resolveGatewayStartupPluginIdsFromRegistry,
|
||||
resolveGatewayStartupPluginPlanFromRegistry,
|
||||
type GatewayStartupPluginPlan,
|
||||
} from "./channel-plugin-ids.js";
|
||||
import { hashJson } from "./installed-plugin-index-hash.js";
|
||||
import {
|
||||
@@ -15,11 +14,7 @@ import type { PluginRegistrySnapshot } from "./plugin-registry-snapshot.js";
|
||||
|
||||
export type PluginLookUpTableOwnerMaps = PluginMetadataSnapshotOwnerMaps;
|
||||
|
||||
export type PluginLookUpTableStartupPlan = {
|
||||
channelPluginIds: readonly string[];
|
||||
configuredDeferredChannelPluginIds: readonly string[];
|
||||
pluginIds: readonly string[];
|
||||
};
|
||||
export type PluginLookUpTableStartupPlan = GatewayStartupPluginPlan;
|
||||
|
||||
export type PluginLookUpTableMetrics = {
|
||||
registrySnapshotMs: number;
|
||||
@@ -72,14 +67,7 @@ export function loadPluginLookUpTable(params: LoadPluginLookUpTableParams): Plug
|
||||
});
|
||||
const { index, manifestRegistry } = metadataSnapshot;
|
||||
const startupPlanStartedAt = performance.now();
|
||||
const channelPluginIds = resolveChannelPluginIdsFromRegistry({ manifestRegistry });
|
||||
const configuredDeferredChannelPluginIds = resolveConfiguredDeferredChannelPluginIdsFromRegistry({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
index,
|
||||
manifestRegistry,
|
||||
});
|
||||
const pluginIds = resolveGatewayStartupPluginIdsFromRegistry({
|
||||
const startup = resolveGatewayStartupPluginPlanFromRegistry({
|
||||
config: params.config,
|
||||
...(params.activationSourceConfig !== undefined
|
||||
? { activationSourceConfig: params.activationSourceConfig }
|
||||
@@ -89,11 +77,6 @@ export function loadPluginLookUpTable(params: LoadPluginLookUpTableParams): Plug
|
||||
manifestRegistry,
|
||||
});
|
||||
const startupPlanMs = performance.now() - startupPlanStartedAt;
|
||||
const startup = {
|
||||
channelPluginIds,
|
||||
configuredDeferredChannelPluginIds,
|
||||
pluginIds,
|
||||
};
|
||||
|
||||
return {
|
||||
...metadataSnapshot,
|
||||
@@ -112,8 +95,8 @@ export function loadPluginLookUpTable(params: LoadPluginLookUpTableParams): Plug
|
||||
...metadataSnapshot.metrics,
|
||||
startupPlanMs,
|
||||
totalMs: metadataSnapshot.metrics.totalMs + startupPlanMs,
|
||||
startupPluginCount: pluginIds.length,
|
||||
deferredChannelPluginCount: configuredDeferredChannelPluginIds.length,
|
||||
startupPluginCount: startup.pluginIds.length,
|
||||
deferredChannelPluginCount: startup.configuredDeferredChannelPluginIds.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export type {
|
||||
PluginMetadataSnapshotRegistryDiagnostic,
|
||||
} from "./plugin-metadata-snapshot.types.js";
|
||||
|
||||
function resolvePluginMetadataSnapshotConfigFingerprint(
|
||||
function resolvePluginMetadataControlPlaneFingerprint(
|
||||
params: Pick<LoadPluginMetadataSnapshotParams, "config" | "env" | "workspaceDir"> & {
|
||||
index?: InstalledPluginIndex;
|
||||
policyHash?: string;
|
||||
@@ -60,7 +60,7 @@ export function isPluginMetadataSnapshotCompatible(params: {
|
||||
params.snapshot.policyHash === resolveInstalledPluginIndexPolicyHash(params.config) &&
|
||||
(!params.snapshot.configFingerprint ||
|
||||
params.snapshot.configFingerprint ===
|
||||
resolvePluginMetadataSnapshotConfigFingerprint({
|
||||
resolvePluginMetadataControlPlaneFingerprint({
|
||||
config: params.config,
|
||||
env,
|
||||
index: params.index ?? params.snapshot.index,
|
||||
@@ -195,7 +195,7 @@ function loadPluginMetadataSnapshotImpl(
|
||||
|
||||
return {
|
||||
policyHash: index.policyHash,
|
||||
configFingerprint: resolvePluginMetadataSnapshotConfigFingerprint({
|
||||
configFingerprint: resolvePluginMetadataControlPlaneFingerprint({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
index,
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
PluginManifestRegistry,
|
||||
} from "./manifest-registry.js";
|
||||
import { isPackageIncludedInCoreBundle } from "./manifest.js";
|
||||
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import {
|
||||
createPluginRegistryIdNormalizer,
|
||||
@@ -28,22 +29,10 @@ export {
|
||||
type PluginRegistryIdNormalizerOptions,
|
||||
} from "./plugin-registry-id-normalizer.js";
|
||||
|
||||
export type PluginLookUpTable = {
|
||||
index: PluginRegistrySnapshot;
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
plugins: readonly PluginManifestRecord[];
|
||||
normalizePluginId: (pluginId: string) => string;
|
||||
owners: {
|
||||
channels: ReadonlyMap<string, readonly string[]>;
|
||||
channelConfigs: ReadonlyMap<string, readonly string[]>;
|
||||
providers: ReadonlyMap<string, readonly string[]>;
|
||||
modelCatalogProviders: ReadonlyMap<string, readonly string[]>;
|
||||
cliBackends: ReadonlyMap<string, readonly string[]>;
|
||||
setupProviders: ReadonlyMap<string, readonly string[]>;
|
||||
commandAliases: ReadonlyMap<string, readonly string[]>;
|
||||
contracts: ReadonlyMap<string, readonly string[]>;
|
||||
};
|
||||
};
|
||||
export type PluginLookUpTable = Pick<
|
||||
PluginMetadataSnapshot,
|
||||
"index" | "manifestRegistry" | "plugins" | "normalizePluginId" | "owners"
|
||||
>;
|
||||
|
||||
export type PluginRegistryContributionOptions = LoadPluginRegistryParams & {
|
||||
includeDisabled?: boolean;
|
||||
|
||||
@@ -153,6 +153,41 @@ describe("bundled plugin public surface loader", () => {
|
||||
expect(createJiti).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not cache missing public artifact locations", async () => {
|
||||
vi.doMock("./native-module-require.js", () => ({
|
||||
tryNativeRequireJavaScriptModule: (modulePath: string) => ({
|
||||
ok: true,
|
||||
moduleExport: { marker: path.basename(path.dirname(modulePath)) },
|
||||
}),
|
||||
}));
|
||||
vi.resetModules();
|
||||
|
||||
const publicSurfaceLoader = await importFreshModule<
|
||||
typeof import("./public-surface-loader.js")
|
||||
>(import.meta.url, "./public-surface-loader.js?scope=missing-location-retry");
|
||||
const tempRoot = createTempDir();
|
||||
const bundledPluginsDir = path.join(tempRoot, "dist");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledPluginsDir;
|
||||
|
||||
expect(
|
||||
publicSurfaceLoader.resolveBundledPluginPublicArtifactPath({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
}),
|
||||
).toBeNull();
|
||||
|
||||
const modulePath = path.join(bundledPluginsDir, "demo", "api.js");
|
||||
fs.mkdirSync(path.dirname(modulePath), { recursive: true });
|
||||
fs.writeFileSync(modulePath, 'export const marker = "demo";\n', "utf8");
|
||||
|
||||
expect(
|
||||
publicSurfaceLoader.loadBundledPluginPublicArtifactModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
}).marker,
|
||||
).toBe("demo");
|
||||
});
|
||||
|
||||
it("rejects public artifacts that change after boundary validation", async () => {
|
||||
const createJiti = vi.fn(() => vi.fn(() => ({ marker: "should-not-load" })));
|
||||
vi.doMock("jiti", () => ({
|
||||
|
||||
@@ -25,7 +25,7 @@ const publicSurfaceLocationCache = new Map<
|
||||
{
|
||||
modulePath: string;
|
||||
boundaryRoot: string;
|
||||
} | null
|
||||
}
|
||||
>();
|
||||
const moduleLoaders: PluginModuleLoaderCache = createPluginModuleLoaderCache();
|
||||
|
||||
@@ -84,11 +84,14 @@ function resolvePublicSurfaceLocation(params: {
|
||||
artifactBasename: string;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const key = createResolutionKey(params);
|
||||
if (publicSurfaceLocationCache.has(key)) {
|
||||
return publicSurfaceLocationCache.get(key) ?? null;
|
||||
const cached = publicSurfaceLocationCache.get(key);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const resolved = resolvePublicSurfaceLocationUncached(params);
|
||||
publicSurfaceLocationCache.set(key, resolved);
|
||||
if (resolved) {
|
||||
publicSurfaceLocationCache.set(key, resolved);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user