Skip to main content
The host-only registry API (loadPlugin / getContributions) and the test-only reset.

PluginContributionsSnapshot

What usePluginContributions returns: the load status and the registry. Properties Related: PluginContributions

PluginLoaderState

The plugin load pass’s public state, mirrored for feature pages to read. It is the shape the app shell’s loader store already exposes: status gates when the contributions are complete, and the error fields carry the loud failures. Properties

__resetContributions

Tests reset the module-level registry between cases.

__resetPluginHostState

Tests reset the module-level host state between cases.

deferred

A promise whose settlement the test controls, returned alongside its own resolve/reject. It pins a component’s loading state for assertion: hand the unsettled promise to the code under test (e.g. as a mocked api call’s return value), assert the in-flight UI while it hangs, then call resolve/reject to drive the success or failure branch and assert the settled UI. Each call returns a fresh, independent deferred.

getContributions

The shell reads this after loading every installed plugin bundle. Related: PluginContributions

getPluginHostState

The current mirrored host state — the snapshot usePluginContributions reads. Related: PluginLoaderState

installJsdomStubs

jsdom omits a handful of browser APIs the Studio components reach for while a test drives them. Installing every stub in one place keeps each feature’s test-setup.ts identical and lets interaction tests exercise the real components instead of mocking them out:
  • Radix primitives observe element size (ResizeObserver), scroll the active item into view, and capture the pointer while opening.
  • Export/download paths create and revoke object URLs.
  • File-import flows read the picked file via Blob.text().
  • Router scroll restoration calls scrollTo on navigation.
Each stub only fills a MISSING API, so installing the whole set everywhere is harmless — the one exception is window.scrollTo, which jsdom ships as a “Not implemented” stub that logs loudly on every navigation, so it is replaced unconditionally. Call once from a package’s Vitest setupFiles entry.

loadPlugin

Load one plugin: call its register entry with a context bound to pluginId, then commit everything it staged into the global registry. Contributions are STAGED into local arrays (identity captured in the closure, never read from ambient state) and only committed after entry settles successfully. entry is AWAITED before commit, so a synchronous or an async entry is fully done first: a post-await throw skips the commit (atomicity holds for async too) and a post-await registration cannot slip past the batch. The seal is set after entry has SETTLED — for a sync entry, after it returns and the await continuation runs; for an async entry, after its returned promise resolves. A registration attempted from that point on (a deferred timer, a callback scheduled after settle) throws loudly instead of pushing into an orphaned staged array that never commits. Registrations made during entry’s synchronous body — including a microtask it enqueues then, which runs before the await continuation — are still captured and committed with the batch. A register that throws commits nothing and leaves the registry untouched. Duplicate guards run against BOTH the staged contributions and what is already committed, and fail loudly. Parameters Related: PluginEntry

setPluginHostState

The host loader pushes every load-pass transition here (it stays the single driver); every subscriber is notified so feature pages re-render off it. Props Related: PluginLoaderState

subscribePluginHost

Subscribe to host-state transitions; returns the unsubscribe function useSyncExternalStore requires so a subscriber detaches on unmount. Parameters

usePluginContributions

Subscribe to the plugin host state and read the committed contributions. The status drives re-renders as the load pass advances; contributions is only meaningful once status === 'ready' (before then the pass has committed nothing, so callers render their non-plugin content). Related: PluginContributionsSnapshot