Skip to content

fix(core): Type controller.set() values by schema - #4133

Merged
ntucker merged 12 commits into
masterfrom
claude/project-thread-jjim10
Oct 4, 2026
Merged

ntucker merged 12 commits into
masterfrom
claude/project-thread-jjim10

Conversation

@ntucker

@ntucker ntucker commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Stacked on #4130 (reuses its row types); GitHub retargets this to master when #4130 merges.

Motivation

Non-batch controller.set() typed its value and updater return as {}, so wrong values typechecked and then failed or wrote nothing at runtime.

// Before: these all typechecked
ctrl.set(new schema.All(Todo), 42);
ctrl.set(TodoResource.getList.schema, 'anything');
ctrl.set(Todo, { id: '5' }, { id: '5', completed: 'yes' });

// After: TypeScript errors on the above; these typecheck
ctrl.set(TodoResource.getList.schema, [{ id: '5', completed: true }]);
ctrl.set(new schema.All(Todo), [{ id: '5', completed: true }]);

Solution

The value is typed as the raw input set() normalizes (SetValue<S>), reusing #4130's SetRow rules (number/string coercion, nested objects and Dates unchecked):

  • Entity: its fields, partial.
  • Collection / All / other list schemas: a list of rows; keyed-object results take an object of rows. Polymorphic rows allow extra keys for discriminators.
  • Query: the input of the schema it wraps, since set() normalizes that schema; process() output is not accepted.
  • Updater functions must return the same type.

Rather than rejecting All/Query, they stay settable because they do normalize at runtime (All writes the rows, Query delegates to its schema). Checked with tsc -p tsconfig.test.json on TS 7 and 5.3; useController/set tests cover each case. Docs: Controller.md#set.

Open questions

None.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc


Generated by Claude Code


Note

Medium Risk
Compile-time breaking change for existing controller.set() call sites with wrong shapes; runtime set() behavior is unchanged but incorrect writes may now fail at build time instead of silently at runtime.

Overview
Controller.set() no longer accepts arbitrary values or {}-shaped updaters. The last argument (and updater return type) is now SetValue<S>, derived from the schema: partial entity fields for Entity, row arrays for Collection / All / list schemas, and the wrapped schema’s normalize input for Query (not process() output). Batch set([Entity], rows) uses the same row rules via SetManyValue, with SkipInfer so schema type inference is not widened by the value argument.

Supporting work includes expanded setManyTypes (Union field merging for speed, literal-safe number/string coercion), Controller.md and release notes, playground .d.ts stubs aligned with public types, and React useController/set tests with @ts-expect-error coverage plus a runtime test that list/Query/All writes still work.

Reviewed by Cursor Bugbot for commit 4b9134f. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4b9134f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@data-client/core Patch
@data-client/react Patch
@data-client/vue Patch
example-benchmark Patch
example-benchmark-react Patch
test-bundlesize Patch
coinbase-lite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs-site Ready Ready Preview Oct 4, 2026 6:08pm UTC

Request Review

@ntucker ntucker self-assigned this Oct 4, 2026
@ntucker
ntucker marked this pull request as ready for review October 4, 2026 01:39

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/controller/Controller.ts Outdated
@codecov

codecov Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.06%. Comparing base (e350950) to head (6b0d942).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4133   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         163      163           
  Lines        3095     3095           
  Branches      616      616           
=======================================
  Hits         3035     3035           
  Misses         18       18           
  Partials       42       42           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Base automatically changed from claude/project-thread-fjrb2j to master October 4, 2026 15:12
controller.set() typed its value (and updater return) as `{}`, so
`set(new schema.All(User), 42)` or `set(UserResource.getList.schema, 'x')`
typechecked. Values are now the raw input the schema normalizes: Entity
fields, rows for Collection/All, and a Query's inner schema input.
Batch set() shares the same row typing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
@ntucker
ntucker force-pushed the claude/project-thread-jjim10 branch from dedbe1f to 12148e0 Compare October 4, 2026 15:53
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 103 kB

