Repository navigation
fix(core): Type controller.set() values by schema - #4133
Conversation
🦋 Changeset detectedLatest commit: 4b9134f The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
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
dedbe1f to
12148e0
Compare
|
Size Change: 0 B Total Size: 103 kB ℹ️ View Unchanged
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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
- 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
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
… 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
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
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
…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>

Requested by Nathaniel · project thread
Stacked on #4130 (reuses its row types); GitHub retargets this to
masterwhen #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.Solution
The value is typed as the raw input
set()normalizes (SetValue<S>), reusing #4130'sSetRowrules (number/string coercion, nested objects and Dates unchecked):set()normalizes that schema;process()output is not accepted.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.jsonon TS 7 and 5.3;useController/settests 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; runtimeset()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 nowSetValue<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 (notprocess()output). Batchset([Entity], rows)uses the same row rules viaSetManyValue, withSkipInferso 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.mdand release notes, playground.d.tsstubs aligned with public types, and ReactuseController/settests with@ts-expect-errorcoverage 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.