diff --git a/apps/typesync/src/subcommands/studio.ts b/apps/typesync/src/subcommands/studio.ts index d7190cc3..04583da0 100644 --- a/apps/typesync/src/subcommands/studio.ts +++ b/apps/typesync/src/subcommands/studio.ts @@ -7,7 +7,7 @@ import * as Console from 'effect/Console'; import * as Data from 'effect/Data'; import * as Effect from 'effect/Effect'; import * as Layer from 'effect/Layer'; -import open, { type AppName } from 'open'; +import open, { type AppName, apps } from 'open'; import * as Server from '../Server.js'; @@ -22,7 +22,15 @@ export const typesync = Command.make('typesync', { Options.withDescription('If true, opens the Typesync studio in your browser'), Options.withDefault(true), ), - browser: Options.choice('browser', ['chrome', 'firefox', 'edge', 'browser', 'browserPrivate']).pipe( + browser: Options.choice('browser', [ + 'chrome', + 'firefox', + 'edge', + 'safari', + 'arc', + 'browser', + 'browserPrivate', + ]).pipe( Options.withAlias('b'), Options.withDescription('Broweser to open the Typesync studio app in. Default is your default selected browser'), Options.withDefault('browser'), @@ -60,19 +68,46 @@ export const typesync = Command.make('typesync', { ), ); -const openBrowser = (port: number, browser: AppName) => +const openBrowser = (port: number, browser: AppName | 'arc' | 'safari' | 'browser' | 'browserPrivate') => Effect.async((resume) => { - open(`http://localhost:${port}`, { - app: { name: browser }, - }).then((subprocess) => { - // wait for child process to start before succeeding - subprocess.on('spawn', () => { - resume(Effect.void); - }); - subprocess.on('error', (err) => { - resume(Effect.fail(new OpenBrowserError({ cause: err }))); + const url = `http://localhost:${port}`; + + const launch = (appOpts?: { name: string | readonly string[] }) => + open(url, appOpts ? { app: appOpts } : undefined).then((subprocess) => { + subprocess.on('spawn', () => resume(Effect.void)); + subprocess.on('error', (err) => resume(Effect.fail(new OpenBrowserError({ cause: err })))); }); - }); + + const mapBrowserName = (b: typeof browser): string | readonly string[] | undefined => { + switch (b) { + case 'chrome': + return apps.chrome; // cross-platform alias from open + case 'firefox': + return apps.firefox; + case 'edge': + return apps.edge; + case 'safari': + return 'Safari'; + case 'arc': + return 'Arc'; + default: + return undefined; + } + }; + + switch (browser) { + case 'browser': + launch(); + break; + case 'browserPrivate': + launch({ name: apps.browserPrivate }); + break; + default: { + const mapped = mapBrowserName(browser); + mapped ? launch({ name: mapped }).catch(() => launch()) : launch(); + break; + } + } }); export class OpenBrowserError extends Data.TaggedError('/typesync/errors/OpenBrowserError')<{