ℹ️ View Unchanged
Filename Size
examples/test-bundlesize/dist/App.js 1.46 kB
examples/test-bundlesize/dist/polyfill.js 307 B
examples/test-bundlesize/dist/rdcClient.js 10.9 kB
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB
examples/test-bundlesize/dist/rdcNextjs.js 12.3 kB
examples/test-bundlesize/dist/rdcPipeableStream.js 9.64 kB
examples/test-bundlesize/dist/react.js 59.7 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benchmark React

Details
Benchmark suite Current: 4b9134f Previous: a82758c Ratio
data-client: getlist-100 127.39 ops/s (± 4.0%) 200 ops/s (± 3.4%) 1.57
data-client: getlist-500 40.65 ops/s (± 5.3%) 67.11 ops/s (± 5.0%) 1.65
data-client: update-entity 333.33 ops/s (± 7.8%) 526.32 ops/s (± 7.3%) 1.58
data-client: update-user 317.54 ops/s (± 7.1%) 476.19 ops/s (± 5.4%) 1.50
data-client: getlist-500-sorted 43.67 ops/s (± 9.1%) 67.57 ops/s (± 9.8%) 1.55
data-client: update-entity-sorted 274.02 ops/s (± 5.2%) 416.67 ops/s (± 6.7%) 1.52
data-client: update-entity-multi-view 294.12 ops/s (± 7.8%) 526.32 ops/s (± 5.1%) 1.79
data-client: list-detail-switch-10 7.74 ops/s (± 6.3%) 23.56 ops/s (± 9.9%) 3.04
data-client: update-user-10000 72.46 ops/s (± 13.5%) 119.05 ops/s (± 11.7%) 1.64
data-client: invalidate-and-resolve 33.45 ops/s (± 5.8%) 55.71 ops/s (± 5.8%) 1.67
data-client: unshift-item 200 ops/s (± 4.6%) 303.03 ops/s (± 5.1%) 1.52
data-client: delete-item 270.27 ops/s (± 4.3%) 400 ops/s (± 5.3%) 1.48
data-client: move-item 158.73 ops/s (± 8.3%) 256.41 ops/s (± 7.8%) 1.62

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benchmark

