UNPKG

ensure-json

Version:

A <3kB, dependency-free helper that repairs 'almost-JSON' text from LLMs and returns a valid JavaScript object—or throws JsonFixError.

87 lines (84 loc) 2.38 kB
#!/usr/bin/env node // src/ensureJson.ts var JsonFixError = class extends Error { constructor(msg, raw) { super(msg); this.name = "JsonFixError"; this.raw = raw; } }; function ensureJson(raw, schema) { let parsed; try { parsed = JSON.parse(raw); } catch { parsed = tryRepair(raw); } if (schema) { const result = schema.safeParse(parsed); if (!result.success) { throw new JsonFixError("Schema validation failed", raw); } return result.data; } return parsed; } function tryRepair(raw) { let text = raw; text = text.replace(/^\s*```(?:json)?|```\s*$/gim, ""); const firstBrace = Math.min( ...["{", "["].map((c) => { const i = text.indexOf(c); return i === -1 ? Infinity : i; }) ); if (firstBrace !== Infinity) text = text.slice(firstBrace); text = text.replace(/,\s*([}\]])/g, "$1"); text = text.replace(/'([^']+)':/g, (_, k) => `"${k}":`); text = text.replace(/([{,]\s*)([a-zA-Z0-9_]+)\s*:/g, '$1"$2":'); const openBraces = (text.match(/{/g) || []).length; const closeBraces = (text.match(/}/g) || []).length; if (openBraces === closeBraces + 1) text += "}"; const openBrackets = (text.match(/\[/g) || []).length; const closeBrackets = (text.match(/]/g) || []).length; if (openBrackets === closeBrackets + 1) text += "]"; try { return JSON.parse(text); } catch (err) { throw new JsonFixError("Failed to repair and parse JSON", raw); } } // src/cli.ts function readStdin() { return new Promise((resolve, reject) => { let data = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (chunk) => data += chunk); process.stdin.on("end", () => resolve(data)); process.stdin.on("error", reject); }); } async function main() { if (process.argv.includes("--help")) { console.log(`Usage: json-fix [--help] Reads JSON from stdin, repairs it, and writes valid JSON to stdout. Exits 0 on success, 1 on failure.`); process.exit(0); } try { const input = await readStdin(); const result = ensureJson(input); process.stdout.write(JSON.stringify(result, null, 2) + "\n"); process.exit(0); } catch (err) { if (err instanceof JsonFixError) { process.stderr.write(`JsonFixError: ${err.message} `); } else { process.stderr.write(`Error: ${String(err)} `); } process.exit(1); } } main();