Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions apps/ui/src/boot-mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { bootConnection } from "./boot";
import { bootBrowser } from "./boot-browser";
import { type BootModeDeps, bootForMode, defaultDeps } from "./boot-mode";
import { bootNativeClient } from "./boot-native";
import { bootSetup } from "./boot-setup";
import type { ConnectionProvider, ResolvedConnection } from "./live/provider";

const CONNECTION: ResolvedConnection = {
Expand All @@ -18,24 +19,32 @@ function provider(connection: ResolvedConnection): ConnectionProvider {

describe("bootForMode", () => {
let root: HTMLElement;
let setupCalls: number;
let clientCalls: number;
let embeddedFactoryCalls: number;
let browserCalls: number;
let connectionBootCalls: number;
let quitCalls: number;
let deps: BootModeDeps;

beforeEach(() => {
root = document.createElement("div");
setupCalls = 0;
clientCalls = 0;
embeddedFactoryCalls = 0;
browserCalls = 0;
connectionBootCalls = 0;
quitCalls = 0;
deps = {
bootNativeClient: async (receivedRoot) => {
expect(receivedRoot).toBe(root);
clientCalls++;
return CONNECTION;
},
bootSetup: async (receivedRoot) => {
expect(receivedRoot).toBe(root);
setupCalls++;
return CONNECTION;
},
embeddedConnectionProvider: () => {
embeddedFactoryCalls++;
return provider(CONNECTION);
Expand All @@ -50,6 +59,9 @@ describe("bootForMode", () => {
connectionBootCalls++;
return resolve();
},
quitApp: async () => {
quitCalls++;
},
};
});

Expand Down Expand Up @@ -83,22 +95,43 @@ describe("bootForMode", () => {
expect(clientCalls).toBe(0);
expect(connectionBootCalls).toBe(0);
});

test("setup routes to bootSetup", async () => {
const connection = await bootForMode("setup", root, deps)();

expect(connection).toBe(CONNECTION);
expect(setupCalls).toBe(1);
expect(clientCalls).toBe(0);
});

test("reopen renders the shared neutral screen and never boots a connection", async () => {
await bootForMode("reopen", root, deps)();

expect(root.textContent).toContain(
"Compass is already set up. Quit and reopen it to change this.",
);
const button = root.querySelector("button");
if (!(button instanceof HTMLButtonElement))
throw new Error("Quit button is missing");
button.click();
expect(quitCalls).toBe(1);
expect(clientCalls).toBe(0);
expect(browserCalls).toBe(0);
expect(connectionBootCalls).toBe(0);
});
});

describe("defaultDeps production wiring", () => {
test("binds the real boot functions", () => {
expect(defaultDeps.bootNativeClient).toBe(bootNativeClient);
expect(defaultDeps.bootSetup).toBe(bootSetup);
expect(defaultDeps.bootConnection).toBe(bootConnection);
expect(defaultDeps.bootBrowser).toBe(bootBrowser);
});

test("embedded provider is the bridge provider (fetchImpl set, no bearer), NOT the env provider", async () => {
const resolved = await defaultDeps.embeddedConnectionProvider().resolve();
// DL-111 ambient-admin: no bearer crosses the IPC seam.
expect(resolved.token).toBeUndefined();
// The IPC tunnel fetch — defined for nativeConnectionProvider, undefined
// for envConnectionProvider; this is what discriminates the two, so a
// revert of embedded→env would fail here.
expect(resolved.fetchImpl).toBeDefined();
});
});
29 changes: 19 additions & 10 deletions apps/ui/src/boot-mode.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,37 @@
import { bootConnection } from "./boot";
import { bootBrowser } from "./boot-browser";
import { bootNativeClient } from "./boot-native";
import { nativeConnectionProvider } from "./daemon-transport";
import { bootSetup, renderReopenScreen } from "./boot-setup";
import { nativeConnectionProvider, quitApp } from "./daemon-transport";
import type { ConnectionProvider, ResolvedConnection } from "./live/provider";
import { type ShellMode, shellServerUrl } from "./shell-globals";

/** The launch mode `bootForMode` dispatches on: the shell-injected `ShellMode`,
* or undefined in a browser dev build where no shell sets it. */
export type BootMode = ShellMode | undefined;

export type BootModeDeps = {
bootNativeClient: (
root: HTMLElement,
) => Promise<ResolvedConnection | undefined>;
bootSetup: (root: HTMLElement) => Promise<ResolvedConnection | undefined>;
embeddedConnectionProvider: () => ConnectionProvider;
bootBrowser: (root: HTMLElement) => Promise<ResolvedConnection | undefined>;
bootConnection: (
root: HTMLElement,
resolve: () => Promise<ResolvedConnection>,
) => Promise<ResolvedConnection | undefined>;
quitApp: () => Promise<void>;
};

export const defaultDeps: BootModeDeps = {
bootNativeClient,
// Embedded never receives __COMPASS_SERVER_URL__ (injected in client mode only), and the
// bridge fetch routes over Wails IPC by path — so this is a syntactic same-origin
// placeholder, never dialed. Must be ABSOLUTE: createDaemonFetch does `new Request(url)`,
// which rejects a relative URL. Matches the packages/compass-client convention.
bootSetup,
// The daemon fetch constructs Requests from this base, so it must be absolute.
embeddedConnectionProvider: () =>
nativeConnectionProvider(shellServerUrl() ?? "http://compass.localhost"),
bootBrowser,
bootConnection,
quitApp,
};

/** Select the runtime boot thunk for the shell-injected launch mode. */
export function bootForMode(
mode: BootMode,
root: HTMLElement,
Expand All @@ -47,7 +45,18 @@ export function bootForMode(
deps.bootConnection(root, () =>
deps.embeddedConnectionProvider().resolve(),
);
default:
case "setup":
return () => deps.bootSetup(root);
case "reopen":
return async () => {
renderReopenScreen(root, deps.quitApp);
return undefined;
};
case undefined:
return () => deps.bootBrowser(root);
default: {
const exhaustive: never = mode;
throw new Error(`Unhandled boot mode: ${exhaustive}`);
}
}
}
Loading
Loading