Lines 38-78javascript
38 if (!existsSync(path))
39 return { plugins: [] };
40 const raw = await readFile(path, "utf8");
42 return pluginConfigSchema.parse(JSON.parse(raw));
45 // Do NOT silently fall back to empty: a caller that then saves would wipe
46 // the enabled-plugins list. Surface it so callers can abort.
47 throw new Error(`plugin config at ${path} is unreadable/invalid: ${err instanceof Error ? err.message : String(err)}`);
50export async function savePluginConfig(path, cfg) {
51 await mkdir(resolve(path, ".."), { recursive: true });
52 // Write atomically (tmp + rename) so an interrupted write can't corrupt it.
53 const tmp = `${path}.tmp`;
54 await writeFile(tmp, JSON.stringify(cfg, null, 2));
55 await rename(tmp, path);
58 * Resolve the entry file and prove it stays inside the plugin directory. Uses
59 * realpath so a symlink under the plugin dir can't point `import()` outside, and
60 * checks on a path boundary (not a bare prefix) so a sibling like `<dir>-evil`
MediumDynamic Require
Package source references dynamic require/import behavior.
dist/kernel/plugin.jsView on unpkg · L58 61 * can't pass either. Returns the real (canonical) entry path.
63async function safeEntryPath(dir, main) {
64 const base = await realpath(dir);
67 entry = await realpath(resolve(dir, main));
70 throw new Error("manifest.main does not resolve to a file inside the plugin directory");
72 if (entry !== base && !entry.startsWith(base + sep)) {
73 throw new Error("manifest.main escapes the plugin directory");
77/** Read + validate a plugin manifest from a plugin directory. */
78export async function readManifest(dir) {