GUIDE 16 / 28 · NATIVE 2.0.7

Local files & persistent data

Separate small manifest settings from plugin-owned data files, journals and HTML output.

Use the plugin directory#

The evaluator supplies fs, path and __dirname. __dirname is the loaded plugin folder. Use that location for plugin-owned state rather than the application install directory, which may be replaced by an update or be unwritable.

JavaScript
const journal = path.join(__dirname, 'clip-journal.jsonl');
async function appendMetadata(clip) {
  await fs.promises.appendFile(journal, JSON.stringify({
    id: clip.id, type: clip.type, duration: clip.duration,
    createdAt: clip.createdAt
  }) + '\n', 'utf8');
}

This stores metadata only and does not duplicate MP4 files. The small asynchronous append is awaited by a save hook; large video processing should run outside the serialized save path.

Choose a format and write strategy#

Manifest config works well for small preferences and a modest counter. JSON Lines works well for append-only event metadata, because each line is one JSON record. An arbitrary .json data file under plugins can trigger hot reload; use an intentional extension and document it.

When replacing a larger state file, write a temporary file in the same plugin directory and rename it after completion. This reduces partial-write reads. Use one writing queue per file; two concurrent append/replace strategies should not compete over the same state.

Missing files and limits#

A missing journal before the first clip is expected. Handle ENOENT separately from permission errors. Add retention/rotation for long-running journals and avoid reading an unlimited file synchronously merely to show a small statistic.

The included Clip Journal action uses file size rather than parsing all historical records. See the journal example for complete hooks and a native action.

Filesystem and user data#

The VM exposes the real fs module. A plugin can access files allowed to the service's Windows account; the plugin directory is a convention, not an enforced filesystem sandbox. Keep writes confined to plugin-owned paths by design.

If you accept a filename, reduce it to a permitted basename or reject path separators and traversal. Never overwrite the main liquid-config.json, liquid-clips.json or drive-tokens.json as plugin storage. Escape file-derived names before placing them in tab HTML.

Implementation reference: src/main/plugins/PluginManager.ts; src/main/store.ts · native 2.0.7.