Skip to content

refactor: replace hand-rolled parsers with standard packages#373

Open
latekvo wants to merge 5 commits into
mainfrom
fix/replace-hand-rolled-parsers
Open

refactor: replace hand-rolled parsers with standard packages#373
latekvo wants to merge 5 commits into
mainfrom
fix/replace-hand-rolled-parsers

Conversation

@latekvo

@latekvo latekvo commented Jun 18, 2026

Copy link
Copy Markdown
Member

What & why

Swept the repo for hand-rolled regex/string parsers that reimplement a solved problem, and replaced the genuinely fragile ones with battle-tested packages (or deps already in the tree). Each swap is paired with a regression test for an input the old code got wrong.

Site Was Now
adb.ts resolveAvdPath, linux-preflight.ts readMb per-key anchored regex over AVD .ini / config.ini ini
workspace-reader.ts extractEnvKeys split-on-first-= line loop dotenv (parse)
uninstall.ts readBundledSkillName ^name:(.+)$ capture + outer-quote strip yaml (already a dep)
mcp-configs.ts escapeRegExp hand-rolled char-class escape escape-string-regexp
export.ts xctrace version, session-context.ts RN minor match(/(\d+)\./) / split(".")[1] semver coerce (already a dep)
source-resolver.ts parseDebugStack at fn (file:l:c) regex (required both line+col) stacktrace-parser

Bugs these fix (covered by new tests)

  • ini: a quoted path = "/Users/My Name/.../X.avd" kept its quotes, failed the startsWith("/") guard → AVD unresolved. Inline comments / trailing junk after a sizing value made the anchored regex return null → sizing warning silently lost.
  • dotenv: export FOO=bar produced the key "export FOO" (dropped by the identifier filter); a multi-line quoted value leaked its continuation lines (FAKE=...) as phantom keys.
  • yaml: a name: value with a trailing # comment was taken verbatim (comment included); odd quoting only had one outer pair stripped.
  • stacktrace-parser: a :line-only frame (no column, as Hermes/JSC can emit) became an unusable unknown frame.

Deliberately left alone (not "flaky", or no safe swap)

To avoid churn and regressions, I did not touch:

  • uiautomator-parser.ts hand-rolled XML parser — a deliberate, documented choice with a dedicated hardening suite, including a leniency test for malformed dumps (stray </node>). A spec XML parser would reject inputs this is built to recover from.
  • ios-profiler/pipeline/xml-parser.ts (xctrace XML) — the most tangled parser, but it has no real .trace fixtures and only thin entity-decode tests. A rewrite can't be proven equivalent without device-generated traces, so swapping it blind risks silently breaking iOS CPU/hang/leak reports. Best done separately with a captured fixture.
  • http.ts extractHostname — DNS-rebinding guard; URL.hostname keeps the IPv6 brackets the loopback check compares without, so the "fix" isn't simpler and the path is security-sensitive.
  • adb.ts shellQuote — the POSIX single-quote escape is the standard correct form.
  • parseAdbDevices, android-screen.ts wm size, detect-app.ts dumpsys — tool-specific CLI-output scraping with no clean package equivalent (and documented edge-case handling).
  • workspace-reader.ts extractMetroPort — a best-effort heuristic over JS config; a correct replacement needs an AST parse, out of scope here.
  • config-parse.ts Yarn/npm age-gate parsing — a precise, unit-tested domain parser; ms would introduce a bare-number ambiguity.

Verification

  • tsc --build clean across the workspace graph.
  • Tests: tool-server 1205, installer 264, update-core 31 — all pass (incl. the new regression tests).
  • prettier --check clean on all changed files.
  • Production esbuild bundle (bundle-tools.cjs) emits a valid tool-server.cjs/mcp-server.mjs with ini/dotenv/stacktrace-parser inlined (node --check OK). (The script's final step errored only on the auth-gated trace-processor.wasm download, unrelated to bundling.)

New runtime deps: ini, dotenv, stacktrace-parser (tool-server), escape-string-regexp (installer) — all small, pure-JS. Lockfile change is purely additive.

latekvo added 5 commits June 18, 2026 17:57
Several utilities hand-rolled parsing that battle-tested packages (or
deps already in the tree) do more reliably. Replaced the fragile cases:

- adb AVD `.ini` + linux-preflight `config.ini`  -> `ini`
- workspace-reader `.env` key extraction         -> `dotenv`
- installer SKILL.md frontmatter `name:`         -> `yaml` (already a dep)
- installer escapeRegExp helper                  -> `escape-string-regexp`
- xctrace / React Native version parsing         -> `semver` (already a dep)
- React `_debugStack` frame parsing              -> `stacktrace-parser`

Added regression tests for inputs the old code mishandled: quoted/
commented ini values, `export`-prefixed and multi-line .env entries,
a frontmatter name with a trailing YAML comment, and line-only stack
frames.
…ed-parsers

# Conflicts:
#	package-lock.json
#	packages/argent-installer/package.json
#	packages/tool-server/package.json
A second full-codebase pass (covering the newly-merged Argent Lens / profiler
code) surfaced four more genuine reimplementations:

- registry zod→JSON-Schema converter -> Zod 4's native `z.toJSONSchema`
  (io:"input"). The hand-rolled walk silently dropped every `.describe()` text
  and `.min/.max/.int/.regex` constraint and emitted `{}` for unions — so the
  schema the model sees for every tool was lossy.
- four MB-capped `formatBytes` helpers -> `bytes` (base-1024). Leak/asset
  totals above 1 GB rendered as e.g. "2048.0 MB" instead of "2 GB".
- react-profiler `isWrappedInMemo` raw-source regex -> a tree-sitter pass
  reusing the in-file `reactWrapperCall` (the regex matched `memo(...)` inside
  comments/strings and ignored AST scope).
- network-interceptor detail script: hand-escaped `requestId` -> `JSON.stringify`
  (the previous escaper injected a raw newline and crashed the runtime parse);
  inspect-at-point aligned to the same.

Adds `bytes` (+ @types/bytes). Tests updated/added to cover the new behavior.
…ed-parsers

# Conflicts:
#	packages/tool-server/src/utils/ios-profiler/export.ts
@latekvo latekvo marked this pull request as ready for review June 19, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant