All files / server/services/tutor/evaluation canonical.ts

87.5% Statements 28/32
68.18% Branches 15/22
100% Functions 8/8
100% Lines 23/23

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      15554x       17573x 17573x 8463x 2323x 2323x   6140x 6140x 6140x 6140x 6140x 6140x 4057x 4057x 4057x 4057x 12641x   12571x 4057x   6140x         194x       1081x       165x    
import { createHash } from "node:crypto";
 
function compareUtf8(left: string, right: string): number {
  return Buffer.compare(Buffer.from(left, "utf8"), Buffer.from(right, "utf8"));
}
 
function serializeCanonical(value: unknown, active: Set<object>): string {
  Iif (value === null) return "null";
  if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
  if (typeof value === "number") {
    Iif (!Number.isFinite(value)) throw new TypeError("Canonical JSON does not allow non-finite numbers");
    return JSON.stringify(value);
  }
  Iif (value === undefined) return "null";
  Iif (typeof value !== "object") throw new TypeError("Canonical JSON accepts only JSON values");
  Iif (active.has(value)) throw new TypeError("Canonical JSON does not allow cyclic values");
  active.add(value);
  try {
    if (Array.isArray(value)) return `[${Array.from(value, (entry) => serializeCanonical(entry, active)).join(",")}]`;
    const prototype = Object.getPrototypeOf(value);
    Iif (prototype !== Object.prototype && prototype !== null) throw new TypeError("Canonical JSON accepts only plain objects");
    const object = value as Record<string, unknown>;
    const entries = Object.keys(object)
      .filter((key) => object[key] !== undefined)
      .sort(compareUtf8)
      .map((key) => `${JSON.stringify(key)}:${serializeCanonical(object[key], active)}`);
    return `{${entries.join(",")}}`;
  } finally {
    active.delete(value);
  }
}
 
export function canonicalJson(value: unknown): string {
  return serializeCanonical(value, new Set());
}
 
export function sha256(value: string | Uint8Array): string {
  return createHash("sha256").update(value).digest("hex");
}
 
export function canonicalDigest(value: unknown): string {
  return sha256(canonicalJson(value));
}