All files / web/src/lib/abacus design-name.ts

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

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 312x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 48x 39x 14x 39x 48x 48x  
/**
 * A design's name (Gitea #11) — deliberately its own module, not part of
 * `design-snapshot.ts`.
 *
 * The snapshot module defines the envelope that gets hashed into the design's
 * id. A name must stay OUT of that hash: ids are content addresses, so if a
 * name were content, renaming would fork the design and orphan every
 * `?design=` link already printed on a THH job card. Keeping the normalizer in
 * a separate file is that boundary, made visible.
 */
 
/** Long enough for "Ada's classroom abacus", short enough to stay one rail
 *  line. The rename input caps at the same number. */
export const DESIGN_NAME_MAX = 60
 
/** Normalize a caller-supplied name.
 *
 *  Returns `null` for "no name" — absent, JSON null, or blank after trimming,
 *  since an empty string is never stored and clearing is how a name is taken
 *  back off. Returns `undefined` for input that is not a usable name at all:
 *  not a string, or longer than the cap. Callers decide whether that is a
 *  refusal (PATCH, where naming IS the request) or something to ignore (POST,
 *  where the snapshot is the request and the name is a passenger). */
export function normalizeDesignName(value: unknown): string | null | undefined {
  if (value === null || value === undefined) return null
  if (typeof value !== 'string') return undefined
  const trimmed = value.trim()
  if (trimmed.length > DESIGN_NAME_MAX) return undefined
  return trimmed.length > 0 ? trimmed : null
}