import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";import * as NodePath from "@effect/platform-node/NodePath";import { assert, it } from "@effect/vitest";import { Deferred, Effect, Exit, FileSystem, Layer, PlatformError, Scope, Sink, Stream,} from "effect";import { ChildProcessSpawner } from "effect/unstable/process"; import { make as makeDesktopLauncher, type DetachedSpawnInput, type LaunchRuntimeOptions,} from "./DesktopLauncher";import { DesktopLauncher, DesktopLauncherSpawnError } from "../Services/DesktopLauncher"; const encoder = new TextEncoder(); interface SpawnCall { readonly command: string; readonly args: ReadonlyArray<string>; readonly detached?: boolean | undefined; readonly shell?: boolean | string | undefined; readonly windowsVerbatimArguments?: boolean | undefined; readonly windowsHide?: boolean | undefined; readonly stdin?: unknown; readonly stdout?: unknown; readonly stderr?: unknown;} function decodePowerShellCommand(encoded: string): string { return Buffer.from(encoded, "base64").toString("utf16le");} function platformSpawnError(message: string) { return PlatformError.systemError({ _tag: "Unknown", module: "ChildProcess", method: "spawn", description: message, });} function spawnerLayer( calls: Array<SpawnCall>, handler: (call: SpawnCall) => { readonly code?: number; readonly fail?: string; readonly awaitExit?: Deferred.Deferred<void>; readonly onKill?: () => void; } = () => ({ code: 0, }),) { return Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => { const standardCommand = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; readonly options: { readonly detached?: boolean; readonly shell?: boolean | string; readonly stdin?: unknown; readonly stdout?: unknown; readonly stderr?: unknown; }; }; const call: SpawnCall = { command: standardCommand.command, args: [...standardCommand.args], detached: standardCommand.options.detached, shell: standardCommand.options.shell, stdin: standardCommand.options.stdin, stdout: standardCommand.options.stdout, stderr: standardCommand.options.stderr, }; calls.push(call); const result = handler(call); if (result.fail) { return Effect.fail(platformSpawnError(result.fail)); } let exited = false; const exitCode = result.awaitExit ? Deferred.await(result.awaitExit).pipe( Effect.tap(() => Effect.sync(() => { exited = true; }), ), Effect.andThen(Effect.succeed(ChildProcessSpawner.ExitCode(result.code ?? 0))), ) : Effect.sync(() => { exited = true; return ChildProcessSpawner.ExitCode(result.code ?? 0); }); return Effect.succeed( ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(calls.length), exitCode, isRunning: Effect.succeed(false), kill: () => Effect.sync(() => { if (!exited) { result.onKill?.(); } }), stdin: Sink.drain, stdout: Stream.make(encoder.encode("")), stderr: Stream.make(encoder.encode("")), all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }), ); }), );} function spawnHarness( calls: Array<SpawnCall>, handler: (call: SpawnCall) => { readonly code?: number; readonly fail?: string; readonly awaitExit?: Deferred.Deferred<void>; readonly onKill?: () => void; } = () => ({ code: 0, }),) { return { layer: spawnerLayer(calls, handler), spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => { const call: SpawnCall = { command: input.command, args: [...input.args], detached: input.detached, shell: input.shell, windowsVerbatimArguments: input.windowsVerbatimArguments, windowsHide: input.windowsHide, stdin: input.stdin, stdout: input.stdout, stderr: input.stderr, }; calls.push(call); const result = handler(call); return result.fail ? Effect.fail( new DesktopLauncherSpawnError({ operation: context.operation, command: input.command, args: [...input.args], ...(context.target !== undefined ? { target: context.target } : {}), ...(context.editor !== undefined ? { editor: context.editor } : {}), }), ) : Effect.void; }, } as const;} const provideOpen = ( options: LaunchRuntimeOptions, harness: { readonly layer: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner>; readonly spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => Effect.Effect<void, DesktopLauncherSpawnError>; },) => Layer.effect( DesktopLauncher, makeDesktopLauncher({ ...options, spawnDetached: harness.spawnDetached }), ).pipe(Layer.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer, harness.layer))); const runOpen = <A>( options: LaunchRuntimeOptions, harness: { readonly layer: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner>; readonly spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => Effect.Effect<void, DesktopLauncherSpawnError>; }, effect: Effect.Effect<A, never, DesktopLauncher>,) => effect.pipe(Effect.provide(provideOpen(options, harness))); const readOpen = Effect.service(DesktopLauncher); const writeExecutable = (filePath: string) => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; yield* fs.writeFileString(filePath, "#!/bin/sh\nexit 0\n"); yield* fs.chmod(filePath, 0o755); }); it.effect("getAvailableEditors detects installed editors through the service", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-editors-" }); yield* fs.writeFileString(`${dir}/code-insiders.CMD`, "@echo off\r\n"); yield* fs.writeFileString(`${dir}/codium.CMD`, "@echo off\r\n"); yield* fs.writeFileString(`${dir}/explorer.CMD`, "@echo off\r\n"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, }, spawnHarness(calls), readOpen, ); const editors = yield* open.getAvailableEditors; assert.deepEqual(editors, ["vscode-insiders", "vscodium", "file-manager"]); assert.deepEqual(calls, []); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("getAvailableEditors does not advertise WSL file-manager from PowerShell alone", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-wsl-editors-" }); yield* fs.makeDirectory(`${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0`, { recursive: true, }); yield* writeExecutable(`${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "linux", env: { PATH: `${dir}:${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0`, WSL_DISTRO_NAME: "Ubuntu", }, isWsl: true, isInsideContainer: false, powerShellCommand: `${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`, }, spawnHarness(calls), readOpen, ); const editors = yield* open.getAvailableEditors; assert.equal(editors.includes("file-manager"), false); assert.deepEqual(calls, []); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openInEditor uses --goto for editors that support it", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-cursor-" }); yield* writeExecutable(`${dir}/cursor`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "/tmp/workspace/src/open.ts:71:5", editor: "cursor", }); assert.deepEqual(calls, [ { command: `${dir}/cursor`, args: ["--goto", "/tmp/workspace/src/open.ts:71:5"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openInEditor uses explorer with a VS Code protocol target on Windows", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-vscode-win32-" }); yield* fs.writeFileString(`${dir}/explorer.exe`, "MZ"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "C:\\work\\100% real\\file.ts:12:4", editor: "vscode", }); assert.deepEqual(calls, [ { command: `${dir}/explorer.exe`, args: ["vscode://file/C:/work/100%25%20real/file.ts:12:4"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openInEditor detached launches bypass the scoped child-process spawner", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-zed-detached-" }); yield* writeExecutable(`${dir}/zed`); const calls: Array<SpawnCall> = []; const harness = spawnHarness(calls); yield* Effect.acquireUseRelease( Scope.make("sequential"), (scope) => Effect.gen(function* () { const runtimeServices = yield* Layer.build( provideOpen( { platform: "darwin", env: { PATH: dir }, }, harness, ), ).pipe(Scope.provide(scope)); const open = yield* readOpen.pipe(Effect.provide(runtimeServices), Scope.provide(scope)); yield* open.openInEditor({ cwd: "/tmp/workspace", editor: "zed", }); }), (scope) => Scope.close(scope, Exit.void), ); assert.deepEqual(calls, [ { command: `${dir}/zed`, args: ["/tmp/workspace"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openInEditor uses the default opener for file-manager on macOS", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-macos-" }); yield* writeExecutable(`${dir}/open`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "/tmp/workspace", editor: "file-manager", }); assert.deepEqual(calls, [ { command: `${dir}/open`, args: ["/tmp/workspace"], detached: undefined, shell: false, stdin: undefined, stdout: undefined, stderr: undefined, }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openBrowser uses macOS open flags and app arguments", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-browser-macos-" }); yield* writeExecutable(`${dir}/open`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openBrowser("https://example.com", { wait: true, background: true, newInstance: true, app: { name: "google chrome", arguments: ["--profile-directory=Work"], }, }); assert.deepEqual(calls, [ { command: `${dir}/open`, args: [ "--wait-apps", "--background", "--new", "-a", "google chrome", "https://example.com", "--args", "--profile-directory=Work", ], detached: undefined, shell: false, stdin: undefined, stdout: undefined, stderr: undefined, }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)),); it.effect("openBrowser uses PowerShell on win32", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-browser-win32-" }); yield* fs.writeFileString(`${dir}/powershell.exe`, "MZ"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, },Lines 1–500 of 835. Load more
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"; import * as NodePath from "@effect/platform-node/NodePath"; import { assert, it } from "@effect/vitest"; import { Deferred, Effect, Exit, FileSystem, Layer, PlatformError, Scope, Sink, Stream, } from "effect"; import { ChildProcessSpawner } from "effect/unstable/process"; import { make as makeDesktopLauncher, type DetachedSpawnInput, type LaunchRuntimeOptions, } from "./DesktopLauncher"; import { DesktopLauncher, DesktopLauncherSpawnError } from "../Services/DesktopLauncher"; const encoder = new TextEncoder(); interface SpawnCall { readonly command: string; readonly args: ReadonlyArray<string>; readonly detached?: boolean | undefined; readonly shell?: boolean | string | undefined; readonly windowsVerbatimArguments?: boolean | undefined; readonly windowsHide?: boolean | undefined; readonly stdin?: unknown; readonly stdout?: unknown; readonly stderr?: unknown; } function decodePowerShellCommand(encoded: string): string { return Buffer.from(encoded, "base64").toString("utf16le"); } function platformSpawnError(message: string) { return PlatformError.systemError({ _tag: "Unknown", module: "ChildProcess", method: "spawn", description: message, }); } function spawnerLayer( calls: Array<SpawnCall>, handler: (call: SpawnCall) => { readonly code?: number; readonly fail?: string; readonly awaitExit?: Deferred.Deferred<void>; readonly onKill?: () => void; } = () => ({ code: 0, }), ) { return Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => { const standardCommand = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; readonly options: { readonly detached?: boolean; readonly shell?: boolean | string; readonly stdin?: unknown; readonly stdout?: unknown; readonly stderr?: unknown; }; }; const call: SpawnCall = { command: standardCommand.command, args: [...standardCommand.args], detached: standardCommand.options.detached, shell: standardCommand.options.shell, stdin: standardCommand.options.stdin, stdout: standardCommand.options.stdout, stderr: standardCommand.options.stderr, }; calls.push(call); const result = handler(call); if (result.fail) { return Effect.fail(platformSpawnError(result.fail)); } let exited = false; const exitCode = result.awaitExit ? Deferred.await(result.awaitExit).pipe( Effect.tap(() => Effect.sync(() => { exited = true; }), ), Effect.andThen(Effect.succeed(ChildProcessSpawner.ExitCode(result.code ?? 0))), ) : Effect.sync(() => { exited = true; return ChildProcessSpawner.ExitCode(result.code ?? 0); }); return Effect.succeed( ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(calls.length), exitCode, isRunning: Effect.succeed(false), kill: () => Effect.sync(() => { if (!exited) { result.onKill?.(); } }), stdin: Sink.drain, stdout: Stream.make(encoder.encode("")), stderr: Stream.make(encoder.encode("")), all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }), ); }), ); } function spawnHarness( calls: Array<SpawnCall>, handler: (call: SpawnCall) => { readonly code?: number; readonly fail?: string; readonly awaitExit?: Deferred.Deferred<void>; readonly onKill?: () => void; } = () => ({ code: 0, }), ) { return { layer: spawnerLayer(calls, handler), spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => { const call: SpawnCall = { command: input.command, args: [...input.args], detached: input.detached, shell: input.shell, windowsVerbatimArguments: input.windowsVerbatimArguments, windowsHide: input.windowsHide, stdin: input.stdin, stdout: input.stdout, stderr: input.stderr, }; calls.push(call); const result = handler(call); return result.fail ? Effect.fail( new DesktopLauncherSpawnError({ operation: context.operation, command: input.command, args: [...input.args], ...(context.target !== undefined ? { target: context.target } : {}), ...(context.editor !== undefined ? { editor: context.editor } : {}), }), ) : Effect.void; }, } as const; } const provideOpen = ( options: LaunchRuntimeOptions, harness: { readonly layer: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner>; readonly spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => Effect.Effect<void, DesktopLauncherSpawnError>; }, ) => Layer.effect( DesktopLauncher, makeDesktopLauncher({ ...options, spawnDetached: harness.spawnDetached }), ).pipe(Layer.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer, harness.layer))); const runOpen = <A>( options: LaunchRuntimeOptions, harness: { readonly layer: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner>; readonly spawnDetached: ( input: DetachedSpawnInput, context: { readonly operation: string; readonly target?: string; readonly editor?: string; }, ) => Effect.Effect<void, DesktopLauncherSpawnError>; }, effect: Effect.Effect<A, never, DesktopLauncher>, ) => effect.pipe(Effect.provide(provideOpen(options, harness))); const readOpen = Effect.service(DesktopLauncher); const writeExecutable = (filePath: string) => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; yield* fs.writeFileString(filePath, "#!/bin/sh\nexit 0\n"); yield* fs.chmod(filePath, 0o755); }); it.effect("getAvailableEditors detects installed editors through the service", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-editors-" }); yield* fs.writeFileString(`${dir}/code-insiders.CMD`, "@echo off\r\n"); yield* fs.writeFileString(`${dir}/codium.CMD`, "@echo off\r\n"); yield* fs.writeFileString(`${dir}/explorer.CMD`, "@echo off\r\n"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, }, spawnHarness(calls), readOpen, ); const editors = yield* open.getAvailableEditors; assert.deepEqual(editors, ["vscode-insiders", "vscodium", "file-manager"]); assert.deepEqual(calls, []); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("getAvailableEditors does not advertise WSL file-manager from PowerShell alone", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-wsl-editors-" }); yield* fs.makeDirectory(`${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0`, { recursive: true, }); yield* writeExecutable(`${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "linux", env: { PATH: `${dir}:${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0`, WSL_DISTRO_NAME: "Ubuntu", }, isWsl: true, isInsideContainer: false, powerShellCommand: `${dir}/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`, }, spawnHarness(calls), readOpen, ); const editors = yield* open.getAvailableEditors; assert.equal(editors.includes("file-manager"), false); assert.deepEqual(calls, []); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openInEditor uses --goto for editors that support it", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-cursor-" }); yield* writeExecutable(`${dir}/cursor`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "/tmp/workspace/src/open.ts:71:5", editor: "cursor", }); assert.deepEqual(calls, [ { command: `${dir}/cursor`, args: ["--goto", "/tmp/workspace/src/open.ts:71:5"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openInEditor uses explorer with a VS Code protocol target on Windows", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-vscode-win32-" }); yield* fs.writeFileString(`${dir}/explorer.exe`, "MZ"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "C:\\work\\100% real\\file.ts:12:4", editor: "vscode", }); assert.deepEqual(calls, [ { command: `${dir}/explorer.exe`, args: ["vscode://file/C:/work/100%25%20real/file.ts:12:4"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openInEditor detached launches bypass the scoped child-process spawner", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-zed-detached-" }); yield* writeExecutable(`${dir}/zed`); const calls: Array<SpawnCall> = []; const harness = spawnHarness(calls); yield* Effect.acquireUseRelease( Scope.make("sequential"), (scope) => Effect.gen(function* () { const runtimeServices = yield* Layer.build( provideOpen( { platform: "darwin", env: { PATH: dir }, }, harness, ), ).pipe(Scope.provide(scope)); const open = yield* readOpen.pipe(Effect.provide(runtimeServices), Scope.provide(scope)); yield* open.openInEditor({ cwd: "/tmp/workspace", editor: "zed", }); }), (scope) => Scope.close(scope, Exit.void), ); assert.deepEqual(calls, [ { command: `${dir}/zed`, args: ["/tmp/workspace"], detached: true, shell: false, stdin: "ignore", stdout: "ignore", stderr: "ignore", }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openInEditor uses the default opener for file-manager on macOS", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-macos-" }); yield* writeExecutable(`${dir}/open`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openInEditor({ cwd: "/tmp/workspace", editor: "file-manager", }); assert.deepEqual(calls, [ { command: `${dir}/open`, args: ["/tmp/workspace"], detached: undefined, shell: false, stdin: undefined, stdout: undefined, stderr: undefined, }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openBrowser uses macOS open flags and app arguments", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-browser-macos-" }); yield* writeExecutable(`${dir}/open`); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "darwin", env: { PATH: dir }, }, spawnHarness(calls), readOpen, ); yield* open.openBrowser("https://example.com", { wait: true, background: true, newInstance: true, app: { name: "google chrome", arguments: ["--profile-directory=Work"], }, }); assert.deepEqual(calls, [ { command: `${dir}/open`, args: [ "--wait-apps", "--background", "--new", "-a", "google chrome", "https://example.com", "--args", "--profile-directory=Work", ], detached: undefined, shell: false, stdin: undefined, stdout: undefined, stderr: undefined, }, ]); }).pipe(Effect.provide(NodeFileSystem.layer)), ); it.effect("openBrowser uses PowerShell on win32", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-browser-win32-" }); yield* fs.writeFileString(`${dir}/powershell.exe`, "MZ"); const calls: Array<SpawnCall> = []; const open = yield* runOpen( { platform: "win32", env: { PATH: dir, PATHEXT: ".COM;.EXE;.BAT;.CMD", }, },