62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
/**
|
|
* Shallow JSON-record diff utilities for UI summary chips.
|
|
*
|
|
* <p>Lives in `src/lib` (not in a route file) so that callers beyond the
|
|
* record-draft ReviewDrawer can reuse it — RecordHistoryDrawer, audit row
|
|
* summary, future audit-trail tooltips. Pre-emptive DRY: surfaced during
|
|
* the 2026-05-14 eng review of MR !186.
|
|
*
|
|
* <p>Shallow on purpose: ordinis business records are flat-ish at the top
|
|
* level (business fields, no deep object trees that would require a real
|
|
* structural diff). Nested-object changes are reported as a single
|
|
* "changed" entry on the outer key. Good enough for "what's the gist" UI
|
|
* chips. For full schema-level diff with line-by-line highlighting, see
|
|
* `ChangelogDiffModal` + its backend `summary.added/removed/changed`
|
|
* payload.
|
|
*
|
|
* <p>Equality is checked via `JSON.stringify`. Order-sensitive — relies on
|
|
* V8/JSC preserving insertion order for plain objects (true on every
|
|
* modern runtime ordinis ships against). Sufficient for record data which
|
|
* is always object-literal serialized; not safe for Map/Set/Date/RegExp,
|
|
* none of which appear in record payloads.
|
|
*/
|
|
|
|
export type ShallowDiffSummary = {
|
|
added: string[]
|
|
removed: string[]
|
|
changed: string[]
|
|
}
|
|
|
|
/**
|
|
* @returns null when both inputs are null/undefined (e.g. CLOSE op draft
|
|
* with no live record). Caller can skip rendering entirely.
|
|
* Otherwise: per-bucket key lists, never null.
|
|
*/
|
|
export function shallowDiffSummary(
|
|
before: Record<string, unknown> | null | undefined,
|
|
after: Record<string, unknown> | null | undefined,
|
|
): ShallowDiffSummary | null {
|
|
if (!before && !after) return null
|
|
const b = before ?? {}
|
|
const a = after ?? {}
|
|
const added: string[] = []
|
|
const removed: string[] = []
|
|
const changed: string[] = []
|
|
for (const k of Object.keys(a)) {
|
|
if (!(k in b)) {
|
|
added.push(k)
|
|
} else if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) {
|
|
changed.push(k)
|
|
}
|
|
}
|
|
for (const k of Object.keys(b)) {
|
|
if (!(k in a)) removed.push(k)
|
|
}
|
|
return { added, removed, changed }
|
|
}
|
|
|
|
/** Convenience: empty when all three buckets are empty. */
|
|
export function isEmptyDiffSummary(s: ShallowDiffSummary): boolean {
|
|
return s.added.length === 0 && s.removed.length === 0 && s.changed.length === 0
|
|
}
|