Skip to content

refactor(frontend): seal readonly leaks in app-types consumers (#1596)#1598

Merged
adamflagg merged 1 commit into
mainfrom
feature/app-types-readonly-1596
May 21, 2026
Merged

refactor(frontend): seal readonly leaks in app-types consumers (#1596)#1598
adamflagg merged 1 commit into
mainfrom
feature/app-types-readonly-1596

Conversation

@adamflagg
Copy link
Copy Markdown
Owner

@adamflagg adamflagg commented May 21, 2026

Closes #1596.

Finishes the app-types readonly contract opened by #1595 — same latent readonly-leak class as the graph-type follow-ups #1593/#1594 (shipped in #1597). Two consumers were intentionally left out of #1595 to keep that a pure type-only change.

Changes

  1. AllCampersView.tsx — drop the now-redundant as Camper[] cast. refactor(frontend): readonly on app-types cache/API shapes #1595 widened buildCamperRows to accept readonly Camper[] (csvExportHelpers.ts:73), and filteredCampers is MergedCamper[] (MergedCamper extends Camper), so it's directly assignable. The standing cast only suppressed future compiler signal if the contract changed.

  2. mergeMultiSessionCampers.ts — mark AdditionalSession's fields and MergedCamper.additionalSessions readonly, matching the now-readonly Camper that MergedCamper extends. The builder constructs each AdditionalSession via .map() into fresh objects, so the element type going readonly is a clean change.

Pure type-only hardening — no runtime behavior change, no active mutation existed.

Tests

Adds the first tests for mergeMultiSessionCampers (previously untested):

  • Behavioral coverage — multi-enrollment merge produces one entry with additionalSessions; single-session camper untouched.
  • Readonly contract@ts-expect-error assertions enforced by tsc --noEmit. Verified red→green: before the change the directives are unused (TS2578); after, they type-check.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added comprehensive test suite for multi-session camper merging functionality, including validation of camper data consolidation and immutability constraints.
  • Refactor

    • Enhanced type safety in CSV export and camper data handling to prevent potential bugs and ensure data integrity during export operations.

Review Change Stack

Finish the app-types readonly contract opened by #1595, same class as the
graph-type follow-ups #1593/#1594 (#1597):

- AllCampersView: drop the now-redundant `as Camper[]` cast — #1595 widened
  buildCamperRows to accept `readonly Camper[]`, and `MergedCamper[]` is
  directly assignable, so the cast only suppressed future compiler signal.
- mergeMultiSessionCampers: mark `AdditionalSession` fields and
  `MergedCamper.additionalSessions` `readonly` to match the now-`readonly`
  `Camper` it extends. The builder maps fresh objects, so the element type
  going readonly is a clean change.

Pure type-only hardening — no runtime behavior change. Adds the first tests
for mergeMultiSessionCampers (behavioral merge coverage + a tsc-enforced
readonly contract via @ts-expect-error).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42e84ea9-c759-4469-8698-428c9f1f78f5

📥 Commits

Reviewing files that changed from the base of the PR and between 651453b and 839f549.

📒 Files selected for processing (3)
  • frontend/src/components/AllCampersView.tsx
  • frontend/src/utils/mergeMultiSessionCampers.test.ts
  • frontend/src/utils/mergeMultiSessionCampers.ts

📝 Walkthrough

Walkthrough

This PR completes the readonly immutability contract for MergedCamper and its AdditionalSession fields. Type contracts are tightened to enforce readonly semantics, comprehensive tests validate both runtime merge behavior and compile-time type safety, and a now-redundant type cast is removed from the CSV export path.

Changes

Readonly contract sealing

Layer / File(s) Summary
Type contracts for immutable merged campers
frontend/src/utils/mergeMultiSessionCampers.ts
AdditionalSession fields now marked readonly with explicit | undefined typing for optional properties. MergedCamper.additionalSessions updated from mutable to readonly AdditionalSession[] | undefined to seal the immutability hole in the readonly Camper base.
Test suite for merge behavior and readonly contract
frontend/src/utils/mergeMultiSessionCampers.test.ts
Fixture builders and two behavioral tests validate multi-session collapse and single-session preservation. Type-level contract assertions using @ts-expect-error directives enforce readonly immutability at compile time via tsc --noEmit.
Remove redundant cast in CSV export
frontend/src/components/AllCampersView.tsx
CSV export handler drops the as Camper[] cast, passing filteredCampers directly to buildCamperRows. Change is safe because MergedCamper[] is assignable to the widened readonly Camper[] contract.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Assessment against linked issues

Objective Addressed Explanation
Remove unnecessary as Camper[] cast in AllCampersView [#1596]
Mark additionalSessions and AdditionalSession fields as readonly to match readonly Camper base [#1596]
Validate readonly contract enforcement at compile time [#1596] @ts-expect-error assertions in test suite ensure TypeScript rejects attempts to mutate readonly fields.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/app-types-readonly-1596

Comment @coderabbitai help to get the list of available commands and usage tips.

@adamflagg
Copy link
Copy Markdown
Owner Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@adamflagg adamflagg merged commit 4fb9f40 into main May 21, 2026
23 checks passed
@adamflagg adamflagg deleted the feature/app-types-readonly-1596 branch May 21, 2026 22:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(frontend): seal readonly leaks in app-types consumers (AllCampersView cast + MergedCamper)

1 participant