Details
Benchmark suite Current: 4b9134f Previous: a82758c Ratio
normalizeLong 428 ops/sec (±3.93%) 426 ops/sec (±3.97%) 1.00
normalizeLong Values 390 ops/sec (±0.23%) 388 ops/sec (±1.14%) 0.99
normalizeLong Scalar 364 ops/sec (±3.07%) 371 ops/sec (±3.69%) 1.02
normalizeLong Scalar update 918 ops/sec (±0.22%) 911 ops/sec (±1.17%) 0.99
denormalizeLong 247 ops/sec (±6.51%) 243 ops/sec (±6.75%) 0.98
denormalizeLong Values 229 ops/sec (±4.67%) 230 ops/sec (±4.44%) 1.00
denormalizeLong donotcache 990 ops/sec (±0.50%) 994 ops/sec (±1.06%) 1.00
denormalizeLong Values donotcache 740 ops/sec (±0.26%) 740 ops/sec (±0.70%) 1
denormalizeLong Scalar donotcache 1033 ops/sec (±0.43%) 1046 ops/sec (±0.88%) 1.01
denormalizeShort donotcache 500x 1417 ops/sec (±0.12%) 1412 ops/sec (±0.18%) 1.00
denormalizeShort 500x 646 ops/sec (±6.96%) 648 ops/sec (±6.58%) 1.00
denormalizeShort 500x withCache 6432 ops/sec (±0.11%) 6888 ops/sec (±0.22%) 1.07
queryShort 500x withCache 3222 ops/sec (±0.22%) 3249 ops/sec (±1.41%) 1.01
buildQueryKey All 58186 ops/sec (±1.18%) 59499 ops/sec (±0.86%) 1.02
query All withCache 6073 ops/sec (±2.51%) 6419 ops/sec (±2.55%) 1.06
denormalizeLong with mixin Entity 219 ops/sec (±7.20%) 223 ops/sec (±7.52%) 1.02
denormalizeLong withCache 6196 ops/sec (±0.15%) 7530 ops/sec (±0.24%) 1.22
denormalizeLong withCache (Scalar churn) 6153 ops/sec (±0.92%) 7475 ops/sec (±0.67%) 1.21
denormalizeLong Values withCache 5157 ops/sec (±1.52%) 5127 ops/sec (±1.21%) 0.99
denormalizeLong Scalar withCache 7750 ops/sec (±0.72%) 7547 ops/sec (±0.36%) 0.97
denormalizeLong Scalar update withCache 4081 ops/sec (±0.22%) 4007 ops/sec (±0.25%) 0.98
denormalizeLong All withCache 6616 ops/sec (±0.27%) 6609 ops/sec (±0.33%) 1.00
denormalizeLong Query-sorted withCache 6455 ops/sec (±1.66%) 6596 ops/sec (±1.79%) 1.02
denormalizeLongAndShort withEntityCacheOnly 1693 ops/sec (±0.49%) 1793 ops/sec (±0.24%) 1.06
denormalize bidirectional 50 4694 ops/sec (±9.74%) 4762 ops/sec (±9.59%) 1.01
denormalize bidirectional 50 donotcache 42243 ops/sec (±0.26%) 41900 ops/sec (±0.87%) 0.99
getResponse 4439 ops/sec (±4.25%) 4466 ops/sec (±3.25%) 1.01
getResponse (null) 10288585 ops/sec (±0.52%) 10400193 ops/sec (±0.60%) 1.01
getResponse (clear cache) 207 ops/sec (±8.65%) 210 ops/sec (±6.80%) 1.01
getSmallResponse 3515 ops/sec (±0.21%) 3494 ops/sec (±1.48%) 0.99
getSmallInferredResponse 2870 ops/sec (±1.15%) 2886 ops/sec (±0.19%) 1.01
getResponse Collection 4531 ops/sec (±1.72%) 4303 ops/sec (±4.07%) 0.95
get Collection 3552 ops/sec (±0.21%) 2754 ops/sec (±0.40%) 0.78
get Query-sorted 4848 ops/sec (±1.79%) 4993 ops/sec (±1.81%) 1.03
setLong 428 ops/sec (±0.24%) 423 ops/sec (±0.53%) 0.99
setLongWithMerge 251 ops/sec (±1.19%) 246 ops/sec (±1.26%) 0.98
setLongWithSimpleMerge 268 ops/sec (±0.62%) 265 ops/sec (±0.43%) 0.99
setSmallResponse 500x 915 ops/sec (±1.54%) 925 ops/sec (±1.60%) 1.01
setMany 50x one-per-row 150 ops/sec (±0.43%) 152 ops/sec (±0.40%) 1.01
setMany 50 batch 3643 ops/sec (±0.46%) 3612 ops/sec (±1.36%) 0.99
setMany 500x one-per-row 15.39 ops/sec (±0.55%) 15.88 ops/sec (±0.37%) 1.03
setMany 500 batch 1426 ops/sec (±0.19%) 1426 ops/sec (±3.20%) 1

This comment was automatically generated by workflow using github-action-benchmark.

claude added 2 commits October 4, 2026 15:59
One overload for value or updater; SetRow handles primitives; rename the
Query unwrap to InputSchema and exclude Entity with pk?: never.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
claude added 2 commits October 4, 2026 17:32
- Infer S from the schema only (SkipInfer), so TS doesn't walk the value
  against every conditional in SetValue
- Merge a Union's members into one row type instead of a union of rows,
  so each row is one comparison rather than one per member
- Keep string/number literal fields exact

On a 30-member Union with 1000 updater calls, check time drops from
16.8s to 4.7s (master, with untyped values: 3.3s); other extreme cases
are now at or below master.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 113032d. Configure here.

Comment thread packages/core/src/controller/setManyTypes.ts
… row tradeoff

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
claude added 2 commits October 4, 2026 17:58
Regenerate the playground's @data-client editor types so the demo
reflects v0.19 set() typing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc

