Plugin lifecycle reference
The plugin lifecycle separates durable installation and user preference from runtime execution. Only an activated plugin contributes live capabilities.
Runtime states
| State | Exact meaning |
|---|---|
| Installed | Phials has a matching set of release artifacts for the plugin. No plugin code needs to be running. |
| Enabled | The user preference allows the installed plugin to run. Compatibility, trust, load, or activation checks can still block it. |
| Loaded | Phials has imported the plugin’s JavaScript module and obtained its default plugin export. |
| Activated | Phials has accepted the plugin definition, prepared its public API and plugin-owned data contracts, registered its capabilities, completed onActivate(), and committed the runtime transition. |
| Deactivated | The plugin is not contributing capabilities or owned runtime registrations. Its release artifacts and durable data can remain. |
| Reloaded | Phials has completed the defined before-reload, deactivation, replacement activation, and after-reload path. |
The normal forward path is:
installed → enabled → loaded → activatedThis is not a single status. For example, a plugin can be installed and enabled but not loaded because community plugin safe mode is on. It can be loaded but not activated because its definition or activation hook fails.
Eligibility before loading
Phials does not load community-plugin code until all pre-load checks succeed:
- Community plugin safe mode is off.
- The plugin is installed and enabled.
- Its manifest is valid and its identities agree.
- The installed artifact inventory still matches the checksummed candidate.
- The running app satisfies both runtime compatibility checks.
- The manifest’s current permission set has user approval.
Failure at this boundary leaves the plugin unactivated and exposes none of its providers, themes, styles, subscriptions, or other runtime contributions.
Loading and definition acceptance
After eligibility succeeds, Phials:
- preloads manifest-owned icon identifiers without importing plugin code;
- reads and imports
main.js; - requires a default export;
- calls the export when it is a factory, otherwise uses it as the PhialsPlugin definition;
- verifies that the definition ID and version match the installed manifest;
- prepares the permission-gated PluginAPI and documented callback scopes.
Top-level module code runs during loading, before activation. It has no
lifecycle-managed PluginAPI. Keep it limited to repeatable declarations and
factory setup. Runtime work belongs in onActivate(api).
Activation order
For an accepted definition, Phials performs one activation transition in this order:
- Load plugin settings and apply schema defaults.
- Initialize the declared plugin database schema.
- Register every provider and its commands, components, shortcuts, and activation-scoped event ownership.
- Register plugin theme assets.
- Call and await
onActivate(api), when present. - Install optional active styles and commit the plugin as activated.
The supplied PluginAPI is the plugin’s base runtime scope. Provider callbacks can receive a narrower or specialized API described by that provider’s contract. Do not construct, retain across replacement code, or widen these API objects.
Activation is atomic from the author’s perspective. It succeeds only after the complete transition. If any step fails, Phials reports an activation failure, removes contributions created by the attempt, and does not mark the plugin activated.
Rollback removes styles and themes, unregisters providers and shortcuts, releases event and file subscriptions, and clears activation-scoped caches in reverse ownership order. Stable-ID collisions fail the transition; Phials never replaces the earlier owner.
Calling activate for an already activated plugin is a no-op. Phials does not
call onActivate() again until a complete deactivation has occurred.
Deactivation order
Deactivation stops runtime behavior without uninstalling the release. Phials:
- calls and awaits
onDeactivate(), when present; - unregisters plugin themes;
- unregisters all providers;
- unregisters plugin-owned shortcuts;
- removes plugin-owned event listeners, file subscriptions, and active styles;
- clears runtime settings caches and other activation-scoped handles;
- invalidates the base, Preview, and Metadata API objects supplied by that activation;
- commits the plugin as deactivated.
Phials continues host-owned cleanup even when onDeactivate() throws. The
plugin becomes deactivated and the error remains diagnosable; a failing hook
cannot keep providers or subscriptions active.
Use onDeactivate() to release resources the plugin owns directly, such as
timers, observers, workers, connections, and third-party subscriptions. Cleanup
must tolerate partial startup and be safe to repeat. Calling deactivate for an
already deactivated plugin is a no-op.
Deactivation preserves:
- installed release artifacts;
- the enabled preference unless the surrounding workflow changes it;
- plugin settings;
- plugin key-value storage;
- plugin database records;
- other durable data governed by a documented storage contract.
Uninstallation and explicit data removal are separate workflows.
Reload order and state transfer
Reload replaces running plugin code through lifecycle hooks. For an activated plugin, Phials:
- calls and awaits
onBeforeReload(); - clones the returned plain data as transient reload state;
- stages and validates the replacement identity and checksummed artifacts;
- completely deactivates the current code;
- atomically swaps and activates the replacement runtime, contributions, and styles;
- calls and awaits
onAfterReload(state)on the replacement, when the before-reload hook returned a value.
undefined means there is no reload state to transfer, so onAfterReload() is
not called. Durable settings, storage, and database records remain available
through their own contracts and do not belong in reload state.
Reload state should be plain, bounded, and version-tolerant. It must not contain DOM nodes, component instances, API objects, subscriptions, open handles, or other resources owned by the old activation.
Non-cloneable state fails before deactivation, so the current activation remains unchanged.
If onBeforeReload() fails, reload stops before deactivation. If replacement
loading, activation, or onAfterReload() fails, Phials rolls back the
replacement, restores the previous activated release, and reports the failing
stage. The failed replacement never remains partly active. Reload handoff state
belongs only to the replacement attempt and is not passed back into the restored
release.
Failure guarantees
| Failure point | Result |
|---|---|
| Manifest, identity, safe-mode, permission-review, or compatibility check | Plugin remains unloaded and unactivated |
| Module read or import | Plugin remains unactivated; no provider or style contribution remains |
| Default export or definition validation | Plugin remains unactivated; the definition is not registered |
Settings, database, provider, theme, style, or onActivate() work | Activation is rolled back and all contributions from the attempt are removed |
onDeactivate() or plugin-owned cleanup | Host-owned cleanup continues; plugin becomes deactivated and the error is reported |
| Reload before-state hook | Existing activation remains in place and reload stops |
| Reload replacement load, activation, or after-state hook | Previous activated release is restored; no mixed activation remains; failure is reported and durable data is preserved |
An update that cannot activate its new release restores the previous installed release when a recoverable backup is available. Permission review is not an activation failure: the new release remains installed but cannot activate until the user approves its current permission set.
For a conceptual walkthrough, see Understand the plugin lifecycle.