experiments in a post-browser web
10
fork

Configure Feed

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

refactor(pubsub): rename ext:* startup topics to feature:*

Coordinated rename of two startup pubsub topics to align with the
tile/feature vocabulary established in the previous commit:

ext:startup:phase -> feature:startup:phase
ext:all-loaded -> feature:all-loaded

All 4 publishes of feature:startup:phase + the single feature:all-loaded
publish in backend/electron/main.ts updated alongside every subscriber
in the same commit so the wire stays consistent end-to-end:

- app/index.js (subscribe + comments)
- app/cmd/background.js (subscribe + log line)
- app/settings/settings.js (subscribe + comments)
- tests/desktop/startup-events.spec.ts (subscribe + test names)

No tile manifest declares either topic in its pubsub.topics allowlist
(grep features/*/manifest.json), so no manifest changes are required.

backend/tauri/src-tauri/src/lib.rs still emits the old topic names
("pubsub:ext:startup:phase", "pubsub:ext:all-loaded") — Tauri/mobile is
out of scope for this rename and will be reconciled separately.

yarn build clean. yarn test:unit 1689 + 588 / 0 fail.

+20 -20
+3 -3
app/cmd/background.js
··· 514 514 515 515 // 1b. Load cached commands if versions match 516 516 // This pre-populates the registry so panel can open immediately 517 - // Note: full version validation (isCacheValid) runs at ext:all-loaded when all 517 + // Note: full version validation (isCacheValid) runs at feature:all-loaded when all 518 518 // extension versions are available. Here we do a quick app-version check to 519 519 // reject clearly stale caches without blocking startup. 520 520 const cache = await loadCommandCache(); ··· 584 584 }); 585 585 586 586 // 4b. Save command cache after all extensions have loaded 587 - api.pubsub.subscribe('ext:all-loaded', async () => { 588 - log('ext:cmd', 'ext:all-loaded - saving command cache'); 587 + api.pubsub.subscribe('feature:all-loaded', async () => { 588 + log('ext:cmd', 'feature:all-loaded - saving command cache'); 589 589 // Small delay to ensure all commands are registered 590 590 setTimeout(async () => { 591 591 // Purge stale cached commands that were NOT re-registered by live extensions
+4 -4
app/index.js
··· 606 606 await initPage(); 607 607 // await initHud(); // disabled — see import comment above 608 608 609 - // Register ext:all-loaded subscriber BEFORE publishing topicCorePrefs. 609 + // Register feature:all-loaded subscriber BEFORE publishing topicCorePrefs. 610 610 // Main subscribes to topicCorePrefs and kicks off loadFeatures() which 611 - // publishes ext:all-loaded. Because loadFeatures runs in parallel with 611 + // publishes feature:all-loaded. Because loadFeatures runs in parallel with 612 612 // the rest of this init(), subscribing later in init() would race and 613 613 // miss the publish — the renderer's subscriber would be registered 614 614 // after the IPC message had already been sent and dropped. 615 - api.subscribe('ext:all-loaded', () => { 615 + api.subscribe('feature:all-loaded', () => { 616 616 try { 617 617 registerExtensionCommands(); 618 618 } catch (err) { ··· 836 836 // Features (tiles) are loaded by the main process feature pipeline. 837 837 // The main-process startup (entry.ts -> loadFeatures()) runs the v2 838 838 // tile pipeline (tile-compat / tile-loader); core background just 839 - // initializes in-process features and waits for `ext:all-loaded`. 839 + // initializes in-process features and waits for `feature:all-loaded`. 840 840 log('core', 'Core features initialized. Tiles loaded by main process.'); 841 841 842 842 };
+3 -3
app/settings/settings.js
··· 2244 2244 // Initial load (may be incomplete if extensions still loading) 2245 2245 await refreshExtensionsList(); 2246 2246 2247 - // Store refresh function for external access (ext:all-loaded is subscribed once in main init) 2247 + // Store refresh function for external access (feature:all-loaded is subscribed once in main init) 2248 2248 window._refreshExtensionsList = refreshExtensionsList; 2249 2249 2250 2250 return container; ··· 3273 3273 // No separate nav items or panes needed. 3274 3274 3275 3275 // Listen for all extensions loaded event to refresh the Extensions list 3276 - // NOTE: Only ONE ext:all-loaded subscription per source - pubsub overwrites duplicates 3277 - api.subscribe('ext:all-loaded', () => { 3276 + // NOTE: Only ONE feature:all-loaded subscription per source - pubsub overwrites duplicates 3277 + api.subscribe('feature:all-loaded', () => { 3278 3278 // Refresh the Extensions list if it's been rendered 3279 3279 if (window._refreshExtensionsList) { 3280 3280 window._refreshExtensionsList();
+5 -5
backend/electron/main.ts
··· 765 765 } 766 766 767 767 // Phase 1: Early 768 - publish('system', 'ext:startup:phase', { phase: 'early' }); 768 + publish('system', 'feature:startup:phase', { phase: 'early' }); 769 769 770 770 // Phase 2: Commands 771 - publish('system', 'ext:startup:phase', { phase: 'commands' }); 771 + publish('system', 'feature:startup:phase', { phase: 'commands' }); 772 772 773 773 // Lazy command stubs and lazy event interceptors live in `tile-lazy.ts` — 774 774 // `registerLazyTile()` (called from tile-compat) publishes ··· 780 780 DEBUG && console.log(`[feature:timing] startup total: ${Date.now() - featStart}ms`); 781 781 782 782 // Phase 3: UI 783 - publish('system', 'ext:startup:phase', { phase: 'ui' }); 783 + publish('system', 'feature:startup:phase', { phase: 'ui' }); 784 784 785 785 // Phase 4: Complete 786 - publish('system', 'ext:startup:phase', { phase: 'complete' }); 787 - publish('system', 'ext:all-loaded', { count: 0 }); 786 + publish('system', 'feature:startup:phase', { phase: 'complete' }); 787 + publish('system', 'feature:all-loaded', { count: 0 }); 788 788 789 789 return 0; 790 790 }
+5 -5
tests/desktop/startup-events.spec.ts
··· 17 17 if (app) await app.close(); 18 18 }); 19 19 20 - test('ext:startup:phase events are available for subscription', async () => { 20 + test('feature:startup:phase events are available for subscription', async () => { 21 21 // Test that extensions can subscribe to startup phase events 22 22 // Since app is already started, we test that the subscription mechanism works 23 23 const result = await bgWindow.evaluate(async () => { ··· 25 25 let received = false; 26 26 27 27 // Subscribe to startup phase events 28 - api.subscribe('ext:startup:phase', (msg: any) => { 28 + api.subscribe('feature:startup:phase', (msg: any) => { 29 29 received = true; 30 30 }); 31 31 ··· 36 36 expect(result.subscriptionCreated).toBe(true); 37 37 }); 38 38 39 - test('ext:all-loaded event was published during startup', async () => { 40 - // Verify that the ext:all-loaded event was published by checking extensions are running 39 + test('feature:all-loaded event was published during startup', async () => { 40 + // Verify that the feature:all-loaded event was published by checking extensions are running 41 41 const result = await bgWindow.evaluate(async () => { 42 42 const api = (window as any).app; 43 43 44 - // Get running extensions - if they're running, ext:all-loaded was published 44 + // Get running extensions - if they're running, feature:all-loaded was published 45 45 const extResult = await api.extensions.list(); 46 46 const extensions = extResult.data || []; 47 47 return {