All files / src/lib/db json.ts

100% Statements 11/11
100% Branches 8/8
100% Functions 1/1
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54                        162x                                           1837x 2x       1835x 1835x   3x 3x     1832x 3x 3x     1829x    
/**
 * Safe JSON parsing for values read from the SQLite cache layer.
 *
 * Cached JSON columns can be corrupted by partial writes, manual DB edits, or
 * incompatible schema migrations. A bare `JSON.parse` on such data throws a
 * `SyntaxError` that crashes whatever command triggered the cache read. This
 * module centralizes the defensive parse so every cache reader treats
 * corruption as a cache miss instead of a fatal error.
 */
 
import { logger } from "../logger.js";
 
const log = logger.withTag("db-json");
 
/**
 * Parse a JSON string read from the cache, returning `undefined` on failure.
 *
 * Failure modes handled:
 * - `raw` is `null`/`undefined` (column was never written) → `undefined`.
 * - `JSON.parse` throws (corrupt JSON) → logged at debug level → `undefined`.
 * - An optional `validate` predicate rejects the parsed shape → `undefined`.
 *
 * Callers should treat `undefined` as a cache miss and recompute the value.
 *
 * @typeParam T - Expected shape of the parsed value.
 * @param raw - Raw JSON string from a SQLite column (may be null/undefined).
 * @param validate - Optional type guard run against the parsed value; when it
 *   returns `false` the result is discarded and `undefined` is returned.
 * @returns The parsed value, or `undefined` if parsing/validation failed.
 */
export function safeParseJson<T>(
  raw: string | null | undefined,
  validate?: (value: unknown) => value is T
): T | undefined {
  if (raw === null || raw === undefined) {
    return;
  }
 
  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch (error) {
    log.debug("Failed to parse cached JSON; treating as cache miss", error);
    return;
  }
 
  if (validate && !validate(parsed)) {
    log.debug("Cached JSON failed validation; treating as cache miss");
    return;
  }
 
  return parsed as T;
}