Lines 40-80javascript
40 if (!existsSync(path))
41 return { plugins: [], features: {} };
42 const raw = await readFile(path, "utf8");
44 return pluginConfigSchema.parse(JSON.parse(raw));
47 // Do NOT silently fall back to empty: a caller that then saves would wipe
48 // the enabled-plugins list. Surface it so callers can abort.
49 throw new Error(`plugin config at ${path} is unreadable/invalid: ${err instanceof Error ? err.message : String(err)}`);
52export async function savePluginConfig(path, cfg) {
53 await mkdir(resolve(path, ".."), { recursive: true });
54 // Write atomically (tmp + rename) so an interrupted write can't corrupt it.
55 const tmp = `${path}.tmp`;
56 await writeFile(tmp, JSON.stringify(cfg, null, 2));
57 await rename(tmp, path);
60 * Resolve the entry file and prove it stays inside the plugin directory. Uses
61 * realpath so a symlink under the plugin dir can't point `import()` outside, and
62 * 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 · L60 63 * can't pass either. Returns the real (canonical) entry path.
65async function safeEntryPath(dir, main) {
66 const base = await realpath(dir);
69 entry = await realpath(resolve(dir, main));
72 throw new Error("manifest.main does not resolve to a file inside the plugin directory");
74 if (entry !== base && !entry.startsWith(base + sep)) {
75 throw new Error("manifest.main escapes the plugin directory");
79/** Read + validate a plugin manifest from a plugin directory. */
80export async function readManifest(dir) {