From 3635c3a3ee6ecd553eb69b77e1e520efe2e301dc Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 5 Sep 2026 14:07:00 -0700 Subject: [PATCH] perf(repos): avoid quadratic icon source scans --- .../repo-icon-source-href-benchmark.mjs | 38 +++++++++++++ src/main/repo-icon-file-detection.test.ts | 56 ++++++++++++++++++- src/main/repo-icon-file-detection.ts | 10 +--- src/main/repo-icon-source-href.test.ts | 52 +++++++++++++++++ src/main/repo-icon-source-href.ts | 26 +++++++++ 5 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 config/scripts/repo-icon-source-href-benchmark.mjs create mode 100644 src/main/repo-icon-source-href.test.ts create mode 100644 src/main/repo-icon-source-href.ts diff --git a/config/scripts/repo-icon-source-href-benchmark.mjs b/config/scripts/repo-icon-source-href-benchmark.mjs new file mode 100644 index 00000000000..c408e3a84bf --- /dev/null +++ b/config/scripts/repo-icon-source-href-benchmark.mjs @@ -0,0 +1,38 @@ +import assert from 'node:assert/strict' +import { performance } from 'node:perf_hooks' +import { extractIconHref } from '../../src/main/repo-icon-source-href.ts' + +// Original production expressions, preserved for the before/after measurement. +const html = + /]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i +const object = + /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i +const original = (source) => source.match(html)?.[1] ?? source.match(object)?.[1] ?? null + +function measure(fn, source, repetitions) { + const samples = [] + for (let run = 0; run < repetitions; run++) { + const started = performance.now() + fn(source) + samples.push(performance.now() - started) + } + return samples.sort((a, b) => a - b)[Math.floor(samples.length / 2)] +} + +const results = [] +for (const size of [8192, 16384, 32768]) { + for (const shape of ['no icon', 'rel without href']) { + const source = 'a'.repeat(size) + (shape === 'rel without href' ? ' rel:"icon"' : '') + assert.equal(extractIconHref(source), original(source)) + const beforeMs = measure(original, source, 3) + const afterMs = measure(extractIconHref, source, 15) + results.push({ + shape, + bytes: Buffer.byteLength(source), + beforeMs, + afterMs, + speedup: beforeMs / afterMs + }) + } +} +console.log(JSON.stringify({ node: process.version, platform: process.platform, results }, null, 2)) diff --git a/src/main/repo-icon-file-detection.test.ts b/src/main/repo-icon-file-detection.test.ts index 820b2edddfc..19c895e6024 100644 --- a/src/main/repo-icon-file-detection.test.ts +++ b/src/main/repo-icon-file-detection.test.ts @@ -1,4 +1,6 @@ -import { readFile, stat } from 'node:fs/promises' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { mkdtemp, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises' import type * as FsPromisesModule from 'node:fs/promises' import { describe, expect, it, vi } from 'vitest' import type { ExecutionHostFilesystemRoute } from './providers/execution-host-provider-dispatch' @@ -136,3 +138,55 @@ describe('detectRepoFileIcon connection boundary', () => { expect(stat).toHaveBeenCalled() }) }) + +describe('declared repo icons through production filesystem routes', () => { + it.each([ + ['local', false], + ['ssh', false], + ['local', true], + ['ssh', true] + ] as const)('preserves declared icon detection on %s (no icon: %s)', async (kind, noIcon) => { + const directory = await mkdtemp(join(tmpdir(), 'orca-icon-href-')) + const source = noIcon + ? 'a'.repeat(256 * 1024) + : `${'a'.repeat(32768)}{ rel: "icon", href: "/first.png", href: "/chosen.png" }` + try { + await mkdir(join(directory, 'public')) + await writeFile(join(directory, 'index.html'), source) + await writeFile(join(directory, 'public', 'chosen.png'), Buffer.from(PNG_BASE64, 'base64')) + const provider = remoteFilesystemProvider({ + stat: async (path) => { + const info = await stat(path) + return { + type: info.isFile() ? 'file' : 'directory', + size: info.size, + mtime: info.mtimeMs + } + }, + readFile: async (path) => { + const buffer = await readFile(path) + const isBinary = path.endsWith('.png') + return { + content: buffer.toString(isBinary ? 'base64' : 'utf8'), + isBinary, + mimeType: isBinary ? 'image/png' : 'text/html' + } + } + }) + const route: ExecutionHostFilesystemRoute = + kind === 'local' ? { kind: 'local', hostId: 'local' } : sshRoute('icon-oracle', provider) + await expect(detectRepoFileIcon(directory, route)).resolves.toEqual( + noIcon + ? null + : { + type: 'image', + src: `data:image/png;base64,${PNG_BASE64}`, + source: 'file', + label: 'public/chosen.png' + } + ) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) +}) diff --git a/src/main/repo-icon-file-detection.ts b/src/main/repo-icon-file-detection.ts index f4289924b08..831248b94b3 100644 --- a/src/main/repo-icon-file-detection.ts +++ b/src/main/repo-icon-file-detection.ts @@ -3,6 +3,7 @@ import { buildImageDataUri } from '../shared/image-data-uri' import { MAX_REPO_ICON_UPLOAD_BYTES, type RepoIcon } from '../shared/repo-icon' import type { ExecutionHostFilesystemRoute } from './providers/execution-host-provider-dispatch' import type { IFilesystemProvider } from './providers/types' +import { extractIconHref } from './repo-icon-source-href' import { iconHrefCandidates } from './repo-icon-href-candidates' import { joinWorktreeRelativePath } from './runtime/runtime-relative-paths' @@ -49,11 +50,6 @@ const REPO_ICON_SOURCE_FILE_CANDIDATES = [ // not read large app entrypoints just to find a small favicon href. const MAX_REPO_ICON_SOURCE_BYTES = 256 * 1024 -const LINK_ICON_HTML_RE = - /]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i -const LINK_ICON_OBJECT_RE = - /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i - type DetectedImageFormat = { mimeType: 'image/png' | 'image/webp' } @@ -98,10 +94,6 @@ function detectImageFormat(buffer: Buffer): DetectedImageFormat | null { return null } -function extractIconHref(source: string): string | null { - return source.match(LINK_ICON_HTML_RE)?.[1] ?? source.match(LINK_ICON_OBJECT_RE)?.[1] ?? null -} - function repoIconFromImageBuffer(buffer: Buffer, relativePath: string): RepoIcon | null { const format = detectImageFormat(buffer) if (!format) { diff --git a/src/main/repo-icon-source-href.test.ts b/src/main/repo-icon-source-href.test.ts new file mode 100644 index 00000000000..f3991344e59 --- /dev/null +++ b/src/main/repo-icon-source-href.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest' +import { extractIconHref } from './repo-icon-source-href' + +// Original production expressions are the compatibility oracle. +const HTML_RE = + /]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i +const OBJECT_RE = + /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i + +export function originalIconHref(source: string): string | null { + return source.match(HTML_RE)?.[1] ?? source.match(OBJECT_RE)?.[1] ?? null +} + +describe('repo icon source href compatibility', () => { + it.each([ + '', + 'plain source without icon properties', + '{ rel: "icon", href: "/first.png", href: "/last.png" }', + '{ href: "/first.png", rel: "icon", rel: "stylesheet" }', + '{ rel: "icon" } { href: "/unrelated.png" }', + '{ rel: "icon", href: "/first.png" } { rel: "icon", href: "/last.png" }', + '{ rel: "icon", href: "/object.png" } ', + '{ rel: "ICON", href: "/UPPER.png" }', + '}\n\n{href: "/line.png",\nrel : "shortcut icon"}', + '{ rel: "icon", href: "/unterminated}after brace', + '{ rel: "icon", href: "?query" }', + '{ rel: "icon", href: "/before?query" }', + '', + ' { + expect(extractIconHref(source)).toBe(originalIconHref(source)) + }) + + it('matches the original across generated malformed property sequences', () => { + const tokens = ['}', '{', ' ', 'rel:"icon"', 'href:"a"', 'href:"b"', 'rel:"other"', 'x', '\n'] + let seed = 97 + for (let sample = 0; sample < 3000; sample++) { + let source = '' + for (let token = 0; token < 12; token++) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + source += tokens[seed % tokens.length] + } + expect(extractIconHref(source), source).toBe(originalIconHref(source)) + } + }) + + it('handles a maximum-size icon-free entrypoint', () => { + expect(extractIconHref('a'.repeat(256 * 1024))).toBeNull() + }) +}) diff --git a/src/main/repo-icon-source-href.ts b/src/main/repo-icon-source-href.ts new file mode 100644 index 00000000000..d07f5fa61c6 --- /dev/null +++ b/src/main/repo-icon-source-href.ts @@ -0,0 +1,26 @@ +const LINK_ICON_HTML_RE = + /]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i +const LINK_ICON_OBJECT_RE = + /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/iy + +export function extractIconHref(source: string): string | null { + const htmlHref = source.match(LINK_ICON_HTML_RE)?.[1] + if (htmlHref !== undefined) { + return htmlHref + } + let start = 0 + while (start <= source.length) { + // Every suffix before the next closing brace sees the same candidate properties. + LINK_ICON_OBJECT_RE.lastIndex = start + const href = LINK_ICON_OBJECT_RE.exec(source)?.[1] + if (href !== undefined) { + return href + } + const closingBrace = source.indexOf('}', start) + if (closingBrace === -1) { + return null + } + start = closingBrace + 1 + } + return null +}