authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-19 21:22:04-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-19 21:22:04-07:00
log01cd564557c2e96019f7e7060869097bbe4f6805
tree48dffaa8a917b91d61cf4dda8f4c0bb2524a6590
parent4ee719a094d4c4f30fab88c1f6969483890e223e
signature Signed by PGP key B5690EEEBB952194

[codex] Align desktop preview Effect services (#3199)

Co-authored-by: codex <codex@users.noreply.github.com>

4 files changed, 143 insertions(+), 138 deletions(-)

apps/desktop/src/ipc/methods/preview.ts+1-1
...@@ -96,7 +96,7 @@ const tabMethod = (...@@ -96,7 +96,7 @@ const tabMethod = (
96 channel: string,96 channel: string,
97 name: string,97 name: string,
98 invoke: (98 invoke: (
99 manager: PreviewManager.PreviewManagerShape,99 manager: PreviewManager.PreviewManager["Service"],
100 tabId: string,100 tabId: string,
101 ) => Effect.Effect<void, PreviewManager.PreviewManagerError>,101 ) => Effect.Effect<void, PreviewManager.PreviewManagerError>,
102) =>102) =>
apps/desktop/src/main.ts+2-2
...@@ -46,7 +46,7 @@ import * as DesktopSshEnvironment from "./ssh/DesktopSshEnvironment.ts";...@@ -46,7 +46,7 @@ import * as DesktopSshEnvironment from "./ssh/DesktopSshEnvironment.ts";
46import * as DesktopSshPasswordPrompts from "./ssh/DesktopSshPasswordPrompts.ts";46import * as DesktopSshPasswordPrompts from "./ssh/DesktopSshPasswordPrompts.ts";
47import * as DesktopState from "./app/DesktopState.ts";47import * as DesktopState from "./app/DesktopState.ts";
48import * as DesktopUpdates from "./updates/DesktopUpdates.ts";48import * as DesktopUpdates from "./updates/DesktopUpdates.ts";
49import * as PreviewBrowserSession from "./preview/BrowserSession.ts";49import * as BrowserSession from "./preview/BrowserSession.ts";
50import * as PreviewManager from "./preview/Manager.ts";50import * as PreviewManager from "./preview/Manager.ts";
51import * as DesktopWindow from "./window/DesktopWindow.ts";51import * as DesktopWindow from "./window/DesktopWindow.ts";
5252
...@@ -133,7 +133,7 @@ const desktopServerExposureLayer = DesktopServerExposure.layer.pipe(...@@ -133,7 +133,7 @@ const desktopServerExposureLayer = DesktopServerExposure.layer.pipe(
133);133);
134134
135const desktopPreviewLayer = PreviewManager.layer.pipe(135const desktopPreviewLayer = PreviewManager.layer.pipe(
136 Layer.provideMerge(PreviewBrowserSession.layer),136 Layer.provideMerge(BrowserSession.layer),
137 Layer.provideMerge(desktopFoundationLayer),137 Layer.provideMerge(desktopFoundationLayer),
138);138);
139139
apps/desktop/src/preview/BrowserSession.ts+20-18
...@@ -2,36 +2,38 @@ import type { Session } from "electron";...@@ -2,36 +2,38 @@ import type { Session } from "electron";
2import { session } from "electron";2import { session } from "electron";
3import * as Context from "effect/Context";3import * as Context from "effect/Context";
4import * as Crypto from "effect/Crypto";4import * as Crypto from "effect/Crypto";
5import * as Data from "effect/Data";
6import * as Effect from "effect/Effect";5import * as Effect from "effect/Effect";
7import * as Encoding from "effect/Encoding";6import * as Encoding from "effect/Encoding";
8import * as Layer from "effect/Layer";7import * as Layer from "effect/Layer";
8import * as Schema from "effect/Schema";
9import * as SynchronizedRef from "effect/SynchronizedRef";9import * as SynchronizedRef from "effect/SynchronizedRef";
1010
11const PREVIEW_PARTITION_PREFIX = "persist:t3code-preview-";11const PREVIEW_PARTITION_PREFIX = "persist:t3code-preview-";
1212
13export class BrowserSessionError extends Data.TaggedError("BrowserSessionError")<{13export class BrowserSessionError extends Schema.TaggedErrorClass<BrowserSessionError>()(
14 readonly operation: string;14 "BrowserSessionError",
15 readonly cause: unknown;15 {
16}> {16 operation: Schema.Literals(["getPartition", "getSession", "clearCookies", "clearCache"]),
17 override get message() {17 cause: Schema.Defect(),
18 },
19) {
20 override get message(): string {
18 return `Desktop preview browser session operation failed: ${this.operation}`;21 return `Desktop preview browser session operation failed: ${this.operation}`;
19 }22 }
20}23}
2124
22export interface BrowserSessionShape {25export class BrowserSession extends Context.Service<
23 readonly getPartition: (scope?: string) => Effect.Effect<string, BrowserSessionError>;26 BrowserSession,
24 readonly isPartition: (partition: string) => boolean;27 {
25 readonly getSession: (scope?: string) => Effect.Effect<Session, BrowserSessionError>;28 readonly getPartition: (scope?: string) => Effect.Effect<string, BrowserSessionError>;
26 readonly clearCookies: () => Effect.Effect<void, BrowserSessionError>;29 readonly isPartition: (partition: string) => boolean;
27 readonly clearCache: () => Effect.Effect<void, BrowserSessionError>;30 readonly getSession: (scope?: string) => Effect.Effect<Session, BrowserSessionError>;
28}31 readonly clearCookies: () => Effect.Effect<void, BrowserSessionError>;
2932 readonly clearCache: () => Effect.Effect<void, BrowserSessionError>;
30export class BrowserSession extends Context.Service<BrowserSession, BrowserSessionShape>()(33 }
31 "@t3tools/desktop/preview/BrowserSession",34>()("@t3tools/desktop/preview/BrowserSession") {}
32) {}
3335
34const make = Effect.gen(function* BrowserSessionMake() {36export const make = Effect.gen(function* BrowserSessionMake() {
35 const crypto = yield* Crypto.Crypto;37 const crypto = yield* Crypto.Crypto;
36 const sessionsRef = yield* SynchronizedRef.make<ReadonlyMap<string, Session>>(new Map());38 const sessionsRef = yield* SynchronizedRef.make<ReadonlyMap<string, Session>>(new Map());
3739
apps/desktop/src/preview/Manager.ts+120-117
...@@ -37,7 +37,6 @@ import {...@@ -37,7 +37,6 @@ import {
37import * as Cause from "effect/Cause";37import * as Cause from "effect/Cause";
38import * as Clock from "effect/Clock";38import * as Clock from "effect/Clock";
39import * as Context from "effect/Context";39import * as Context from "effect/Context";
40import * as Data from "effect/Data";
41import * as DateTime from "effect/DateTime";40import * as DateTime from "effect/DateTime";
42import * as Effect from "effect/Effect";41import * as Effect from "effect/Effect";
43import * as Exit from "effect/Exit";42import * as Exit from "effect/Exit";
...@@ -432,15 +431,17 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -432,15 +431,17 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
432 ) {431 ) {
433 const tabs = yield* SynchronizedRef.get(tabsRef);432 const tabs = yield* SynchronizedRef.get(tabsRef);
434 const tab = tabs.get(tabId);433 const tab = tabs.get(tabId);
435 if (!tab) return yield* fail("requireWebContents", new PreviewTabNotFoundError(tabId));434 if (!tab) {
435 return yield* fail("requireWebContents", new PreviewTabNotFoundError({ tabId }));
436 }
436 if (tab.webContentsId == null) {437 if (tab.webContentsId == null) {
437 return yield* fail("requireWebContents", new PreviewWebviewNotInitializedError(tabId));438 return yield* fail("requireWebContents", new PreviewWebviewNotInitializedError({ tabId }));
438 }439 }
439 const wc = webContents.fromId(tab.webContentsId);440 const wc = webContents.fromId(tab.webContentsId);
440 if (!wc) {441 if (!wc) {
441 return yield* fail(442 return yield* fail(
442 "requireWebContents",443 "requireWebContents",
443 new PreviewWebContentsNotFoundError(tabId, tab.webContentsId),444 new PreviewWebContentsNotFoundError({ tabId, webContentsId: tab.webContentsId }),
444 );445 );
445 }446 }
446 return wc;447 return wc;
...@@ -845,7 +846,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -845,7 +846,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
845 });846 });
846 } else {847 } else {
847 const error = Option.getOrNull(Cause.findErrorOption(exit.cause));848 const error = Option.getOrNull(Cause.findErrorOption(exit.cause));
848 const underlying = error instanceof PreviewManagerError ? error.cause : error;849 const underlying = isPreviewManagerError(error) ? error.cause : error;
849 const interrupted =850 const interrupted =
850 underlying instanceof Error &&851 underlying instanceof Error &&
851 underlying.name === "PreviewAutomationControlInterruptedError";852 underlying.name === "PreviewAutomationControlInterruptedError";
...@@ -1161,7 +1162,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -1161,7 +1162,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
1161 ) {1162 ) {
1162 const tab = (yield* SynchronizedRef.get(tabsRef)).get(tabId);1163 const tab = (yield* SynchronizedRef.get(tabsRef)).get(tabId);
1163 if (!tab) {1164 if (!tab) {
1164 return yield* fail("registerWebview", new PreviewTabNotFoundError(tabId));1165 return yield* fail("registerWebview", new PreviewTabNotFoundError({ tabId }));
1165 }1166 }
1166 const wc = webContents.fromId(webContentsId);1167 const wc = webContents.fromId(webContentsId);
1167 const mainWindow = yield* Ref.get(mainWindowRef);1168 const mainWindow = yield* Ref.get(mainWindowRef);
...@@ -1172,7 +1173,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -1172,7 +1173,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
1172 ) {1173 ) {
1173 return yield* fail(1174 return yield* fail(
1174 "registerWebview",1175 "registerWebview",
1175 new PreviewWebContentsNotFoundError(tabId, webContentsId),1176 new PreviewWebContentsNotFoundError({ tabId, webContentsId }),
1176 );1177 );
1177 }1178 }
1178 const attached = yield* Ref.get(attachedRef);1179 const attached = yield* Ref.get(attachedRef);
...@@ -1224,7 +1225,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -1224,7 +1225,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
1224 ] as const;1225 ] as const;
1225 });1226 });
1226 if (Option.isNone(registration)) {1227 if (Option.isNone(registration)) {
1227 return yield* fail("registerWebview", new PreviewTabNotFoundError(tabId));1228 return yield* fail("registerWebview", new PreviewTabNotFoundError({ tabId }));
1228 }1229 }
1229 const { state: registered, pendingUrl } = registration.value;1230 const { state: registered, pendingUrl } = registration.value;
1230 yield* emit(tabId, registered);1231 yield* emit(tabId, registered);
...@@ -2200,129 +2201,131 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function...@@ -2200,129 +2201,131 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
2200 };2201 };
2201});2202});
22022203
2203export class PreviewTabNotFoundError extends Error {2204export class PreviewTabNotFoundError extends Schema.TaggedErrorClass<PreviewTabNotFoundError>()(
2204 readonly tabId: string;2205 "PreviewTabNotFoundError",
2205 constructor(tabId: string) {2206 { tabId: Schema.String },
2206 super(`Preview tab not found: ${tabId}`);2207) {
2207 this.name = "PreviewTabNotFoundError";2208 override get message(): string {
2208 this.tabId = tabId;2209 return `Preview tab not found: ${this.tabId}`;
2209 }2210 }
2210}2211}
22112212
2212export class PreviewWebContentsNotFoundError extends Error {2213export class PreviewWebContentsNotFoundError extends Schema.TaggedErrorClass<PreviewWebContentsNotFoundError>()(
2213 readonly tabId: string;2214 "PreviewWebContentsNotFoundError",
2214 readonly webContentsId: number;2215 { tabId: Schema.String, webContentsId: Schema.Number },
2215 constructor(tabId: string, webContentsId: number) {2216) {
2216 super(`WebContents ${webContentsId} not found for preview tab ${tabId}`);2217 override get message(): string {
2217 this.name = "PreviewWebContentsNotFoundError";2218 return `WebContents ${this.webContentsId} not found for preview tab ${this.tabId}`;
2218 this.tabId = tabId;
2219 this.webContentsId = webContentsId;
2220 }2219 }
2221}2220}
22222221
2223export class PreviewWebviewNotInitializedError extends Error {2222export class PreviewWebviewNotInitializedError extends Schema.TaggedErrorClass<PreviewWebviewNotInitializedError>()(
2224 readonly tabId: string;2223 "PreviewWebviewNotInitializedError",
2225 constructor(tabId: string) {2224 { tabId: Schema.String },
2226 super(`Preview tab "${tabId}" has no webview registered`);2225) {
2227 this.name = "PreviewWebviewNotInitializedError";2226 override get message(): string {
2228 this.tabId = tabId;2227 return `Preview tab "${this.tabId}" has no webview registered`;
2229 }2228 }
2230}2229}
22312230
2232export class PreviewManagerError extends Data.TaggedError("PreviewManagerError")<{2231export class PreviewManagerError extends Schema.TaggedErrorClass<PreviewManagerError>()(
2233 readonly operation: string;2232 "PreviewManagerError",
2234 readonly cause: unknown;2233 {
2235}> {2234 operation: Schema.String,
2236 override get message() {2235 cause: Schema.Defect(),
2236 },
2237) {
2238 override get message(): string {
2237 return `Desktop preview operation failed: ${this.operation}`;2239 return `Desktop preview operation failed: ${this.operation}`;
2238 }2240 }
2239}2241}
22402242
2241export interface PreviewManagerShape {2243const isPreviewManagerError = Schema.is(PreviewManagerError);
2242 readonly setMainWindow: (window: BrowserWindow) => Effect.Effect<void, PreviewManagerError>;2244
2243 readonly getBrowserSession: (scope?: string) => Effect.Effect<Session, PreviewManagerError>;2245export class PreviewManager extends Context.Service<
2244 readonly isBrowserPartition: (partition: string) => boolean;2246 PreviewManager,
2245 readonly createTab: (tabId: string) => Effect.Effect<PreviewTabState, PreviewManagerError>;2247 {
2246 readonly closeTab: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2248 readonly setMainWindow: (window: BrowserWindow) => Effect.Effect<void, PreviewManagerError>;
2247 readonly registerWebview: (2249 readonly getBrowserSession: (scope?: string) => Effect.Effect<Session, PreviewManagerError>;
2248 tabId: string,2250 readonly isBrowserPartition: (partition: string) => boolean;
2249 webContentsId: number,2251 readonly createTab: (tabId: string) => Effect.Effect<PreviewTabState, PreviewManagerError>;
2250 ) => Effect.Effect<void, PreviewManagerError>;2252 readonly closeTab: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2251 readonly navigate: (tabId: string, url: string) => Effect.Effect<void, PreviewManagerError>;2253 readonly registerWebview: (
2252 readonly goBack: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2254 tabId: string,
2253 readonly goForward: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2255 webContentsId: number,
2254 readonly refresh: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2256 ) => Effect.Effect<void, PreviewManagerError>;
2255 readonly zoomIn: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2257 readonly navigate: (tabId: string, url: string) => Effect.Effect<void, PreviewManagerError>;
2256 readonly zoomOut: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2258 readonly goBack: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2257 readonly resetZoom: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2259 readonly goForward: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2258 readonly hardReload: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2260 readonly refresh: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2259 readonly openDevTools: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2261 readonly zoomIn: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2260 readonly clearCookies: () => Effect.Effect<void, PreviewManagerError>;2262 readonly zoomOut: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2261 readonly clearCache: () => Effect.Effect<void, PreviewManagerError>;2263 readonly resetZoom: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2262 readonly getBrowserPartition: (scope?: string) => Effect.Effect<string, PreviewManagerError>;2264 readonly hardReload: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2263 readonly setAnnotationTheme: (2265 readonly openDevTools: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2264 theme: DesktopPreviewAnnotationTheme,2266 readonly clearCookies: () => Effect.Effect<void, PreviewManagerError>;
2265 ) => Effect.Effect<void, PreviewManagerError>;2267 readonly clearCache: () => Effect.Effect<void, PreviewManagerError>;
2266 readonly pickElement: (2268 readonly getBrowserPartition: (scope?: string) => Effect.Effect<string, PreviewManagerError>;
2267 tabId: string,2269 readonly setAnnotationTheme: (
2268 ) => Effect.Effect<PreviewAnnotationPayload | null, PreviewManagerError>;2270 theme: DesktopPreviewAnnotationTheme,
2269 readonly cancelPickElement: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2271 ) => Effect.Effect<void, PreviewManagerError>;
2270 readonly captureScreenshot: (2272 readonly pickElement: (
2271 tabId: string,2273 tabId: string,
2272 ) => Effect.Effect<DesktopPreviewScreenshotArtifact, PreviewManagerError>;2274 ) => Effect.Effect<PreviewAnnotationPayload | null, PreviewManagerError>;
2273 readonly revealArtifact: (path: string) => Effect.Effect<void, PreviewManagerError>;2275 readonly cancelPickElement: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2274 readonly copyArtifactToClipboard: (path: string) => Effect.Effect<void, PreviewManagerError>;2276 readonly captureScreenshot: (
2275 readonly startRecording: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2277 tabId: string,
2276 readonly stopRecording: (tabId: string) => Effect.Effect<void, PreviewManagerError>;2278 ) => Effect.Effect<DesktopPreviewScreenshotArtifact, PreviewManagerError>;
2277 readonly saveRecording: (2279 readonly revealArtifact: (path: string) => Effect.Effect<void, PreviewManagerError>;
2278 tabId: string,2280 readonly copyArtifactToClipboard: (path: string) => Effect.Effect<void, PreviewManagerError>;
2279 mimeType: string,2281 readonly startRecording: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2280 data: Uint8Array,2282 readonly stopRecording: (tabId: string) => Effect.Effect<void, PreviewManagerError>;
2281 ) => Effect.Effect<DesktopPreviewRecordingArtifact, PreviewManagerError>;2283 readonly saveRecording: (
2282 readonly automationStatus: (2284 tabId: string,
2283 tabId: string,2285 mimeType: string,
2284 ) => Effect.Effect<PreviewAutomationStatus, PreviewManagerError>;2286 data: Uint8Array,
2285 readonly automationSnapshot: (2287 ) => Effect.Effect<DesktopPreviewRecordingArtifact, PreviewManagerError>;
2286 tabId: string,2288 readonly automationStatus: (
2287 ) => Effect.Effect<PreviewAutomationSnapshot, PreviewManagerError>;2289 tabId: string,
2288 readonly automationClick: (2290 ) => Effect.Effect<PreviewAutomationStatus, PreviewManagerError>;
2289 tabId: string,2291 readonly automationSnapshot: (
2290 input: PreviewAutomationClickInput,2292 tabId: string,
2291 ) => Effect.Effect<void, PreviewManagerError>;2293 ) => Effect.Effect<PreviewAutomationSnapshot, PreviewManagerError>;
2292 readonly automationType: (2294 readonly automationClick: (
2293 tabId: string,2295 tabId: string,
2294 input: PreviewAutomationTypeInput,2296 input: PreviewAutomationClickInput,
2295 ) => Effect.Effect<void, PreviewManagerError>;2297 ) => Effect.Effect<void, PreviewManagerError>;
2296 readonly automationPress: (2298 readonly automationType: (
2297 tabId: string,2299 tabId: string,
2298 input: PreviewAutomationPressInput,2300 input: PreviewAutomationTypeInput,
2299 ) => Effect.Effect<void, PreviewManagerError>;2301 ) => Effect.Effect<void, PreviewManagerError>;
2300 readonly automationScroll: (2302 readonly automationPress: (
2301 tabId: string,2303 tabId: string,
2302 input: PreviewAutomationScrollInput,2304 input: PreviewAutomationPressInput,
2303 ) => Effect.Effect<void, PreviewManagerError>;2305 ) => Effect.Effect<void, PreviewManagerError>;
2304 readonly automationEvaluate: (2306 readonly automationScroll: (
2305 tabId: string,2307 tabId: string,
2306 input: PreviewAutomationEvaluateInput,2308 input: PreviewAutomationScrollInput,
2307 ) => Effect.Effect<unknown, PreviewManagerError>;2309 ) => Effect.Effect<void, PreviewManagerError>;
2308 readonly automationWaitFor: (2310 readonly automationEvaluate: (
2309 tabId: string,2311 tabId: string,
2310 input: PreviewAutomationWaitForInput,2312 input: PreviewAutomationEvaluateInput,
2311 ) => Effect.Effect<void, PreviewManagerError>;2313 ) => Effect.Effect<unknown, PreviewManagerError>;
2312 readonly subscribeStateChanges: (listener: Listener) => Effect.Effect<void, never, Scope.Scope>;2314 readonly automationWaitFor: (
2313 readonly subscribePointerEvents: (2315 tabId: string,
2314 listener: PointerEventListener,2316 input: PreviewAutomationWaitForInput,
2315 ) => Effect.Effect<void, never, Scope.Scope>;2317 ) => Effect.Effect<void, PreviewManagerError>;
2316 readonly subscribeRecordingFrames: (2318 readonly subscribeStateChanges: (listener: Listener) => Effect.Effect<void, never, Scope.Scope>;
2317 listener: RecordingFrameListener,2319 readonly subscribePointerEvents: (
2318 ) => Effect.Effect<void, never, Scope.Scope>;2320 listener: PointerEventListener,
2319}2321 ) => Effect.Effect<void, never, Scope.Scope>;
23202322 readonly subscribeRecordingFrames: (
2321export class PreviewManager extends Context.Service<PreviewManager, PreviewManagerShape>()(2323 listener: RecordingFrameListener,
2322 "@t3tools/desktop/preview/Manager/PreviewManager",2324 ) => Effect.Effect<void, never, Scope.Scope>;
2323) {}2325 }
2326>()("@t3tools/desktop/preview/Manager/PreviewManager") {}
23242327
2325const make = Effect.gen(function* PreviewManagerMake() {2328export const make = Effect.gen(function* PreviewManagerMake() {
2326 const environment = yield* DesktopEnvironment.DesktopEnvironment;2329 const environment = yield* DesktopEnvironment.DesktopEnvironment;
2327 const browserSession = yield* BrowserSession.BrowserSession;2330 const browserSession = yield* BrowserSession.BrowserSession;
2328 const operations = yield* makeNativeOperations(environment.browserArtifactsDir);2331 const operations = yield* makeNativeOperations(environment.browserArtifactsDir);