Senior Frontend Engineer at Horsefly Analytics, Manchester. I build production TypeScript and React for a labour-market analytics platform, and I work on the AWS and AI-agent infrastructure behind it.
Most of what I enjoy is the unglamorous half: parser edge cases, streaming protocols, the failure modes that only show up under real input.
I contribute fixes upstream to packages the JavaScript ecosystem depends on. Each one below is a bug I reproduced, traced to a root cause, and verified against a differential build before opening.
Merged and released
benjamn/recast#1438— comment tokens sit inloc.tokensfor the babel, TypeScript and flow parsers but not for esprima or acorn, so a comment between a node and its enclosing parenthesis hid the parenthesis fromhasParens()and the printer emitted a second pair. This broke recast's own documentedprint(parse(source)) === sourceidentity on unmodified source. Verified across 5,076 generated inputs: 2,540 identity violations fixed, zero regressions. Shipped in0.23.20— a package with ~139M downloads a month.postcss/postcss-selector-parser#330— unclosed[,(and a trailing|threw a rawTypeErrorinstead of the parser's own error. Shipped in7.1.5— ~574M downloads a month.corsairdev/corsair#111— improved the Telegram integration plugin.
In review
shadcn-ui/ui#11463— the Tailwind prefix transform rebuilt class literals as quoted source text by hand, so the step that stripped the delimiters also stripped every quote inside the class value.[stroke='#fff']became[stroke=#fff], which is not valid CSS, so the browser silently discarded the rule. Found by reading the CLI source rather than from a bug report. Verified over all 1,435 registry components: 1,404 byte-identical, zero regressions.postcss/postcss-selector-parser#335— attribute selectors with no valid attribute name threw a rawTypeError, or emitted the literal stringundefinedinto CSS. Verified against 43,200 generated selectors.postcss/postcss-selector-parser#336—$dropped from attribute names, breaking Sass interpolation like[#{$attr}].postcss/postcss-selector-parser#337— lossless mode dropped trailing whitespace when a selector ended before any node was created.- Fixes also pending review in
hast-util-from-parse5,eslint-plugin-import,stacktrace-parser,xml-jsandeslint-plugin-react-native.
- llm-guard — validating and securing LLM prompts, in TypeScript.
- bedrock-ui-stream — bridges AWS Bedrock Agent Runtime event streams to the Vercel AI SDK UI message stream protocol. Handles the parts that bite: chunk boundaries splitting multi-byte characters, partial tool-call state, and redaction on the error path.
- MSc Computer Science, Manchester Metropolitan University
- AWS Certified Solutions Architect
- Cyber Runway Launch 2024 — the DSIT-funded national cyber accelerator delivered by Plexal
- Volunteer mentor, Manchester Metropolitan University
TypeScript · JavaScript · React · Next.js · Node.js · AWS · PostgreSQL · Docker · Terraform




