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

ai-json-safe-parse

v0.3.0

Published

Parse malformed JSON from AI/LLM responses. Handles markdown code blocks, trailing commas, comments, unquoted keys, and more.

Readme

ai-json-safe-parse

npm version license bundle size

Parse malformed JSON from AI/LLM responses. Zero dependencies. Works in Node.js and browsers.

If you've built anything with ChatGPT, Claude, Gemini, or any other LLM, you've hit this: you ask for JSON and get back something that almost works — wrapped in markdown code blocks, sprinkled with trailing commas, decorated with smart quotes, or buried in a paragraph of explanation. JSON.parse chokes and your app breaks.

ai-json-safe-parse handles all of it. One function call, typed generics, no dependencies.

Install

npm install ai-json-safe-parse

Works with any package manager (npm, yarn, pnpm, bun) and any runtime (Node.js, browsers, Deno, Cloudflare Workers, edge functions).

Quick Start

import { aiJsonParse } from 'ai-json-safe-parse'

// Typical LLM response with markdown code fences
const llmOutput = `Sure! Here's the JSON:

\`\`\`json
{
  "sentiment": "positive",
  "confidence": 0.95,
  "keywords": ["great", "excellent", "loved it"]
}
\`\`\`

Let me know if you need anything else!`

const result = aiJsonParse<{
  sentiment: string
  confidence: number
  keywords: string[]
}>(llmOutput)

if (result.success) {
  console.log(result.data.sentiment) // "positive"
  console.log(result.data.confidence) // 0.95
}

API

The library exports three functions. Pick the one that fits your error-handling style.

aiJsonParse<T>(text, options?) — Result object

Returns a discriminated union: { success: true, data: T } or { success: false, error: string }. Never throws.

import { aiJsonParse } from 'ai-json-safe-parse'

const result = aiJsonParse<{ name: string }>(llmOutput)

if (result.success) {
  console.log(result.data.name)
} else {
  console.error(result.error)
}

aiJsonSafeParse<T>(text, options?) — Nullable

Returns the parsed value or null. The simplest option when you just need the data.

import { aiJsonSafeParse } from 'ai-json-safe-parse'

const data = aiJsonSafeParse<{ name: string }>(llmOutput)
if (data) {
  console.log(data.name)
}

You can also pass a fallback value — if parsing fails, you get the fallback instead of null:

const data = aiJsonSafeParse(llmOutput, { name: 'unknown' })
// data is always { name: string }, never null

aiJsonStrictParse<T>(text, options?) — Throws on failure

Returns the parsed value or throws an Error. Use this when a parse failure should be exceptional.

import { aiJsonStrictParse } from 'ai-json-safe-parse'

const data = aiJsonStrictParse<{ name: string }>(llmOutput)
console.log(data.name)

What It Fixes

The parser applies recovery strategies in order, from safest to most aggressive:

| Strategy | Mode | What it handles | |---|---|---| | Direct JSON.parse | both | Already-valid JSON | | Markdown extraction | both | ```json ... ```, ```jsonc ... ```, ```js ... ```, and bare code blocks | | Unicode normalization | both | UTF-8 BOM, zero-width chars, smart quotes \u201C\u201D\u2018\u2019, em/en dashes \u2014\u2013, non-breaking spaces, ellipsis \u2026 | | Bracket matching | both | JSON embedded in prose: "Here is the result: {...} hope that helps!" | | Comment removal | aggressive | // line comments and /* block comments */ (preserves URLs) | | Trailing comma removal | aggressive | {"a": 1,} and [1, 2,] | | Single quote replacement | aggressive | {'name': 'Alice'} | | Unquoted key fix | aggressive | {name: "Alice"} | | Regex key-value extraction | aggressive | Severely broken JSON (last resort) |

Real-world examples this handles

// Markdown code blocks
aiJsonParse('```json\n{"key": "value"}\n```')

// JSON buried in a paragraph
aiJsonParse('The analysis shows {"score": 95, "grade": "A"} for this input.')

// Trailing commas (extremely common from LLMs)
aiJsonParse('{"items": ["a", "b", "c",],}')

// JavaScript-style objects with comments
aiJsonParse(`{
  name: "Alice",       // the user
  age: 30,             // in years
  active: true,
}`)

// Smart quotes from copy-paste or GPT
aiJsonParse('{\u201Cname\u201D: \u201CAlice\u201D}')

// Em dashes in content
aiJsonParse('{"range": "10\u201420"}')  // → {"range": "10-20"}

Options

interface ParseOptions {
  /**
   * How aggressively to attempt repairs on malformed JSON.
   * Default: 'aggressive'
   */
  mode?: 'safe' | 'aggressive'
}

safe — Tries direct parse, markdown extraction, unicode normalization, and bracket matching only. Use this when data integrity is more important than recovery rate. Won't modify the JSON structure.

aggressive (default) — Additionally fixes syntax issues and falls back to regex extraction. Higher recovery rate, but may misinterpret severely malformed input.

// Only use safe recovery strategies
const result = aiJsonParse(text, { mode: 'safe' })

// Use all strategies (default)
const result = aiJsonParse(text, { mode: 'aggressive' })

TypeScript

Full generic type support. All exports are fully typed.

import { aiJsonParse, aiJsonSafeParse, aiJsonStrictParse } from 'ai-json-safe-parse'
import type { ParseResult, ParseSuccess, ParseFailure, ParseOptions } from 'ai-json-safe-parse'

interface MyResponse {
  summary: string
  score: number
  tags: string[]
}

// All three functions accept a generic type parameter
const result = aiJsonParse<MyResponse>(text)       // ParseResult<MyResponse>
const data = aiJsonSafeParse<MyResponse>(text)     // MyResponse | null
const strict = aiJsonStrictParse<MyResponse>(text) // MyResponse (throws on failure)

Bundle Size

Zero dependencies. Tree-shakeable ESM. The entire library is ~5KB unminified (~2KB gzipped).

Both ESM (import) and CommonJS (require) are supported via the exports field in package.json.

Background

This library was extracted from the production codebase at LeadTruffle, where we use it to parse thousands of AI-generated JSON responses daily across our lead qualification, SMS automation, and voice AI pipelines. We run it in AWS Lambda (Node.js), Cloudflare Workers, and in-browser — so it had to be lightweight, dependency-free, and universal from day one.

After copy-pasting variations of this code across multiple services, it made sense to clean it up and share it. Thank you to LeadTruffle for supporting the open-source release.

Testing

The test suite covers valid JSON, markdown extraction, unicode edge cases, bracket matching, all aggressive-mode fixes, safe mode, real-world LLM outputs, and error cases.

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Type check
npm run lint

Tests use Vitest. To run them locally:

git clone https://github.com/a-r-d/ai-json-safe-parse.git
cd ai-json-safe-parse
npm install
npm test

Contributing

Contributions are welcome! If you've hit an LLM output format that this library doesn't handle, please open an issue with the raw text — that's the most helpful thing you can provide.

To contribute code:

  1. Fork the repo and create a branch
  2. Add a failing test for the case you're fixing
  3. Implement the fix
  4. Run npm test to make sure everything passes
  5. Run npm run lint to check types
  6. Open a PR

Please keep PRs focused — one fix or feature per PR.

Author

Aaron Deckerard.ninja / @a-r-d

CTO at LeadTruffle, a speed-to-lead platform for home service businesses. I've been a software engineer for 15+ years. After writing the same JSON-from-LLM parsing code across too many services, I decided to open source it. If you're building AI-powered products and burning time on malformed JSON, I hope this saves you some headaches.

License

MIT