(current: ShellRunRecord, patch: ShellRunPatch)
| 382 | * is what callers use to tell a durable write from a redundant one. |
| 383 | */ |
| 384 | export function nextShellRunRecord(current: ShellRunRecord, patch: ShellRunPatch): ShellRunRecord { |
| 385 | assertShellRunPatch(patch); |
| 386 | const { sessionId, shellRunId } = current; |
| 387 | if (patch.output && patch.output.mode !== current.output.mode) { |
| 388 | throw new Error(`ShellRun output mode is immutable: ${current.output.mode}`); |
| 389 | } |
| 390 | const effectivePatch = |
| 391 | current.observedAt !== undefined && Object.hasOwn(patch, 'observedAt') |
| 392 | ? { ...patch, observedAt: current.observedAt } |
| 393 | : patch; |
| 394 | const candidate = normalizeShellRunRecord( |
| 395 | { ...current, ...effectivePatch, sessionId, shellRunId, revision: current.revision }, |
| 396 | sessionId, |
| 397 | shellRunId, |
| 398 | ); |
| 399 | if (!isValidShellRunStatusTransition(current.status, candidate.status)) { |
| 400 | throw new Error(`Invalid ShellRun status transition: ${current.status} -> ${candidate.status}`); |
| 401 | } |
| 402 | if ( |
| 403 | isTerminalShellRunStatus(current.status) && |
| 404 | (candidate.completedAt !== current.completedAt || |
| 405 | candidate.exitCode !== current.exitCode || |
| 406 | candidate.failureMessage !== current.failureMessage) |
| 407 | ) { |
| 408 | throw new Error(`ShellRun terminal outcome is immutable: ${current.status}`); |
| 409 | } |
| 410 | if (shellRunRecordsEqual(candidate, current)) return current; |
| 411 | return normalizeShellRunRecord( |
| 412 | { ...candidate, revision: current.revision + 1 }, |
| 413 | sessionId, |
| 414 | shellRunId, |
| 415 | ); |
| 416 | } |
| 417 | |
| 418 | /** |
| 419 | * Structural equality for normalized ShellRun records. Records are plain |
no test coverage detected