JSON Validator — JavaScript source
Validate JSON and pinpoint errors with line and column numbers. Clear valid/invalid verdict plus the exact error location - runs entirely in your browser.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// json-validator - JavaScript polyglot showcase port
// Language: JavaScript (ES module)
// CosmoDev polyglot showcase port of the json-validator tool.
// Ported from src/lib/jsonValidate.ts (canonical TypeScript logic).
//
// This is display source - part of CosmoDev's polyglot tool pages (dev.cosmolabs.org).
//
// Pure JSON validation logic. No DOM, no I/O. Deterministic and never throws.
//
// The host's JSON engine embeds the error offset as "at position N" in its
// SyntaxError message (V8/Node, Chrome, Firefox). JavaScriptCore (bun, Safari)
// does not. Position recovery is therefore best-effort: when the offset is
// present it is converted to a 1-based line/column, otherwise both fall back
// to null.
/**
* Extract the 0-based char offset from a SyntaxError message ("at position N").
* Returns null when no offset is reported.
*/
export function extractOffset(message) {
const match = /at position (\d+)/.exec(message);
return match ? Number(match[1]) : null;
}
/** Coerce a caught value into a human-readable message. */
export function errorMessage(value) {
return value instanceof Error ? value.message : String(value);
}
/**
* Normalize an engine error message for display.
* Strips JavaScriptCore's "JSON Parse error: " prefix (case-insensitive).
*/
export function normalizeError(message) {
return message.replace(/^JSON Parse error:\s*/i, '');
}
/**
* Convert a 0-based char offset into a 1-based { line, column }.
* Counts "\n", a lone "\r", and "\r\n" each as a single line break.
*/
export function offsetToLineCol(text, offset) {
let line = 1;
let column = 1;
const max = Math.min(offset, text.length);
for (let i = 0; i < max; i++) {
const ch = text[i];
if (ch === '\n') {
line++;
column = 1;
} else if (ch === '\r') {
line++;
column = 1;
if (text[i + 1] === '\n') i++; // treat CRLF as a single break
} else {
column++;
}
}
return { line, column };
}
/** Map a parse error message to { line, column } (or nulls) using the input text. */
export function locateError(text, message) {
const offset = extractOffset(message);
return offset === null ? { line: null, column: null } : offsetToLineCol(text, offset);
}
/**
* Validate a JSON string. Never throws.
* - empty / whitespace-only / non-string → invalid, "Input is empty", no location
* - valid JSON → valid: true
* - invalid JSON → valid: false, cleaned error, and a
* 1-based line/column when the engine
* reports one
*/
export function validateJson(text) {
if (typeof text !== 'string' || text.trim().length === 0) {
return { valid: false, error: 'Input is empty', line: null, column: null };
}
try {
JSON.parse(text);
return { valid: true, error: null, line: null, column: null };
} catch (e) {
const raw = errorMessage(e);
return { valid: false, error: normalizeError(raw), ...locateError(text, raw) };
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →