πŸ“„ File detail

cli/ndjsonSafeStringify.ts

🧩 .tsπŸ“ 33 linesπŸ’Ύ 1,408 bytesπŸ“ text
← Back to All Files

🎯 Use case

This file lives under β€œcli/”, which covers the CLI transport, NDJSON/streaming I/O, and command handlers. On the API surface it exposes ndjsonSafeStringify β€” mainly functions, hooks, or classes. It composes internal code from utils (relative imports). What the file header says: JSON.stringify emits U+2028/U+2029 raw (valid per ECMA-404). When the output is a single NDJSON line, any receiver that uses JavaScript line-terminator semantics (ECMA-262 Β§11.3 β€” \n \r U+2028 U+2029) to split the stream will cut the JSON mid-string. ProcessTransport now silently.

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

JSON.stringify emits U+2028/U+2029 raw (valid per ECMA-404). When the output is a single NDJSON line, any receiver that uses JavaScript line-terminator semantics (ECMA-262 Β§11.3 β€” \n \r U+2028 U+2029) to split the stream will cut the JSON mid-string. ProcessTransport now silently skips non-JSON lines rather than crashing (gh-28405), but the truncated fragment is still lost β€” the message is silently dropped. The \uXXXX form is equivalent JSON (parses to the same string) but can never be mistaken for a line terminator by ANY receiver. This is what ES2019's "Subsume JSON" proposal and Node's util.inspect do.

πŸ“€ Exports (heuristic)

  • ndjsonSafeStringify

πŸ–₯️ Source preview

import { jsonStringify } from '../utils/slowOperations.js'

// JSON.stringify emits U+2028/U+2029 raw (valid per ECMA-404). When the
// output is a single NDJSON line, any receiver that uses JavaScript
// line-terminator semantics (ECMA-262 Β§11.3 β€” \n \r U+2028 U+2029) to
// split the stream will cut the JSON mid-string. ProcessTransport now
// silently skips non-JSON lines rather than crashing (gh-28405), but
// the truncated fragment is still lost β€” the message is silently dropped.
//
// The \uXXXX form is equivalent JSON (parses to the same string) but
// can never be mistaken for a line terminator by ANY receiver. This is
// what ES2019's "Subsume JSON" proposal and Node's util.inspect do.
//
// Single regex with alternation: the callback's one dispatch per match
// is cheaper than two full-string scans.
const JS_LINE_TERMINATORS = /\u2028|\u2029/g

function escapeJsLineTerminators(json: string): string {
  return json.replace(JS_LINE_TERMINATORS, c =>
    c === '\u2028' ? '\\u2028' : '\\u2029',
  )
}

/**
 * JSON.stringify for one-message-per-line transports. Escapes U+2028
 * LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR so the serialized output
 * cannot be broken by a line-splitting receiver. Output is still valid
 * JSON and parses to the same value.
 */
export function ndjsonSafeStringify(value: unknown): string {
  return escapeJsLineTerminators(jsonStringify(value))
}