Skip to content

Commit 6ef0dfa

Browse files
authored
refactor: flatten session open flow (#296)
* refactor: simplify session open preparation flow * refactor: flatten session open flow * refactor: use explicit open details result
1 parent 8f6c01c commit 6ef0dfa

2 files changed

Lines changed: 141 additions & 199 deletions

File tree

Lines changed: 23 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { resolveTargetDevice } from '../../core/dispatch.ts';
21
import { isDeepLinkTarget } from '../../core/open-target.ts';
32
import { ensureDeviceReady } from '../device-ready.ts';
43
import type { DeviceInfo } from '../../utils/device.ts';
@@ -8,7 +7,6 @@ import {
87
classifyAndroidAppTarget,
98
formatAndroidInstalledPackageRequiredMessage,
109
} from '../../platforms/android/open-target.ts';
11-
import { refreshSessionDeviceIfNeeded } from './session-device-utils.ts';
1210
import {
1311
maybeClearRemovedRuntimeTransportHints,
1412
tryResolveOpenRuntimeHints,
@@ -27,18 +25,17 @@ type ResolveAndroidPackageForOpen = (
2725
openTarget: string | undefined,
2826
) => Promise<string | undefined>;
2927

30-
type PreparedOpenCommand = {
31-
device: DeviceInfo;
32-
surface: SessionSurface;
33-
openTarget?: string;
34-
openPositionals: string[];
28+
type OpenCommandDetails = {
3529
appBundleId?: string;
3630
appName?: string;
3731
runtime: SessionRuntimeHints | undefined;
38-
existingSession?: SessionState;
3932
};
4033

41-
function invalidArgs(message: string): DaemonResponse {
34+
export type PreparedOpenCommandDetailsResult =
35+
| { type: 'response'; response: DaemonResponse }
36+
| { type: 'details'; details: OpenCommandDetails };
37+
38+
export function invalidOpenArgs(message: string): DaemonResponse {
4239
return {
4340
ok: false,
4441
error: {
@@ -48,7 +45,7 @@ function invalidArgs(message: string): DaemonResponse {
4845
};
4946
}
5047

51-
function toSurfaceResponse(
48+
export function resolveOpenSurfaceResponse(
5249
device: DeviceInfo,
5350
surfaceFlag: string | undefined,
5451
openTarget: string | undefined,
@@ -72,7 +69,7 @@ function toSurfaceResponse(
7269
}
7370
}
7471

75-
function validatePreparedOpenRequest(params: {
72+
export function validateResolvedOpenRequest(params: {
7673
shouldRelaunch: boolean;
7774
openTarget: string | undefined;
7875
surface: SessionSurface;
@@ -81,76 +78,64 @@ function validatePreparedOpenRequest(params: {
8178
const { shouldRelaunch, openTarget, surface, device } = params;
8279
if (!shouldRelaunch) return null;
8380
if (openTarget && isDeepLinkTarget(openTarget)) {
84-
return invalidArgs('open --relaunch does not support URL targets.');
81+
return invalidOpenArgs('open --relaunch does not support URL targets.');
8582
}
8683
if (surface !== 'app') {
87-
return invalidArgs('open --relaunch is supported only for app surfaces.');
84+
return invalidOpenArgs('open --relaunch is supported only for app surfaces.');
8885
}
8986
if (
9087
device.platform === 'android' &&
9188
openTarget &&
9289
classifyAndroidAppTarget(openTarget) === 'binary'
9390
) {
94-
return invalidArgs(formatAndroidInstalledPackageRequiredMessage(openTarget));
91+
return invalidOpenArgs(formatAndroidInstalledPackageRequiredMessage(openTarget));
9592
}
9693
return null;
9794
}
9895

99-
function validatePreResolvedOpenRequest(params: {
96+
export function validatePreResolvedOpenRequest(params: {
10097
shouldRelaunch: boolean;
10198
openTarget: string | undefined;
10299
platform: DeviceInfo['platform'] | undefined;
103100
}): DaemonResponse | null {
104101
const { shouldRelaunch, openTarget, platform } = params;
105102
if (!shouldRelaunch) return null;
106103
if (openTarget && isDeepLinkTarget(openTarget)) {
107-
return invalidArgs('open --relaunch does not support URL targets.');
104+
return invalidOpenArgs('open --relaunch does not support URL targets.');
108105
}
109106
if (platform === 'android' && openTarget && classifyAndroidAppTarget(openTarget) === 'binary') {
110-
return invalidArgs(formatAndroidInstalledPackageRequiredMessage(openTarget));
107+
return invalidOpenArgs(formatAndroidInstalledPackageRequiredMessage(openTarget));
111108
}
112109
return null;
113110
}
114111

115-
async function prepareResolvedOpenCommand(params: {
112+
export async function prepareOpenCommandDetails(params: {
116113
req: DaemonRequest;
117114
sessionName: string;
118115
sessionStore: SessionStore;
119116
device: DeviceInfo;
120117
surface: SessionSurface;
121118
openTarget: string | undefined;
122-
openPositionals: string[];
123119
ensureReady: typeof ensureDeviceReady;
124120
resolveAndroidPackageForOpen: (
125121
device: DeviceInfo,
126122
openTarget: string | undefined,
127123
) => Promise<string | undefined>;
128-
existingSession?: SessionState;
129124
clearRuntimeHints?: typeof clearRuntimeHintsFromApp;
130-
}): Promise<{ response: DaemonResponse } | { prepared: PreparedOpenCommand }> {
125+
existingSession?: SessionState;
126+
}): Promise<PreparedOpenCommandDetailsResult> {
131127
const {
132128
req,
133129
sessionName,
134130
sessionStore,
135131
device,
136132
surface,
137133
openTarget,
138-
openPositionals,
139134
ensureReady,
140135
resolveAndroidPackageForOpen,
141136
existingSession,
142137
clearRuntimeHints,
143138
} = params;
144-
const validation = validatePreparedOpenRequest({
145-
shouldRelaunch: req.flags?.relaunch === true,
146-
openTarget,
147-
surface,
148-
device,
149-
});
150-
if (validation) {
151-
return { response: validation };
152-
}
153-
154139
await ensureReady(device);
155140
const { appBundleId, appName } = await resolvePreparedOpenIdentity({
156141
device,
@@ -166,7 +151,10 @@ async function prepareResolvedOpenCommand(params: {
166151
device,
167152
});
168153
if (!runtimeResult.ok) {
169-
return { response: runtimeResult.response };
154+
return {
155+
type: 'response',
156+
response: runtimeResult.response,
157+
};
170158
}
171159

172160
if (existingSession && clearRuntimeHints) {
@@ -181,15 +169,11 @@ async function prepareResolvedOpenCommand(params: {
181169
}
182170

183171
return {
184-
prepared: {
185-
device,
186-
surface,
187-
openTarget,
188-
openPositionals,
172+
type: 'details',
173+
details: {
189174
appBundleId,
190175
appName,
191176
runtime: runtimeResult.data.runtime,
192-
existingSession,
193177
},
194178
};
195179
}
@@ -215,134 +199,3 @@ async function resolvePreparedOpenIdentity(params: {
215199
appName: macOsSurfaceState.appName ?? openTarget,
216200
};
217201
}
218-
219-
export async function prepareExistingOpenCommand(params: {
220-
req: DaemonRequest;
221-
sessionName: string;
222-
sessionStore: SessionStore;
223-
session: SessionState;
224-
ensureReady: typeof ensureDeviceReady;
225-
resolveDevice: typeof resolveTargetDevice;
226-
clearRuntimeHints: typeof clearRuntimeHintsFromApp;
227-
resolveAndroidPackageForOpen: ResolveAndroidPackageForOpen;
228-
}): Promise<{ response: DaemonResponse } | { prepared: PreparedOpenCommand }> {
229-
const {
230-
req,
231-
sessionName,
232-
sessionStore,
233-
session,
234-
ensureReady,
235-
resolveDevice,
236-
clearRuntimeHints,
237-
resolveAndroidPackageForOpen,
238-
} = params;
239-
const shouldRelaunch = req.flags?.relaunch === true;
240-
const requestedOpenTarget = req.positionals?.[0];
241-
const openTarget = requestedOpenTarget ?? (shouldRelaunch ? session.appName : undefined);
242-
const surfaceResult = toSurfaceResponse(
243-
session.device,
244-
req.flags?.surface,
245-
openTarget,
246-
session.surface,
247-
);
248-
if (typeof surfaceResult !== 'string') {
249-
return { response: surfaceResult };
250-
}
251-
252-
if (!openTarget && surfaceResult === 'app') {
253-
return {
254-
response: shouldRelaunch
255-
? invalidArgs('open --relaunch requires an app name or an active session app.')
256-
: invalidArgs('Session already active. Close it first or pass a new --session name.'),
257-
};
258-
}
259-
260-
const validation = validatePreparedOpenRequest({
261-
shouldRelaunch,
262-
openTarget,
263-
surface: surfaceResult,
264-
device: session.device,
265-
});
266-
if (validation) {
267-
return { response: validation };
268-
}
269-
270-
const device = await refreshSessionDeviceIfNeeded(session.device, resolveDevice);
271-
return await prepareResolvedOpenCommand({
272-
req,
273-
sessionName,
274-
sessionStore,
275-
device,
276-
surface: surfaceResult,
277-
openTarget,
278-
openPositionals: requestedOpenTarget ? (req.positionals ?? []) : openTarget ? [openTarget] : [],
279-
ensureReady,
280-
resolveAndroidPackageForOpen,
281-
clearRuntimeHints,
282-
existingSession: session,
283-
});
284-
}
285-
286-
export async function prepareNewOpenCommand(params: {
287-
req: DaemonRequest;
288-
sessionStore: SessionStore;
289-
sessionName: string;
290-
ensureReady: typeof ensureDeviceReady;
291-
resolveDevice: typeof resolveTargetDevice;
292-
resolveAndroidPackageForOpen: ResolveAndroidPackageForOpen;
293-
}): Promise<{ response: DaemonResponse } | { prepared: PreparedOpenCommand }> {
294-
const {
295-
req,
296-
sessionStore,
297-
sessionName,
298-
ensureReady,
299-
resolveDevice,
300-
resolveAndroidPackageForOpen,
301-
} = params;
302-
const shouldRelaunch = req.flags?.relaunch === true;
303-
const openTarget = req.positionals?.[0];
304-
if (shouldRelaunch && !openTarget) {
305-
return { response: invalidArgs('open --relaunch requires an app argument.') };
306-
}
307-
308-
const preResolvedValidation = validatePreResolvedOpenRequest({
309-
shouldRelaunch,
310-
openTarget,
311-
platform: req.flags?.platform === 'android' ? 'android' : undefined,
312-
});
313-
if (preResolvedValidation) {
314-
return { response: preResolvedValidation };
315-
}
316-
317-
const device = await resolveDevice(req.flags ?? {});
318-
const surfaceResult = toSurfaceResponse(device, req.flags?.surface, openTarget);
319-
if (typeof surfaceResult !== 'string') {
320-
return { response: surfaceResult };
321-
}
322-
323-
const inUse = sessionStore.toArray().find((session) => session.device.id === device.id);
324-
if (inUse) {
325-
return {
326-
response: {
327-
ok: false,
328-
error: {
329-
code: 'DEVICE_IN_USE',
330-
message: `Device is already in use by session "${inUse.name}".`,
331-
details: { session: inUse.name, deviceId: device.id, deviceName: device.name },
332-
},
333-
},
334-
};
335-
}
336-
337-
return await prepareResolvedOpenCommand({
338-
req,
339-
sessionName,
340-
sessionStore,
341-
device,
342-
surface: surfaceResult,
343-
openTarget,
344-
openPositionals: req.positionals ?? [],
345-
ensureReady,
346-
resolveAndroidPackageForOpen,
347-
});
348-
}

0 commit comments

Comments
 (0)