# Conflicts:
#	website/blog/2026-10-03-v0.19-batch-set.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BN4uSb49CL2pwcZFnTCJkc
@ntucker
ntucker merged commit a82758c into master Oct 4, 2026
7 of 9 checks passed
@ntucker
ntucker deleted the claude/project-thread-jjim10 branch October 4, 2026 18:08
@github-actions github-actions Bot mentioned this pull request Oct 4, 2026
ntucker pushed a commit that referenced this pull request Oct 5, 2026
Re-measures every stress test against the v0.18.1 packages so the v0.19
numbers include #4133's typed set(), adds set() value and updater rows,
and lets PerfChart show regressions as a red gap below 1x.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk
ntucker added a commit that referenced this pull request Oct 5, 2026
…extend() (#4173)

* enhance(rest): Faster type checking for RestEndpoint, resource() and extend()

- Move generic this-typed extend()/paginated() into non-generic mixin
  interfaces so their signatures are shared instead of re-instantiated
  per endpoint type
- Infer options from a naked O next to Readonly<O> so inference yields a
  plain object type instead of a reverse-mapped type
- Skip Omit<> work in extend() option/result types when there are no
  extra members; collapse member-less fetch types in getPage
- Linear path-key scan and as-free KeysToArgs for literal keys

Same diagnostics on TS 4.0-7 (verified with every @ts-expect-error
neutralized); paths stress test 822K -> 342K instantiations.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(blog): Note faster TypeScript checking in v0.19

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* test(rest): Commit extend/paginate/path type probes; simplify cleanups

- Add the must-fail probes used to prove zero type-checking loss as
  @ts-expect-error tests in packages/rest/typescript-tests
- typetest-libcheck: extend() process params aren't implicitly any on a
  chained extend() (checked on TS 4.0-7 in CI)
- Rename path key helpers to avoid clashing with utiltypes RequiredKeys
- Drop redundant annotations/casts in resource(); refresh mixin comments
- Correct changeset/blog: process params are no longer implicitly any

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs: Update type-check numbers against latest master

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(blog): Headline faster TypeScript in v0.19 post with charts and full results

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Show draft pages on Vercel preview deploys

Production and local builds still drop draft: true pages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(blog): Use PerfChart for faster TypeScript results

Adds a ratioLabel prop to PerfChart so memory and instantiation charts
don't label their multiplier column "Speedup".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Mark 1x in PerfChart and add visual PerfTable

PerfChart bars are shaded up to a dashed 1x line so gains read against no
change. PerfTable replaces the v0.19 faster TypeScript details table with
before/after values, change badges and paired bars.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Show PerfTable exact numbers on hover

Cells show paired bars and the percent change; exact before and after
numbers appear on hover, tap or keyboard focus and stay in the DOM.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Move PerfChart exact numbers into hover tooltips

Drops the table under each chart. Each bar row reveals its before and
after numbers on hover, tap or keyboard focus, and the numbers stay in
the page text for crawlers and screen readers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Scroll PerfTable in place on narrow screens

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Align neighboring PerfChart 1x lines and add tap tooltips

PerfChart takes scaleMax so charts next to each other share a scale, and
fixed label and multiplier column widths keep their 1x lines aligned.
PerfChart rows and PerfTable cells now toggle their exact numbers on tap;
hover styles only apply on devices that hover.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Decide PerfChart log scale from its own rows, not scaleMax

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(blog): Compare faster TypeScript numbers with published v0.18.1

Re-measures every stress test against the v0.18.1 packages so the v0.19
numbers include #4133's typed set(), adds set() value and updater rows,
and lets PerfChart show regressions as a red gap below 1x.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

* docs(website): Keep regressed rows visible on log-scale PerfCharts

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9V2RBxtoPnFRffXJEqvnk

---------

Co-authored-by: Claude <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 4b9134f1 Deployed Oct 4, 2026 by vercel[bot]
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.

2 participants