Skip to content

Commit 95e07ec

Browse files
sirtimidclaude
andcommitted
fix(kernel-browser-runtime): address UI vat infrastructure issues
- Create logger once at construction time in makeUIVatWorker instead of per method call - Track in-progress launches to prevent concurrent launches with same ID from orphaning iframes - Clean up iframe from DOM when connection establishment fails Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ad9e8d4 commit 95e07ec

2 files changed

Lines changed: 19 additions & 8 deletions

File tree

packages/kernel-browser-runtime/src/ui/UIOrchestrator.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ export class UIOrchestrator {
9292

9393
readonly #uiVats: Map<UiVatId, UiVatState> = new Map();
9494

95+
readonly #launchesInProgress: Set<UiVatId> = new Set();
96+
9597
/**
9698
* Creates a new UIOrchestrator.
9799
*
@@ -128,7 +130,7 @@ export class UIOrchestrator {
128130
async launch(config: UiVatConfig): Promise<MessagePort> {
129131
const { id, uri, slot, title, visible = true } = config;
130132

131-
if (this.#uiVats.has(id)) {
133+
if (this.#uiVats.has(id) || this.#launchesInProgress.has(id)) {
132134
throw new Error(`UI vat "${id}" already exists`);
133135
}
134136

@@ -137,13 +139,23 @@ export class UIOrchestrator {
137139
throw new Error(`Slot "${slot}" not found`);
138140
}
139141

142+
this.#launchesInProgress.add(id);
143+
140144
this.#logger.info(`Launching UI vat: ${id} in slot: ${slot}`);
141145

142146
const iframe = this.#createIframe(id, uri, title, visible);
143147
slotElement.appendChild(iframe);
144148

145-
// Wait for iframe to load and establish MessageChannel
146-
const port = await this.#establishConnection(iframe);
149+
let port: MessagePort;
150+
try {
151+
// Wait for iframe to load and establish MessageChannel
152+
port = await this.#establishConnection(iframe);
153+
} catch (error) {
154+
// Clean up iframe if connection establishment fails
155+
iframe.remove();
156+
this.#launchesInProgress.delete(id);
157+
throw error;
158+
}
147159

148160
const state: UiVatState = {
149161
config,
@@ -153,6 +165,7 @@ export class UIOrchestrator {
153165
visible,
154166
};
155167
this.#uiVats.set(id, state);
168+
this.#launchesInProgress.delete(id);
156169

157170
this.#logger.info(`UI vat "${id}" launched successfully in slot: ${slot}`);
158171
return port;

packages/kernel-browser-runtime/src/ui/makeUIVatWorker.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export const makeUIVatWorker = ({
6565
visible = true,
6666
logger,
6767
}: MakeUIVatWorkerOptions): VatWorker => {
68+
const workerLogger = logger ?? new Logger('makeUIVatWorker');
69+
6870
return {
6971
launch: async (_vatConfig: VatConfig): Promise<[MessagePort, unknown]> => {
7072
const port = await orchestrator.launch({
@@ -83,12 +85,8 @@ export const makeUIVatWorker = ({
8385
terminate: async (): Promise<null> => {
8486
if (orchestrator.has(id)) {
8587
orchestrator.terminate(id);
86-
} else if (logger) {
87-
logger.warn(`UI vat "${id}" not found for termination`);
8888
} else {
89-
new Logger('makeUIVatWorker').warn(
90-
`UI vat "${id}" not found for termination`,
91-
);
89+
workerLogger.warn(`UI vat "${id}" not found for termination`);
9290
}
9391
return null;
9492
},

0 commit comments

Comments
 (0)