Plugin runtime environment
What capture hooks and the transform may use inside the sandbox, and what does not exist there.
Package code runs in a capability-free sandbox: QuickJS compiled to WebAssembly, with an empty import table. fetch, require, process, and the Node built-ins are not merely blocked; they do not exist as symbols. Memory and wall-clock are bounded. The same sandbox ships in the server and the Desktop app, so a package behaves identically wherever it runs.
Entry points
| Module | Exports | Called with |
|---|---|---|
transform.hooks.file | normalize (optional), materializeRecords, buildView | extract items; the root record and its related records |
capture.hooks.file | the functions listed in capture.hooks.exports | the page API, limited to the declared capabilities |
Entry point names are fixed by the runtime id (fruitful-page-plugin@1); the manifest never restates them.
Always available
Bare ECMAScript: JSON, Math, Date, RegExp, String, Array, Object, Map, Set, encodeURIComponent, decodeURIComponent, parseInt, parseFloat.
Available when declared
stdlib on the hooks block links pure, pinned polyfills. Declare only what the module uses:
"hooks": { "file": "blog-plugin.js", "stdlib": ["url", "intl", "base64"] }| Module | Provides |
|---|---|
url | URL, URLSearchParams, TextEncoder, TextDecoder |
intl | Intl.NumberFormat, Intl.PluralRules, Intl.Locale, Intl.getCanonicalLocales, Number.prototype.toLocaleString |
base64 | atob, btoa, Uint8Array.fromBase64, Uint8Array.prototype.toBase64 |
The sandbox reads no ambient locale: pass one explicitly (value.toLocaleString('en-US')). To read a base64url identifier, use Uint8Array.fromBase64(value, { alphabet: 'base64url' }) and TextDecoder, never Buffer.
Not available
Buffer, require and every node: built-in, process, fetch, setTimeout and other timers, crypto, fs. The bundler builds with esbuild in browser mode and bundles everything, so a node: import fails plugin generate with "Could not resolve"; a typecheck against Node types will not catch it earlier.
Capture hook operations
A capture hook's import table is exactly the operations its declared capabilities grant:
| Capability | Operations |
|---|---|
browser.dom.read | waitForVisible(selector), count(selector) |
browser.dom.click | click(selector) |
browser.dom.type | type(selector, text); refuses password fields and credential-looking input |
Each operation is clamped by a timeout (10 s by default, 30 s at most). An operation the export did not declare is absent at link time, not denied at call time. Hooks are written against CaptureApi from workspace.packages.universal.lib.
Marshalling
Arguments and results cross the boundary as JSON values. No host objects or functions enter the guest; nothing the guest creates can hold a reference to the host. A guest that throws surfaces as a sandbox error with the message; a guest that exceeds the time limit is terminated.