npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

json-mending

v0.1.1

Published

Repair malformed JSON from LLM outputs: unterminated strings, trailing commas, markdown fences, and more.

Readme

json-mending

Repair malformed JSON from LLM outputs. Zero dependencies. Never throws (unless you ask it to). Every fix it makes is recorded, so you can see exactly what changed.

import { repairJson } from 'json-mending';

repairJson('{"a": 1,}');
// { ok: true, value: { a: 1 }, repairs: [{ type: 'removed-trailing-comma', position: 7 }] }

Why

LLMs asked for JSON don't always produce valid JSON. Output gets cut off mid-token when it hits max_tokens. Chat-tuned models wrap it in ```json fences even when told not to. Models trained on lots of JavaScript write trailing commas and single-quoted strings without thinking twice. JSON.parse rejects all of it outright, so most LLM pipelines end up with a hand-rolled try/catch and a pile of regexes.

json-mending tokenizes and parses the input properly instead of pattern-matching text, so it can repair structure — missing commas, unclosed brackets, a truncated value — rather than just cleaning up surface syntax.

Install

npm install json-mending

Usage

import { repairJson, repairJsonOrThrow } from 'json-mending';

// repairJson never throws — check `ok` instead
const result = repairJson('```json\n{name: \'Ada\', "tags": [1, 2,]\n```');
if (result.ok) {
  console.log(result.value); // { name: 'Ada', tags: [1, 2] }
  console.log(result.repairs); // every fix that was applied, in order
}

// repairJsonOrThrow throws on truly unparseable input, like JSON.parse
const value = repairJsonOrThrow('{"a": 1,}'); // { a: 1 }

Options

repairJson(input, {
  // How to handle an object member whose value was cut off entirely,
  // e.g. `{"a":1,"b":` at EOF with nothing after the colon.
  onTruncation: 'drop-partial', // (default) discard the dangling key
  // onTruncation: 'keep-partial', // keep it with a `null` value instead
});

What it handles

  • Markdown code fences (with or without a json language tag)
  • Leading prose before the JSON value ("Sure, here's the JSON you asked for: ...")
  • Unterminated strings — cut off at end of input, or at a raw unescaped newline (which is never valid inside a JSON string, so anything after it is treated as separate structure, not more string content)
  • Trailing, leading, and doubled commas
  • Missing commas between object members or array elements
  • Unclosed objects/arrays at end of input, including nested ones
  • Single-quoted strings and unquoted object keys ({name: 'Ada'})
  • Unescaped quotes embedded inside a string value ("The TV is 72" wide") — a candidate closing quote is only trusted as the real end if what follows looks like the start of the next token; otherwise it's treated as content
  • Truncated keywords at end of input (tru → true)
  • Leading-dot numbers (.5 → 0.5)
  • Invalid numbers — dropped rather than fabricated, and logged
  • Stray tokens anywhere in the middle of a structure — a bad token is skipped, not treated as the end of the array or object

Every one of these is backed by a fixture in src/__fixtures__/corpus.ts and exercised by property-based fuzz tests in src/fuzz/ that generate random JSON, corrupt it, and check the exact original value comes back out.

What it doesn't handle (yet)

  • Python-style literals (True, False, None) — only lowercase JSON keywords are recognized
  • Comments (//, /* */) inside the JSON
  • NaN / Infinity as literal tokens

None of these are structural — patches are welcome.

API

repairJson(input, options?) => RepairResult

type RepairResult =
  | { ok: true; value: unknown; repairs: RepairLog[] }
  | { ok: false; error: string; repairs: RepairLog[] };

Valid JSON takes a fast path straight through JSON.parse with zero tokenizing overhead and an empty repairs array. Only malformed input pays for the lenient tokenizer/parser.

repairJsonOrThrow(input, options?) => unknown

Same repair behavior as repairJson, but returns the value directly and throws an Error instead of returning { ok: false }.

RepairLog

A discriminated union describing exactly what was fixed and where (as a character offset into the input), e.g. { type: 'removed-trailing-comma', position: 8 }. See src/types.ts for the full list of repair kinds.

Status

Covers the common structural failure modes seen in real LLM output, not every conceivable malformed input. See the gaps above.