// @effect-diagnostics globalTimers:off -- This protocol broker owns cancellable request deadlines outside the Effect runtime.import { DESKTOP_APP_ACTIVATION_PROTOCOL_VERSION, type DesktopAppActivationFailure, type DesktopAppActivationRequest, type DesktopAppActivationResponse,} from "@t3tools/contracts"; interface PendingActivation { readonly request: DesktopAppActivationRequest; readonly resolve: (response: DesktopAppActivationResponse) => void; readonly timeout: ReturnType<typeof setTimeout>; dispatched: boolean;} type RendererSender = (request: DesktopAppActivationRequest) => void; function failure( requestId: string, code: DesktopAppActivationFailure["code"], message: string,): DesktopAppActivationFailure { return { version: DESKTOP_APP_ACTIVATION_PROTOCOL_VERSION, requestId, ok: false, code, message, };} /** Holds CLI requests until the real desktop renderer is ready to handle them. */export class DesktopAppActivationBroker { readonly #pending = new Map<string, PendingActivation>(); readonly #requestTimeoutMs: number; readonly #activate: () => void; #renderer: RendererSender | null = null; #closed = false; constructor(input: { readonly requestTimeoutMs: number; readonly activate: () => void }) { this.#requestTimeoutMs = input.requestTimeoutMs; this.#activate = input.activate; } request(request: DesktopAppActivationRequest): Promise<DesktopAppActivationResponse> { if (this.#closed) { return Promise.resolve( failure(request.requestId, "renderer-unavailable", "T3 Code is shutting down."), ); } if (this.#pending.has(request.requestId)) { return Promise.resolve( failure(request.requestId, "invalid-request", "The request id is already in use."), ); } const response = new Promise<DesktopAppActivationResponse>((resolve) => { const timeout = setTimeout(() => { this.#settle( failure( request.requestId, "request-timeout", "The desktop app did not finish opening the project in time.", ), ); }, this.#requestTimeoutMs); this.#pending.set(request.requestId, { request, resolve, timeout, dispatched: false, }); }); this.#activate(); this.#flush(); return response; } registerRenderer(send: RendererSender): void { this.#renderer = send; this.#flush(); } clearRenderer(): void { this.#renderer = null; for (const pending of this.#pending.values()) { if (pending.dispatched) { this.#settle( failure( pending.request.requestId, "renderer-unavailable", "The T3 Code window closed before it opened the project.", ), ); } } } complete(response: DesktopAppActivationResponse): void { this.#settle(response); } cancel(requestId: string): void { this.#settle( failure(requestId, "renderer-unavailable", "The command closed before T3 Code was ready."), ); } close(): void { this.#closed = true; this.#renderer = null; for (const pending of this.#pending.values()) { this.#settle( failure(pending.request.requestId, "renderer-unavailable", "T3 Code is shutting down."), ); } } #flush(): void { const renderer = this.#renderer; if (renderer === null) return; if ([...this.#pending.values()].some((pending) => pending.dispatched)) return; for (const pending of this.#pending.values()) { if (pending.dispatched) continue; try { pending.dispatched = true; renderer(pending.request); } catch { pending.dispatched = false; this.#renderer = null; } return; } } #settle(response: DesktopAppActivationResponse): void { const pending = this.#pending.get(response.requestId); if (!pending) return; clearTimeout(pending.timeout); this.#pending.delete(response.requestId); pending.resolve(response); this.#flush(); }}
// @effect-diagnostics globalTimers:off -- This protocol broker owns cancellable request deadlines outside the Effect runtime.
import {
DESKTOP_APP_ACTIVATION_PROTOCOL_VERSION,
type DesktopAppActivationFailure,
type DesktopAppActivationRequest,
type DesktopAppActivationResponse,
} from "@t3tools/contracts";
interface PendingActivation {
readonly request: DesktopAppActivationRequest;
readonly resolve: (response: DesktopAppActivationResponse) => void;
readonly timeout: ReturnType<typeof setTimeout>;
dispatched: boolean;
}
type RendererSender = (request: DesktopAppActivationRequest) => void;
function failure(
requestId: string,
code: DesktopAppActivationFailure["code"],
message: string,
): DesktopAppActivationFailure {
return {
version: DESKTOP_APP_ACTIVATION_PROTOCOL_VERSION,
requestId,
ok: false,
code,
message,
};
}
/** Holds CLI requests until the real desktop renderer is ready to handle them. */
export class DesktopAppActivationBroker {
readonly #pending = new Map<string, PendingActivation>();
readonly #requestTimeoutMs: number;
readonly #activate: () => void;
#renderer: RendererSender | null = null;
#closed = false;
constructor(input: { readonly requestTimeoutMs: number; readonly activate: () => void }) {
this.#requestTimeoutMs = input.requestTimeoutMs;
this.#activate = input.activate;
}
request(request: DesktopAppActivationRequest): Promise<DesktopAppActivationResponse> {
if (this.#closed) {
return Promise.resolve(
failure(request.requestId, "renderer-unavailable", "T3 Code is shutting down."),
);
}
if (this.#pending.has(request.requestId)) {
return Promise.resolve(
failure(request.requestId, "invalid-request", "The request id is already in use."),
);
}
const response = new Promise<DesktopAppActivationResponse>((resolve) => {
const timeout = setTimeout(() => {
this.#settle(
failure(
request.requestId,
"request-timeout",
"The desktop app did not finish opening the project in time.",
),
);
}, this.#requestTimeoutMs);
this.#pending.set(request.requestId, {
request,
resolve,
timeout,
dispatched: false,
});
});
this.#activate();
this.#flush();
return response;
}
registerRenderer(send: RendererSender): void {
this.#renderer = send;
this.#flush();
}
clearRenderer(): void {
this.#renderer = null;
for (const pending of this.#pending.values()) {
if (pending.dispatched) {
this.#settle(
failure(
pending.request.requestId,
"renderer-unavailable",
"The T3 Code window closed before it opened the project.",
),
);
}
}
}
complete(response: DesktopAppActivationResponse): void {
this.#settle(response);
}
cancel(requestId: string): void {
this.#settle(
failure(requestId, "renderer-unavailable", "The command closed before T3 Code was ready."),
);
}
close(): void {
this.#closed = true;
this.#renderer = null;
for (const pending of this.#pending.values()) {
this.#settle(
failure(pending.request.requestId, "renderer-unavailable", "T3 Code is shutting down."),
);
}
}
#flush(): void {
const renderer = this.#renderer;
if (renderer === null) return;
if ([...this.#pending.values()].some((pending) => pending.dispatched)) return;
for (const pending of this.#pending.values()) {
if (pending.dispatched) continue;
try {
pending.dispatched = true;
renderer(pending.request);
} catch {
pending.dispatched = false;
this.#renderer = null;
}
return;
}
}
#settle(response: DesktopAppActivationResponse): void {
const pending = this.#pending.get(response.requestId);
if (!pending) return;
clearTimeout(pending.timeout);
this.#pending.delete(response.requestId);
pending.resolve(response);
this.#flush();
}
}