Load and initialization sequence#
The manager scans plugin directories, parses each manifest, evaluates the entry file, stores the module and then calls onInit for enabled plugins. Evaluation happens for disabled plugins too. Keep top-level code limited to declarations and state; start work in a hook instead.
onInit is called on load, reload and enable. Its returned promise is not awaited, and the surrounding try/catch only handles synchronous throws. Catch asynchronous initialization failures explicitly.
module.exports = {
onInit(context) {
initialize(context).catch(error => context.log(error.message, 'error'));
}
};
async function initialize(context) {
context.log('Initializing');
// Await optional work here and handle its failure above.
}Recording and save order#
For a manual start, the recorder first records its start timestamp, then awaits onRecordingStart, emits status and shows the start notification. At manual completion it finalizes and validates the MP4, inserts the clip into the library, emits clip:created, awaits onClipSaved, clears the manual marker and awaits onRecordingStop. A replay calls onClipSaved without the manual start/stop pair.
Screenshots call onScreenshot instead of onClipSaved. Hook dispatch is sequential across enabled plugins. One slow hook can delay later plugins and serialized recording work; offload network work and always attach an error handler.
What triggers hot reload#
The recursive filesystem watcher reacts to .js and .json changes with a 400 ms debounce. A relevant external edit reloads all plugin modules, not only the modified directory. Manifest saves through the manager suppress watcher reload while its internal-saving flag is set, normally for about one second.
Explicit Reload Plugins is useful after a change made during that suppression interval or when a filesystem does not deliver watcher events. Editing a .json data file can also cause reloads, which is why the journal example uses .jsonl for its event log.
Timer and cleanup limits#
The current API has no dispose/disable hook and does not automatically clear timers created by old VM contexts. Reloading a plugin that started a permanent interval can leave the previous interval running. An interval guard inside one module only prevents duplicates within that module instance.
Prefer event-driven hooks and bounded tasks. Check context.manifest.enabled before work when retaining a context, but remember that old contexts can hold old manifest objects after reload. For reliable cleanup of an already leaking plugin, disable it, remove recurring work from its source and restart the native service after recording ends.
Implementation reference: src/main/plugins/PluginManager.ts; src/native/recorder.ts · native 2.0.7.