Skip to content

Commit c8c7e7d

Browse files
committed
refactor: prefer value guards for indexed access
1 parent a9e917a commit c8c7e7d

15 files changed

Lines changed: 72 additions & 50 deletions

src/commands/cli-grammar/interactions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,15 @@ function readLongPressTargetFromPositionals(positionals: string[]): LongPressOpt
140140
}
141141

142142
export function readFillTargetFromPositionals(positionals: string[]): DecodedFillTarget {
143-
if (positionals[0]?.startsWith('@')) {
143+
const firstPositional = positionals[0];
144+
if (firstPositional?.startsWith('@')) {
144145
const text =
145146
positionals.length >= 3 ? positionals.slice(2).join(' ') : positionals.slice(1).join(' ');
146-
const maybeLabel = positionals[1];
147147
return {
148148
kind: 'ref',
149149
target: {
150-
ref: positionals[0]!,
151-
label: positionals.length >= 3 ? optionalTrimmedText([maybeLabel!]) : undefined,
150+
ref: firstPositional,
151+
label: positionals.length >= 3 ? optionalTrimmedText(positionals.slice(1, 2)) : undefined,
152152
},
153153
text,
154154
};

src/commands/interaction-targeting.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ function findPreferredActionableDescendant(
8080
if (sameRectChildren.length !== 1) {
8181
break;
8282
}
83-
current = sameRectChildren[0]!;
83+
const child = sameRectChildren[0];
84+
if (child === undefined) break;
85+
current = child;
8486
}
8587

8688
return current === node ? null : current;

src/daemon-client.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,12 @@ async function prepareRemoteRequest(
265265
clientArtifactPaths,
266266
});
267267

268-
if (
269-
!isRemoteDaemon(info) ||
270-
(req.command !== 'install' && req.command !== 'reinstall') ||
271-
positionals.length < 2
272-
) {
268+
if (!isRemoteDaemon(info) || (req.command !== 'install' && req.command !== 'reinstall')) {
273269
return baseResult();
274270
}
275271

276-
const rawPath = positionals[1]!;
272+
const rawPath = positionals[1];
273+
if (rawPath === undefined) return baseResult();
277274
if (rawPath.startsWith('remote:')) {
278275
positionals[1] = rawPath.slice('remote:'.length);
279276
return createPreparedRemoteRequest({ positionals, flags, clientArtifactPaths });
@@ -959,7 +956,11 @@ function resolveDaemonLaunchSpec(): DaemonLaunchSpec {
959956
path.join(root, 'dist', 'src', 'internal', 'daemon.js'),
960957
path.join(root, 'dist', 'src', 'daemon.js'),
961958
];
962-
const distPath = distPaths.find((candidate) => fs.existsSync(candidate)) ?? distPaths[0]!;
959+
const defaultDistPath = distPaths[0];
960+
if (defaultDistPath === undefined) {
961+
throw new AppError('COMMAND_FAILED', 'Daemon dist path list is empty');
962+
}
963+
const distPath = distPaths.find((candidate) => fs.existsSync(candidate)) ?? defaultDistPath;
963964
const srcPath = path.join(root, 'src', 'daemon.ts');
964965

965966
const hasDist = distPaths.some((candidate) => fs.existsSync(candidate));

src/daemon/artifact-archive.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ function resolveArchiveRootName(entries: string[], platform: 'ios' | 'android'):
9292
const rootEntries = [...roots];
9393
if (platform === 'ios') {
9494
const appRoots = rootEntries.filter((entry) => entry.toLowerCase().endsWith('.app'));
95-
if (appRoots.length === 1) return appRoots[0]!;
95+
const appRoot = appRoots[0];
96+
if (appRoot !== undefined && appRoots.length === 1) return appRoot;
9697
if (appRoots.length === 0) {
9798
throw new AppError(
9899
'INVALID_ARGS',
@@ -104,7 +105,8 @@ function resolveArchiveRootName(entries: string[], platform: 'ios' | 'android'):
104105
`iOS app bundle archives must contain exactly one top-level .app directory, found: ${appRoots.join(', ')}`,
105106
);
106107
}
107-
if (rootEntries.length === 1) return rootEntries[0]!;
108+
const rootEntry = rootEntries[0];
109+
if (rootEntry !== undefined && rootEntries.length === 1) return rootEntry;
108110
throw new AppError(
109111
'INVALID_ARGS',
110112
`Archive must contain a single top-level bundle, found: ${rootEntries.join(', ')}`,

src/daemon/screenshot-overlay-rects.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export function rectContains(container: Rect, nested: Rect): boolean {
1818
}
1919

2020
export function unionRects(rects: Rect[]): Rect {
21-
const firstRect = rects[0]!;
21+
const firstRect = rects[0];
22+
if (firstRect === undefined) {
23+
throw new Error('unionRects requires at least one rect');
24+
}
2225
let minX = firstRect.x;
2326
let minY = firstRect.y;
2427
let maxRight = firstRect.x + firstRect.width;

src/daemon/selectors-parse.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ export function splitSelectorFromArgs(
103103
}
104104
}
105105
if (boundaries.length === 0) return null;
106-
const lastBoundary = boundaries[boundaries.length - 1]!;
106+
const lastBoundary = boundaries.at(-1);
107+
if (lastBoundary === undefined) return null;
107108
let boundary = lastBoundary;
108109
if (preferTrailingValue) {
109110
for (let i = boundaries.length - 1; i >= 0; i -= 1) {
110-
const candidateBoundary = boundaries[i]!;
111+
const candidateBoundary = boundaries[i];
112+
if (candidateBoundary === undefined) continue;
111113
if (candidateBoundary < args.length) {
112114
boundary = candidateBoundary;
113115
break;

src/daemon/session-routing.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export function resolveEffectiveSessionName(
1212
if (sessionStore.has(requested)) return requested;
1313

1414
const sessions = sessionStore.toArray();
15-
if (sessions.length === 1) return sessions[0]!.name;
15+
const session = sessions[0];
16+
if (session !== undefined && sessions.length === 1) return session.name;
1617
return requested;
1718
}
1819

src/platforms/android/app-lifecycle.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ export async function resolveAndroidApp(
7777
const matches = packages.filter((pkg: string) =>
7878
pkg.toLowerCase().includes(trimmed.toLowerCase()),
7979
);
80-
if (matches.length === 1) {
80+
const match = matches[0];
81+
if (match !== undefined && matches.length === 1) {
8182
return androidAppResolutionCache.set(cacheScope, trimmed, {
8283
type: 'package',
83-
value: matches[0]!,
84+
value: match,
8485
});
8586
}
8687

@@ -586,9 +587,11 @@ export function parseAndroidLaunchComponent(stdout: string): string | null {
586587
.map((line: string) => line.trim())
587588
.filter(Boolean);
588589
for (let index = lines.length - 1; index >= 0; index -= 1) {
589-
const line = lines[index]!;
590+
const line = lines[index];
591+
if (line === undefined) continue;
590592
if (!line.includes('/')) continue;
591-
return line.split(/\s+/)[0]!;
593+
const component = line.split(/\s+/)[0];
594+
if (component !== undefined) return component;
592595
}
593596
return null;
594597
}

src/platforms/android/app-parsers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ function parseAndroidBlockingDialogFromSegment(
8686
const respondingMatch = ANDROID_RESPONDING_TITLE_PATTERN.exec(windowText);
8787
if (!respondingMatch) return null;
8888

89-
const focusedWindow = respondingMatch[1]!.trim().replace(/\s+/g, ' ');
89+
const focusedWindowTitle = respondingMatch[1];
90+
if (focusedWindowTitle === undefined) return null;
91+
const focusedWindow = focusedWindowTitle.trim().replace(/\s+/g, ' ');
9092
const packageName = ANDROID_PACKAGE_PATTERN.exec(focusedWindow)?.[1];
9193
return {
9294
...(packageName ? { package: packageName } : {}),

src/platforms/install-source.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,9 @@ function isBlockedIpv4(address: string): boolean {
266266
if (octets.length !== 4 || octets.some((part) => Number.isNaN(part) || part < 0 || part > 255)) {
267267
return false;
268268
}
269-
const a = octets[0]!;
270-
const b = octets[1]!;
269+
const a = octets[0];
270+
const b = octets[1];
271+
if (a === undefined || b === undefined) return false;
271272
if (a === 10 || a === 127) return true;
272273
if (a === 169 && b === 254) return true;
273274
if (a === 172 && b >= 16 && b <= 31) return true;
@@ -323,10 +324,11 @@ async function resolveInstallableCandidate(
323324

324325
if (stat.isDirectory()) {
325326
const installables = await collectMatchingPaths(candidatePath, params.isInstallablePath);
326-
if (installables.length === 1) {
327+
const installable = installables[0];
328+
if (installable !== undefined && installables.length === 1) {
327329
return {
328330
archivePath: params.archivePath,
329-
installablePath: installables[0]!,
331+
installablePath: installable,
330332
};
331333
}
332334
if (installables.length > 1) {
@@ -341,8 +343,8 @@ async function resolveInstallableCandidate(
341343
candidatePath,
342344
(entryPath, entryStat) => entryStat.isFile() && isArchivePath(entryPath),
343345
);
344-
if (archives.length === 1) {
345-
const archive = archives[0]!;
346+
const archive = archives[0];
347+
if (archive !== undefined && archives.length === 1) {
346348
if (!params.allowArchiveExtraction) {
347349
throw new AppError(
348350
'INVALID_ARGS',

0 commit comments

Comments
 (0)