feat: expose PWA, app identity, and extension directory keys in JSON config #312

This commit is contained in:
Linus Rath
2026-05-21 23:35:58 +02:00
parent 9b22ef810e
commit fc5f6f43d6
58 changed files with 159 additions and 130 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
// a bundle the loader verifies the signature; mismatch refuses the load.
//
// User-installed plugins (uploaded via the file picker, no server hop) have
// no signature verification is skipped for those, since the user is
// no signature - verification is skipped for those, since the user is
// installing their own code. Verification kicks in for server-managed
// bundles only (the `managed: true` flag on `InstalledPlugin`).
+2 -2
View File
@@ -28,7 +28,7 @@ const PERM_PER_METHOD: Record<string, Permission | null> = {
'admin.getAllConfig': 'admin:config',
'admin.setConfig': 'admin:config',
'admin.deleteConfig': 'admin:config',
// ui any plugin can ask the host to render a modal or open a URL.
// ui - any plugin can ask the host to render a modal or open a URL.
'ui.confirm': null,
'ui.alert': null,
'ui.openExternalUrl': null,
@@ -289,7 +289,7 @@ export async function dispatchApiCall(
}
case 'ui.openExternalUrl': {
const url = String(args[0] ?? '');
// Only http(s) the sandbox should not be able to navigate the host
// Only http(s) - the sandbox should not be able to navigate the host
// anywhere internal, nor open javascript:/data:/file: schemes.
let parsed: URL;
try { parsed = new URL(url); } catch { throw new Error('ui.openExternalUrl: invalid URL'); }
+3 -3
View File
@@ -39,7 +39,7 @@ function encodeCallbacks(
if (Array.isArray(value)) {
return value.map((v) => encodeCallbacks(v, table, depth + 1));
}
// Plain object copy own enumerable keys.
// Plain object - copy own enumerable keys.
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = encodeCallbacks(v, table, depth + 1);
@@ -161,7 +161,7 @@ export class SandboxInstance {
private send(msg: HostToSandbox): void {
// targetOrigin '*' is required because the iframe is opaque-origin. The
// payload contains no host secrets bundle code and manifest fields the
// payload contains no host secrets - bundle code and manifest fields the
// plugin already owns.
this.iframe.contentWindow?.postMessage(msg, '*');
}
@@ -236,7 +236,7 @@ export class SandboxInstance {
}
case 'slot-resize':
// The iframe has no intrinsic height sync it to the content height
// The iframe has no intrinsic height - sync it to the content height
// the sandbox reported, otherwise the wrapper reserves space but the
// iframe stays at 0px and the slot appears blank.
this.iframe.style.height = `${msg.height}px`;
+2 -2
View File
@@ -181,7 +181,7 @@ function buildPluginApi(manifest: PluginManifest) {
/**
* Resolve a bundler-emitted `require(name)` call inside the sandbox. Plugin
* bundlers should be configured to externalise React; the runtime provides
* those modules here. Anything else is refused the sandbox has no Node-
* those modules here. Anything else is refused - the sandbox has no Node-
* compatible module resolution and we don't want plugins probing globals.
*
* The host injects the per-plugin API as `@plugin-host`, so plugin code can
@@ -337,7 +337,7 @@ function bootSlot(payload: SlotInit): void {
sendToHost({ type: 'init-done', hooks: [], slots: [], shortcuts: [] });
}
// Populated by bootSlot receives `props-update` messages.
// Populated by bootSlot - receives `props-update` messages.
let slotPropsUpdater: ((next: Record<string, unknown>) => void) | null = null;
async function handleInit(payload: InitPayload): Promise<void> {