perf(server): do not watch bundled plugins for file changes (#16677)

Bundled sandbox-provider plugins are installed from local paths inside the image, so production started a chokidar dev watcher for each of them over files that never change. createPluginDevWatcher now takes unwatchedRoots (compared in resolved and real-path form), and the app passes the bundled plugin catalog root; plugins installed elsewhere are watched as before, and a plugin moving into an unwatched root closes its old watcher and cancels any pending restart.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin FoleyandPaperclip authored and GitHub committed 2026-10-17 18:39:11 -07:00
1 parent d30648dee8
commit bebeb389f1
3 files changed
+91 -2

No files matched your search

@@ -1,4 +1,4 @@
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { EventEmitter } from "node:events";
import os from "node:os";
import path from "node:path";
@@ -134,6 +134,75 @@ describe("createPluginDevWatcher", () => {
devWatcher.close();
});
it("never watches plugins installed from an unwatched root such as the bundled catalog", async () => {
const catalogRoot = makeTempPluginDir();
const bundledDir = path.join(catalogRoot, "sandbox-providers", "example");
writePluginPackage(bundledDir);
const localDir = makeTempPluginDir();
writePluginPackage(localDir);
installMockFsWatcher();
const lifecycle = createLifecycle();
const devWatcher = createPluginDevWatcher(
lifecycle as never,
async (pluginId) => (pluginId === "bundled" ? bundledDir : localDir),
undefined,
{ unwatchedRoots: [catalogRoot] },
);
lifecycle.emit("plugin.loaded", { pluginId: "bundled", pluginKey: "bundled" });
devWatcher.watch("bundled-direct", bundledDir);
lifecycle.emit("plugin.loaded", { pluginId: "local", pluginKey: "local" });
await vi.waitFor(() => expect(chokidarMock.watch).toHaveBeenCalledTimes(1));
const [watchedPaths] = chokidarMock.watch.mock.calls[0] ?? [];
expect(watchedPaths).toContain(path.join(localDir, "dist", "worker.js"));
const everyWatched = chokidarMock.watch.mock.calls.flatMap(([paths]) => paths as string[]);
expect(everyWatched.some((watched) => watched.startsWith(catalogRoot))).toBe(false);
devWatcher.close();
});
it("matches unwatched roots through symbolic links", async () => {
const realRoot = makeTempPluginDir();
const linkParent = makeTempPluginDir();
const linkedRoot = path.join(linkParent, "catalog");
symlinkSync(realRoot, linkedRoot, "dir");
const bundledDir = path.join(realRoot, "sandbox-providers", "example");
writePluginPackage(bundledDir);
installMockFsWatcher();
const devWatcher = createPluginDevWatcher(createLifecycle() as never, undefined, undefined, { unwatchedRoots: [linkedRoot] });
devWatcher.watch("bundled", bundledDir);
devWatcher.watch("bundled-through-link", path.join(linkedRoot, "sandbox-providers", "example"));
expect(chokidarMock.watch).not.toHaveBeenCalled();
devWatcher.close();
});
it("closes the old watcher and cancels a pending restart when a plugin moves into an unwatched root", async () => {
vi.useFakeTimers();
const localDir = makeTempPluginDir();
writePluginPackage(localDir);
const catalogRoot = makeTempPluginDir();
const bundledDir = path.join(catalogRoot, "sandbox-providers", "example");
writePluginPackage(bundledDir);
const { fakeWatcher, handlers } = installMockFsWatcher();
const lifecycle = createLifecycle();
const devWatcher = createPluginDevWatcher(lifecycle as never, undefined, undefined, { unwatchedRoots: [catalogRoot] });
devWatcher.watch("plugin-1", localDir);
handlers.all?.("change", path.join(localDir, "dist", "worker.js"));
devWatcher.watch("plugin-1", bundledDir);
await vi.advanceTimersByTimeAsync(1_000);
expect(fakeWatcher.close).toHaveBeenCalled();
expect(lifecycle.restartWorker).not.toHaveBeenCalled();
expect(chokidarMock.watch).toHaveBeenCalledTimes(1);
devWatcher.close();
});
it("debounces watched file changes and restarts the plugin worker", async () => {
vi.useFakeTimers();
const pluginDir = makeTempPluginDir();