experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(tile-ipc): tile:features:settings-schema + tile:window:get-id shims (Phase 3.5e)

Add two strict IPC shims as part of the V1-removal Phase 3.5e:

tile:features:settings-schema
- Main-process handler in tile-ipc.ts gated by checkFeaturesAllowed(grant, 'read')
- Looks up the feature registry entry, reads settingsSchema from the manifest
(v2 field or raw fallback for v1), reads and parses the JSON schema file
- Returns { success, data } — data is null when no schema is declared or file absent
- Mirrors legacy `feature-settings-schema` channel semantics

tile:window:get-id
- Main-process handler in tile-ipc.ts — token validity enforced, no capability gate
(every tile has an inherent right to know its own window id)
- Returns { success, id } from BrowserWindow.fromWebContents(event.sender)
- Mirrors legacy un-gated `get-window-id` channel

tile-preload.cts additions (additive only):
- featuresStrict.settingsSchema + api.features.settingsSchema (strict path routes
through tile:features:settings-schema; v1-compat falls back to feature-settings-schema)
- windowStrict.getId (api.window.getId) — strict path routes through tile:window:get-id;
v1-compat falls back to get-window-id

No legacy handler deletions.

+85
+63
backend/electron/tile-ipc.ts
··· 2185 2185 } 2186 2186 }); 2187 2187 2188 + // ── tile:features:settings-schema ──────────────────────────────── 2189 + // 2190 + // Read the JSON settings schema file declared in a feature's manifest. 2191 + // Gated by `features` read capability — mirrors the legacy 2192 + // `feature-settings-schema` channel in ipc.ts but enforces the 2193 + // token + `features` capability before reading from disk. 2194 + // 2195 + // `args.id` — the feature/extension id whose schema to fetch. 2196 + // Returns `{ success: true, data: schema }` (data may be null when 2197 + // the feature declares no settingsSchema or the file is absent). 2198 + ipcMain.handle('tile:features:settings-schema', async (_event, args: { 2199 + token: string; 2200 + id: string; 2201 + }) => { 2202 + const grant = getGrantForToken(args.token); 2203 + const check = checkFeaturesAllowed(grant, 'read'); 2204 + if (!check.ok) { 2205 + handleViolation(grant, 'features', 'features:settings-schema', check.error, args.token); 2206 + return { success: false, error: check.error }; 2207 + } 2208 + 2209 + const registry = getFeatureRegistry(); 2210 + if (!registry) return { success: false, error: 'Feature registry not initialized' }; 2211 + 2212 + const entry = registry.getFeature(args.id); 2213 + if (!entry) return { success: false, error: `Feature not found: ${args.id}` }; 2214 + 2215 + try { 2216 + const manifest = entry.path ? parseManifestFile(path.join(entry.path, 'manifest.json')) : null; 2217 + // settingsSchema lives under v2 for tile manifests; fall back to raw for v1 extensions. 2218 + const settingsSchema: string | undefined = 2219 + manifest?.v2?.settingsSchema ?? (manifest?.raw?.settingsSchema as string | undefined); 2220 + if (!settingsSchema) return { success: true, data: null }; 2221 + 2222 + const schemaPath = path.join(entry.path, settingsSchema); 2223 + if (!fs.existsSync(schemaPath)) return { success: true, data: null }; 2224 + 2225 + const schemaContent = fs.readFileSync(schemaPath, 'utf-8'); 2226 + const schema = JSON.parse(schemaContent); 2227 + return { success: true, data: schema }; 2228 + } catch (err) { 2229 + const message = err instanceof Error ? err.message : String(err); 2230 + return { success: false, error: message }; 2231 + } 2232 + }); 2233 + 2188 2234 // ── IZUI (strict) ───────────────────────────────────────────────── 2189 2235 // 2190 2236 // Strict mirrors of the legacy `izui-*` IPC channels. The original ··· 3071 3117 openerWin.show(); 3072 3118 openerWin.focus(); 3073 3119 return { success: true }; 3120 + }); 3121 + 3122 + // ── tile:window:get-id ──────────────────────────────────────────── 3123 + // 3124 + // Return the Electron BrowserWindow id for the calling renderer. 3125 + // No `window` capability required — every tile has an inherent right 3126 + // to know its own window id (mirrors the un-gated legacy `get-window-id` 3127 + // channel). Token validity is still enforced so un-initialised callers 3128 + // cannot probe the id. 3129 + ipcMain.handle('tile:window:get-id', (_event, args: { 3130 + token: string; 3131 + }) => { 3132 + const grant = getGrantForToken(args.token); 3133 + if (!grant) return { success: false, error: 'Invalid token' }; 3134 + 3135 + const win = BrowserWindow.fromWebContents(_event.sender); 3136 + return win ? { success: true, id: win.id } : { success: false, error: 'Window not found' }; 3074 3137 }); 3075 3138 3076 3139 // ── tile:screen:get-primary-display ───────────────────────────────
+22
backend/electron/tile-preload.cts
··· 824 824 token: tileToken, 825 825 }); 826 826 }, 827 + 828 + /** 829 + * Get the Electron BrowserWindow id for the calling renderer. 830 + * 831 + * No `window` capability required — the tile's token is the 832 + * authority (a tile always knows its own window). Falls back to 833 + * the un-gated legacy `get-window-id` channel for v1-compat tiles. 834 + */ 835 + getId: () => { 836 + if (!tokenValid) return Promise.reject(new Error('Not initialized')); 837 + if (hasWindowCapability()) { 838 + return ipcRenderer.invoke('tile:window:get-id', { token: tileToken }); 839 + } 840 + return ipcRenderer.invoke('get-window-id'); 841 + }, 827 842 }; 828 843 829 844 // ── Datastore (if granted) ── ··· 1544 1559 ipcRenderer.invoke('tile:features:install', { token: tileToken, atUri, userApproved }), 1545 1560 resolvePublisher: (query: string) => 1546 1561 ipcRenderer.invoke('tile:features:browse-resolve-publisher', { token: tileToken, query }), 1562 + settingsSchema: (id: string) => 1563 + ipcRenderer.invoke('tile:features:settings-schema', { token: tileToken, id }), 1547 1564 }; 1548 1565 1549 1566 api.features = { ··· 1596 1613 return hasFeaturesCapability() 1597 1614 ? featuresStrict.resolvePublisher(query as string) 1598 1615 : featuresCompat.resolvePublisher(query as string); 1616 + }, 1617 + settingsSchema: (id: unknown) => { 1618 + return hasFeaturesCapability() 1619 + ? featuresStrict.settingsSchema(id as string) 1620 + : ipcRenderer.invoke('feature-settings-schema', { token: tileToken, id }); 1599 1621 }, 1600 1622 1601 1623 // ── Admin surfaces (strict-only) ─────────────────────────────────