experiments in a post-browser web
10
fork

Configure Feed

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

feat(v1-removal): Phase 3.7f — migrate sync to strict tile:sync:* handlers

Move sync-get-config / sync-set-config / sync-status / sync-pull /
sync-push from legacy ipc.ts handlers to strict tile:sync:* handlers
in tile-ipc.ts. Behavior preserved exactly (same updateLastSyncTime
side effect, same sync:pull-completed publish on pulled > 0).

- ipc.ts: drop legacy sync-* handlers and now-unused imports
(setSyncConfig, pullFromServer, pushToServer, getSyncStatus,
updateLastSyncTime)
- tile-ipc.ts: import sync.js + profiles.js helpers, add
tile:sync:get-config / set-config / status / pull / push behind
trustedBuiltin grant
- tile-preload.cts: api.sync.{getConfig,setConfig,getStatus,pull,push}
now invoke strict channels with token

Third of 5 namespace migrations in Phase 3.7f. Build green.

+143 -102
+3 -91
backend/electron/ipc.ts
··· 46 46 enableSync, 47 47 disableSync, 48 48 getSyncConfig as getProfileSyncConfig, 49 - updateLastSyncTime, 50 49 } from './profiles.js'; 51 50 52 51 import { ··· 112 111 113 112 import { 114 113 getSyncConfig, 115 - setSyncConfig, 116 - pullFromServer, 117 - pushToServer, 118 114 syncAll, 119 - getSyncStatus, 120 115 } from './sync.js'; 121 116 122 117 import { ··· 2593 2588 } 2594 2589 }); 2595 2590 2596 - // Get sync configuration 2597 - ipcMain.handle('sync-get-config', async () => { 2598 - try { 2599 - const config = getSyncConfig(); 2600 - return { success: true, data: config }; 2601 - } catch (error) { 2602 - const message = error instanceof Error ? error.message : String(error); 2603 - return { success: false, error: message }; 2604 - } 2605 - }); 2606 - 2607 - // Set sync configuration 2608 - ipcMain.handle('sync-set-config', async (_ev, data) => { 2609 - try { 2610 - setSyncConfig(data); 2611 - return { success: true }; 2612 - } catch (error) { 2613 - const message = error instanceof Error ? error.message : String(error); 2614 - return { success: false, error: message }; 2615 - } 2616 - }); 2617 - 2618 - // Pull items from server 2619 - ipcMain.handle('sync-pull', async (_ev, data = {}) => { 2620 - try { 2621 - const config = getSyncConfig(); 2622 - if (!config.serverUrl || !config.apiKey) { 2623 - return { success: false, error: 'Sync not configured. Set serverUrl and apiKey first.' }; 2624 - } 2625 - 2626 - const since = data.since !== undefined ? data.since : config.lastSyncTime; 2627 - const syncTime = Date.now(); 2628 - const result = await pullFromServer(config.serverUrl, config.apiKey, since); 2629 - 2630 - // Update lastSyncTime after successful pull 2631 - const activeProfile = getActiveProfile(); 2632 - updateLastSyncTime(activeProfile.id, syncTime); 2633 - 2634 - // Notify all windows that sync pulled new data so they can refresh 2635 - if (result.pulled > 0) { 2636 - publish('system', 'sync:pull-completed', { 2637 - pulled: result.pulled, 2638 - conflicts: result.conflicts 2639 - }); 2640 - } 2641 - 2642 - return { success: true, data: result }; 2643 - } catch (error) { 2644 - const message = error instanceof Error ? error.message : String(error); 2645 - return { success: false, error: message }; 2646 - } 2647 - }); 2648 - 2649 - // Push items to server 2650 - ipcMain.handle('sync-push', async (_ev, data = {}) => { 2651 - try { 2652 - const config = getSyncConfig(); 2653 - if (!config.serverUrl || !config.apiKey) { 2654 - return { success: false, error: 'Sync not configured. Set serverUrl and apiKey first.' }; 2655 - } 2656 - 2657 - const since = data.since !== undefined ? data.since : config.lastSyncTime; 2658 - const syncTime = Date.now(); 2659 - const result = await pushToServer(config.serverUrl, config.apiKey, since); 2660 - 2661 - // Update lastSyncTime after successful push 2662 - const activeProfile = getActiveProfile(); 2663 - updateLastSyncTime(activeProfile.id, syncTime); 2664 - 2665 - return { success: true, data: result }; 2666 - } catch (error) { 2667 - const message = error instanceof Error ? error.message : String(error); 2668 - return { success: false, error: message }; 2669 - } 2670 - }); 2671 - 2672 - // Get sync status 2673 - ipcMain.handle('sync-status', async () => { 2674 - try { 2675 - const status = getSyncStatus(); 2676 - return { success: true, data: status }; 2677 - } catch (error) { 2678 - const message = error instanceof Error ? error.message : String(error); 2679 - return { success: false, error: message }; 2680 - } 2681 - }); 2591 + // sync-get-config / sync-set-config / sync-pull / sync-push / sync-status 2592 + // were here. Strict counterparts live in tile-ipc.ts (Phase 3.7f) as 2593 + // tile:sync:get-config / set-config / pull / push / status. 2682 2594 } 2683 2595 2684 2596 /**
+132 -1
backend/electron/tile-ipc.ts
··· 109 109 import { checkOAuthAllowed } from './tile-oauth-enforcement.js'; 110 110 import { checkSyncAllowed } from './tile-sync-enforcement.js'; 111 111 import { createLoopbackServer } from './oauth-loopback.js'; 112 - import { syncAll, getSyncConfig } from './sync.js'; 112 + import { syncAll, getSyncConfig, setSyncConfig, getSyncStatus, pullFromServer, pushToServer } from './sync.js'; 113 + import { getActiveProfile, updateLastSyncTime } from './profiles.js'; 113 114 import { checkEscapeAllowed } from './tile-escape-enforcement.js'; 114 115 import { checkSessionAllowed } from './tile-session-enforcement.js'; 115 116 import { checkIzuiAllowed } from './tile-izui-enforcement.js'; ··· 4292 4293 conflicts: result.conflicts, 4293 4294 }); 4294 4295 } 4296 + 4297 + return { success: true, data: result }; 4298 + } catch (error) { 4299 + const message = error instanceof Error ? error.message : String(error); 4300 + return { success: false, error: message }; 4301 + } 4302 + }); 4303 + 4304 + // ── tile:sync:get-config / set-config / status / pull / push (Phase 3.7f) ── 4305 + // 4306 + // Strict counterparts of the legacy `sync-get-config`, `sync-set-config`, 4307 + // `sync-status`, `sync-pull`, `sync-push` channels in ipc.ts (deleted in 4308 + // Phase 3.7f). Settings UI consumes these via api.sync.{getConfig,setConfig, 4309 + // getStatus,pull,push}. trustedBuiltin only — sync touches network, server 4310 + // creds, and profile state. 4311 + 4312 + registerTileIpc('tile:sync:get-config', { mode: 'handle' }, (event, args: { 4313 + token: string; 4314 + }, _grant) => { 4315 + if (!args?.token) return { success: false, error: 'Invalid token' }; 4316 + const grant = _grant; 4317 + if (!grant.trustedBuiltin) { 4318 + handleViolation(grant, 'sync', 'tile:sync:get-config', 'trustedBuiltin required', args.token); 4319 + return { success: false, error: 'trustedBuiltin required for tile:sync:get-config' }; 4320 + } 4321 + try { 4322 + const config = getSyncConfig(); 4323 + return { success: true, data: config }; 4324 + } catch (error) { 4325 + const message = error instanceof Error ? error.message : String(error); 4326 + return { success: false, error: message }; 4327 + } 4328 + }); 4329 + 4330 + registerTileIpc('tile:sync:set-config', { mode: 'handle' }, (event, args: { 4331 + token: string; 4332 + config: unknown; 4333 + }, _grant) => { 4334 + if (!args?.token) return { success: false, error: 'Invalid token' }; 4335 + const grant = _grant; 4336 + if (!grant.trustedBuiltin) { 4337 + handleViolation(grant, 'sync', 'tile:sync:set-config', 'trustedBuiltin required', args.token); 4338 + return { success: false, error: 'trustedBuiltin required for tile:sync:set-config' }; 4339 + } 4340 + try { 4341 + setSyncConfig(args.config as Parameters<typeof setSyncConfig>[0]); 4342 + return { success: true }; 4343 + } catch (error) { 4344 + const message = error instanceof Error ? error.message : String(error); 4345 + return { success: false, error: message }; 4346 + } 4347 + }); 4348 + 4349 + registerTileIpc('tile:sync:status', { mode: 'handle' }, (event, args: { 4350 + token: string; 4351 + }, _grant) => { 4352 + if (!args?.token) return { success: false, error: 'Invalid token' }; 4353 + const grant = _grant; 4354 + if (!grant.trustedBuiltin) { 4355 + handleViolation(grant, 'sync', 'tile:sync:status', 'trustedBuiltin required', args.token); 4356 + return { success: false, error: 'trustedBuiltin required for tile:sync:status' }; 4357 + } 4358 + try { 4359 + const status = getSyncStatus(); 4360 + return { success: true, data: status }; 4361 + } catch (error) { 4362 + const message = error instanceof Error ? error.message : String(error); 4363 + return { success: false, error: message }; 4364 + } 4365 + }); 4366 + 4367 + registerTileIpc('tile:sync:pull', { mode: 'handle' }, async (event, args: { 4368 + token: string; 4369 + since?: number; 4370 + }, _grant) => { 4371 + if (!args?.token) return { success: false, error: 'Invalid token' }; 4372 + const grant = _grant; 4373 + if (!grant.trustedBuiltin) { 4374 + handleViolation(grant, 'sync', 'tile:sync:pull', 'trustedBuiltin required', args.token); 4375 + return { success: false, error: 'trustedBuiltin required for tile:sync:pull' }; 4376 + } 4377 + try { 4378 + const config = getSyncConfig(); 4379 + if (!config.serverUrl || !config.apiKey) { 4380 + return { success: false, error: 'Sync not configured. Set serverUrl and apiKey first.' }; 4381 + } 4382 + 4383 + const since = args.since !== undefined ? args.since : config.lastSyncTime; 4384 + const syncTime = Date.now(); 4385 + const result = await pullFromServer(config.serverUrl, config.apiKey, since); 4386 + 4387 + const activeProfile = getActiveProfile(); 4388 + updateLastSyncTime(activeProfile.id, syncTime); 4389 + 4390 + if (result.pulled > 0) { 4391 + publish('system', 'sync:pull-completed', { 4392 + pulled: result.pulled, 4393 + conflicts: result.conflicts, 4394 + }); 4395 + } 4396 + 4397 + return { success: true, data: result }; 4398 + } catch (error) { 4399 + const message = error instanceof Error ? error.message : String(error); 4400 + return { success: false, error: message }; 4401 + } 4402 + }); 4403 + 4404 + registerTileIpc('tile:sync:push', { mode: 'handle' }, async (event, args: { 4405 + token: string; 4406 + since?: number; 4407 + }, _grant) => { 4408 + if (!args?.token) return { success: false, error: 'Invalid token' }; 4409 + const grant = _grant; 4410 + if (!grant.trustedBuiltin) { 4411 + handleViolation(grant, 'sync', 'tile:sync:push', 'trustedBuiltin required', args.token); 4412 + return { success: false, error: 'trustedBuiltin required for tile:sync:push' }; 4413 + } 4414 + try { 4415 + const config = getSyncConfig(); 4416 + if (!config.serverUrl || !config.apiKey) { 4417 + return { success: false, error: 'Sync not configured. Set serverUrl and apiKey first.' }; 4418 + } 4419 + 4420 + const since = args.since !== undefined ? args.since : config.lastSyncTime; 4421 + const syncTime = Date.now(); 4422 + const result = await pushToServer(config.serverUrl, config.apiKey, since); 4423 + 4424 + const activeProfile = getActiveProfile(); 4425 + updateLastSyncTime(activeProfile.id, syncTime); 4295 4426 4296 4427 return { success: true, data: result }; 4297 4428 } catch (error) {
+8 -10
backend/electron/tile-preload.cts
··· 1853 1853 } 1854 1854 return ipcRenderer.invoke('tile:sync:sync-all', { token: tileToken }); 1855 1855 }, 1856 - // Thin wrappers over the existing legacy `sync-*` ipcMain handlers 1857 - // (ipc.ts:3624+). Settings uses these to show current config + live 1858 - // status. Not capability-gated — Settings has trustedBuiltin grant 1859 - // and these are read-only status/config reads + user-initiated 1860 - // push/pull. Matches the `api.profiles` pattern from 58ef736e. 1861 - getConfig: () => ipcRenderer.invoke('sync-get-config'), 1862 - setConfig: (config: unknown) => ipcRenderer.invoke('sync-set-config', config), 1863 - getStatus: () => ipcRenderer.invoke('sync-status'), 1864 - pull: (options?: unknown) => ipcRenderer.invoke('sync-pull', options ?? {}), 1865 - push: (options?: unknown) => ipcRenderer.invoke('sync-push', options ?? {}), 1856 + // Strict tile:sync:* counterparts of the legacy `sync-*` handlers 1857 + // live in tile-ipc.ts (Phase 3.7f). trustedBuiltin only — sync 1858 + // touches network, server creds, and profile state. 1859 + getConfig: () => ipcRenderer.invoke('tile:sync:get-config', { token: tileToken }), 1860 + setConfig: (config: unknown) => ipcRenderer.invoke('tile:sync:set-config', { token: tileToken, config }), 1861 + getStatus: () => ipcRenderer.invoke('tile:sync:status', { token: tileToken }), 1862 + pull: (options?: { since?: number }) => ipcRenderer.invoke('tile:sync:pull', { token: tileToken, since: options?.since }), 1863 + push: (options?: { since?: number }) => ipcRenderer.invoke('tile:sync:push', { token: tileToken, since: options?.since }), 1866 1864 }; 1867 1865 1868 1866 // ── Ad blocker ────────────────────────────────────────────────────