Skip to content

feat: first-class synthetic columns (#391) - #406

Merged
rickcedwhat merged 4 commits into
mainfrom
feat/391-synthetic-columns
Aug 1, 2026
Merged

feat: first-class synthetic columns (#391)#406
rickcedwhat merged 4 commits into
mainfrom
feat/391-synthetic-columns

Conversation

@rickcedwhat-ai

@rickcedwhat-ai rickcedwhat-ai commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Closes #391

Summary

Adds synthetic columns — computed columns with no DOM presence. Values are derived from other columns at runtime (e.g. Total = Price × Qty).

  • New syntheticColumns config option on useTable()
  • New row.getValue(col) universal accessor — works for real, override, and synthetic columns
  • toJSON() includes synthetic columns (respects columns option)
  • findRow() / findRows() / countRows() filter by synthetic values via per-row evaluation
  • getCell() and smartFill() throw clear errors for synthetic columns (no DOM cell)
  • Collision validation rejects synthetic names that match real headers
  • No-chaining enforcement: a synthetic's compute() cannot read another synthetic

Bail-out point: The branch forks cleanly from d80637f — if this feature proves to be a bad idea, revert the merge commit.

Files changed

File Change
src/types.ts SyntheticColumnDef type, syntheticColumns on config, getValue on SmartRow
src/smartRow.ts getValue() impl, toJSON() synthetic append, getCell/smartFill guards
src/engine/rowFinder.ts splitFilters 3-way split, matchesSyntheticFilters post-filter
src/engine/tableMapper.ts Collision validation at init
src/useTable.ts countRows synthetic support, getRow guard, getHeaders includes synthetics
src/index.ts Export new types

Test plan

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for computed synthetic columns in table configuration.
    • Synthetic values can be read, serialized, included in headers, and used for row filtering and counting.
    • Added SmartRow.getValue() for retrieving resolved column values.
    • Exported public types for synthetic columns and column overrides.
  • Bug Fixes
    • Added validation to prevent synthetic columns from conflicting with real headers.
    • Improved handling and error messages for unsupported synthetic-column operations.
  • Tests
    • Added coverage for synthetic-column computation, filtering, serialization, and row operations.

rickcedwhat-ai and others added 2 commits July 31, 2026 19:54
…esence (#391)

Adds `syntheticColumns` config for columns whose values are computed from
other columns at runtime (e.g. Total = Price × Qty). Synthetic columns
appear in toJSON(), getValue(), findRow/findRows, and countRows filters,
but throw clear errors from getCell() and smartFill() since they have no
DOM cell.

New API surface:
- `syntheticColumns` config option on useTable()
- `row.getValue(col)` — universal accessor for real, override, and synthetic columns
- `SyntheticColumnDef` type exported from index

Implementation:
- Synthetics stay out of the header map (no sentinel indices)
- splitFilters() extended with a third bucket for synthetic filters
- matchesSyntheticFilters() post-evaluates candidates via compute()
- No-chaining enforcement prevents one synthetic from reading another
- Collision validation at init rejects names that match real headers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rickcedwhat-ai

rickcedwhat-ai commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Bot HQ

🔗 Issue Link

🔗 Closes #391


🔍 AI Review

  • 🔍 Request AI review

✅ Review complete — no blocking issues.

📊 This PR: $0.0537 · Repo today: $0.05 / $1.00 · Month: $0.05 / $10.00

Review History (1 round)
Round Date Commit In Tokens Out Tokens Cost Summary
1 2026-08-01 e8591fc 53,113 112 $0.0537 No issues found

This comment is managed by the bot — do not edit directly.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1fba17dd-eae7-4da5-98b6-ffb4e1f9d5d0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-01 19:40 UTC

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
src/engine/rowFinder.ts (1)

93-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract a shared override/synthetic post-filter matcher. src/engine/rowFinder.ts and src/useTable.ts each implement their own copy of the override+synthetic filter matching logic. The duplication already caused the two copies to diverge in how often they instantiate a SmartRow per row.

  • src/engine/rowFinder.ts#L93-L107: keep matchesSyntheticFilters/matchesOverrideFilters here as the canonical implementation, or move them into a shared exported helper.
  • src/useTable.ts#L356-L390: replace the inline override+synthetic matching loop in countPostFilterMatches with the shared helper from rowFinder.ts, instead of re-implementing the same logic with a different SmartRow instantiation pattern.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engine/rowFinder.ts` around lines 93 - 107, The override and synthetic
post-filter matching logic is duplicated and creates SmartRow instances
inconsistently. Keep or expose matchesSyntheticFilters and
matchesOverrideFilters in src/engine/rowFinder.ts as the shared canonical
implementation, then update countPostFilterMatches in src/useTable.ts to call
those helpers instead of its inline matching loop; preserve the existing filter
behavior while ensuring each row follows the shared SmartRow instantiation
pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engine/tableMapper.ts`:
- Around line 71-84: Move the synthetic-column collision validation in the
table-mapping success path before assigning the map to this._headerMap. Keep the
collision check based on the newly built entries/map, and only commit
this._headerMap after validation succeeds so failed mapping leaves no cached
state for getMap().

In `@src/smartRow.ts`:
- Around line 577-582: Update the synthetic-column computation in toJSON’s
atomic path so def.compute receives a row bound to the frozen snapshot or
materialized clone, with getValue/getCell resolving from the already-extracted
result rather than live DOM. Preserve existing synthetic column filtering and
output conversion, and ensure atomic toJSON maintains zero inter-column stagger
when compute reads other columns.
- Around line 444-461: Replace the shared _inSyntheticCompute boolean in
smart.getValue with per-row serialization of synthetic column computations,
ensuring concurrent sibling reads queue and execute without false nesting errors
while genuine nested reads still throw the existing error. Update the
syntheticDef handling and cleanup flow accordingly, and add coverage for
Promise.all on independent synthetic columns if tests are available.
- Around line 463-486: Update getValue to use the same cell-navigation and
beforeCellRead flow as getCell and toJSON, including reuse of the already-read
value from the current non-atomic toJSON result when available. Ensure real and
override columns are navigated before reading, while preserving synthetic-column
compute behavior and existing column-not-found handling.

In `@src/typeContext.ts`:
- Around line 307-313: Do not edit the declarations in src/typeContext.ts
directly, including getValue, SyntheticColumnDef, and syntheticColumns. Update
the corresponding source declarations in src/types.ts, then regenerate
typeContext.ts using the project’s established generation process so all
mirrored sections remain synchronized.

In `@src/types.ts`:
- Around line 699-706: Update mergeTableConfig to merge syntheticColumns using
the same two-sided merge path as columnOverrides, so preset synthetic
definitions are retained when absent from the override while override keys
replace matching preset keys. Ensure the resulting configuration preserves
top-level override semantics without dropping preset synthetic columns.

---

Nitpick comments:
In `@src/engine/rowFinder.ts`:
- Around line 93-107: The override and synthetic post-filter matching logic is
duplicated and creates SmartRow instances inconsistently. Keep or expose
matchesSyntheticFilters and matchesOverrideFilters in src/engine/rowFinder.ts as
the shared canonical implementation, then update countPostFilterMatches in
src/useTable.ts to call those helpers instead of its inline matching loop;
preserve the existing filter behavior while ensuring each row follows the shared
SmartRow instantiation pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a19290dc-89ac-4e98-9bdf-a3b385d1d8ca

📥 Commits

Reviewing files that changed from the base of the PR and between d80637f and b8595ad.

📒 Files selected for processing (11)
  • docs/.vitepress/smartrow-signatures.json
  • docs/.vitepress/tableconfig-signatures.json
  • src/engine/rowFinder.ts
  • src/engine/tableMapper.ts
  • src/index.ts
  • src/smartRow.ts
  • src/typeContext.ts
  • src/types.ts
  • src/useTable.ts
  • tests/synthetic-columns.spec.ts
  • tests/unit/syntheticColumns.test.ts

Comment thread src/engine/tableMapper.ts Outdated
Comment thread src/smartRow.ts Outdated
Comment thread src/smartRow.ts
Comment thread src/smartRow.ts
Comment thread src/typeContext.ts
Comment thread src/types.ts
rickcedwhat-ai and others added 2 commits August 1, 2026 15:23
- Move collision check before _headerMap assignment in tableMapper
- Replace shared _inSyntheticCompute boolean with per-compute guarded proxy row
- Atomic toJSON compute reads from snapshot result via proxy row
- Deep-merge syntheticColumns in mergeTableConfig
- Make matchesOverrideFilters/matchesSyntheticFilters public on RowFinder
- Refactor countPostFilterMatches to delegate to RowFinder methods

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply the same snapshot-bound proxy row pattern to the non-atomic
toJSON path, so synthetic compute() reuses already-extracted column
values instead of re-reading from potentially evicted DOM cells on
horizontally virtualized tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rickcedwhat-ai

Copy link
Copy Markdown
Collaborator Author

AI Review

PR #406 adds first-class synthetic columns feature with computed values derived from real/override columns. The implementation is well-structured with 321 unit tests passing, comprehensive E2E coverage, and proper TypeScript strictness. All acceptance criteria from #391 are met with no regressions detected.

✅ No actionable issues found.

@rickcedwhat
rickcedwhat merged commit 7a29ebf into main Aug 1, 2026
15 checks passed
@rickcedwhat
rickcedwhat deleted the feat/391-synthetic-columns branch August 1, 2026 19:40
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.

feat: first-class synthetic columns — computed columns with no DOM presence

2 participants