Context
Surfaced during the v0.5.299 docs audit. The entire perry/plugin module — five docs pages worth of API surface — is fully implemented in the runtime but unreachable from TypeScript code.
Runtime FFI in crates/perry-runtime/src/plugin.rs (21 exported functions):
perry_plugin_load, perry_plugin_unload
perry_plugin_register_hook, perry_plugin_emit_hook
perry_plugin_invoke_tool
perry_plugin_list_plugins
- (plus the rest — see the
#[no_mangle] pub extern "C" fn perry_plugin_* block in that file)
But crates/perry-codegen/src/lower_call.rs has no module == "perry/plugin" branch in lower_call.rs or lower_native_method_call. Receiver-less native calls (loadPlugin(name)) fall through to the no-op stub at lower_call.rs:2849-2854. Instance-method calls (api.registerHook(...)) similarly have no PluginApi entry in the receiver-method dispatcher.
Verified end-to-end: a host-side test program calling loadPlugin(...) compiles + links + runs but every plugin call is silently elided.
What to do
Two pieces, mirroring the existing perry/ui and perry/system wiring:
-
Static-function dispatch — add module-level entries in lower_call.rs mapping ("perry/plugin", "loadPlugin") → "perry_plugin_load", etc., for every receiver-less call.
-
Instance-method dispatch — PluginApi's methods (registerHook, invokeTool, emit, …) need a method-table entry in the same shape as the existing Window instance-method dispatch (search lower_call.rs for method: "show", runtime: "perry_ui_window_show" for the pattern).
The runtime's argument shapes are documented per-function in crates/perry-runtime/src/plugin.rs. Most are (string_ptr, payload_ptr) style — match the existing perry/ui dispatcher's arg-coercion logic.
Acceptance criteria
Pointers
- Runtime impl:
crates/perry-runtime/src/plugin.rs
- Codegen dispatcher:
crates/perry-codegen/src/lower_call.rs
- Static-function pattern: search for
UiSig { method: "isDarkMode"
- Instance-method pattern: search for
method: "show", runtime: "perry_ui_window_show"
- Receiver-less early-out (the bug):
lower_call.rs:2849-2854
- Type defs (may need extending): consider adding
types/perry/plugin/index.d.ts if it doesn't already exist.
Estimated size
Substantial — ~150-300 lines of compiler code plus the matching type declarations and at least one runnable plugin example. Multi-day work historically.
Context
Surfaced during the v0.5.299 docs audit. The entire
perry/pluginmodule — five docs pages worth of API surface — is fully implemented in the runtime but unreachable from TypeScript code.Runtime FFI in
crates/perry-runtime/src/plugin.rs(21 exported functions):perry_plugin_load,perry_plugin_unloadperry_plugin_register_hook,perry_plugin_emit_hookperry_plugin_invoke_toolperry_plugin_list_plugins#[no_mangle] pub extern "C" fn perry_plugin_*block in that file)But
crates/perry-codegen/src/lower_call.rshas nomodule == "perry/plugin"branch inlower_call.rsorlower_native_method_call. Receiver-less native calls (loadPlugin(name)) fall through to the no-op stub atlower_call.rs:2849-2854. Instance-method calls (api.registerHook(...)) similarly have noPluginApientry in the receiver-method dispatcher.Verified end-to-end: a host-side test program calling
loadPlugin(...)compiles + links + runs but every plugin call is silently elided.What to do
Two pieces, mirroring the existing
perry/uiandperry/systemwiring:Static-function dispatch — add module-level entries in
lower_call.rsmapping("perry/plugin", "loadPlugin") → "perry_plugin_load", etc., for every receiver-less call.Instance-method dispatch —
PluginApi's methods (registerHook,invokeTool,emit, …) need a method-table entry in the same shape as the existingWindowinstance-method dispatch (searchlower_call.rsformethod: "show", runtime: "perry_ui_window_show"for the pattern).The runtime's argument shapes are documented per-function in
crates/perry-runtime/src/plugin.rs. Most are(string_ptr, payload_ptr)style — match the existing perry/ui dispatcher's arg-coercion logic.Acceptance criteria
perry/pluginfunction listed incrates/perry-runtime/src/plugin.rsis reachable from TypeScript.,no-testfences indocs/src/plugins/{overview,creating-plugins,hooks-and-events,native-extensions,appstore-review}.md(~25 total) can be converted to verified{{#include}}extracts using the anchor pattern (see e.g.docs/examples/ui/styling/snippets.ts)._expected/stdout.Pointers
crates/perry-runtime/src/plugin.rscrates/perry-codegen/src/lower_call.rsUiSig { method: "isDarkMode"method: "show", runtime: "perry_ui_window_show"lower_call.rs:2849-2854types/perry/plugin/index.d.tsif it doesn't already exist.Estimated size
Substantial — ~150-300 lines of compiler code plus the matching type declarations and at least one runnable plugin example. Multi-day work historically.