Skip to content

Move normalize args and visit into delegate#3934

Merged
ntucker merged 9 commits intomasterfrom
cursor/normalize-delegate-args-c1af
Apr 30, 2026
Merged

Move normalize args and visit into delegate#3934
ntucker merged 9 commits intomasterfrom
cursor/normalize-delegate-args-c1af

Conversation

@ntucker
Copy link
Copy Markdown
Collaborator

@ntucker ntucker commented Apr 29, 2026

Fixes # .

Motivation

Normalize still passed endpoint args and recursive visit as separate positional parameters even though denormalize has moved analogous context onto a delegate. This explores the same shape for normalize and reduces positional arguments through the normalize hot path.

Solution

  • Add args and visit to INormalizeDelegate.
  • Update the normalizr visitor to install delegate.visit and call schema.normalize(input, parent, key, delegate, parentEntity).
  • Update endpoint schema implementations and related typings/tests to read delegate.args / delegate.visit.
  • Add a changeset for the public schema interface change, including a before/after custom schema migration example.
  • Document the migration in the v0.18 blog draft; split custom-schema API reference into CustomSchema.
  • Extend the v0.18 migration skill and codemod to migrate Schema.normalize() signatures, recursive visit(...) calls, args references, and pass-through .normalize(...) calls.

Before / after (custom schemas)

// before — normalize
normalize(input, parent, key, args, visit, delegate, parentEntity?) {
  const nested = visit(this.schema, input.value, parent, key, args);
  delegate.mergeEntity(this, this.pk(input, parent, key, args), nested);
  return nested;
}

// after — normalize
normalize(input, parent, key, delegate, parentEntity?) {
  const nested = delegate.visit(this.schema, input.value, parent, key);
  delegate.mergeEntity(this, this.pk(input, parent, key, delegate.args), nested);
  return nested;
}
// before — denormalize (already delegate-based in v0.18; shown for symmetry)
denormalize(input, args, unvisit) {
  return unvisit(this.schema, input);
}

// after — denormalize
denormalize(input, delegate) {
  return delegate.unvisit(this.schema, input);
}

See also Custom Schema for full delegate typings.

Validation

  • yarn build
  • yarn test (116 suites, 1662 passed, 1 skipped)
  • yarn jest --config '{"testEnvironment":"node","testMatch":["<rootDir>/website/static/codemods/__tests__/v0.18.test.js"]}' --runInBand
  • Targeted regression subset: yarn test --selectProjects ReactDOM --testPathPatterns packages/rest/src/__tests__/Resource.test.ts packages/react/src/__tests__/integration-collections.tsx packages/react/src/hooks/__tests__/useController/fetch.tsx

Benchmarks

Normalize-only Node benchmark (yarn workspace example-benchmark start normalizr "^normalize"), 5 measured runs each after warm-up. Comparison uses trimmed mean (drop min/max), higher is better.

Scenario master trimmed mean branch trimmed mean delta
normalizeLong 818.7 ops/sec 820.7 ops/sec +0.24%
normalizeLong Values 735.0 ops/sec 734.0 ops/sec -0.14%
normalizeLong Scalar 640.3 ops/sec 637.7 ops/sec -0.42%
normalizeLong Scalar update 1627.3 ops/sec 1624.0 ops/sec -0.20%

All measured deltas are well within expected benchmark noise for normalize scenarios.

Deopt check

Direct V8 trace (node --allow-natives-syntax --trace-opt --trace-deopt examples/benchmark/index.js normalizr "^normalize") confirms normalize-path deopts occur, but the same deopts are present on origin/master; this branch does not introduce a new deopt pattern.

Open questions

None.

Open in Web Open in Cursor 

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs-site Ignored Ignored Preview Apr 30, 2026 1:32pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 29, 2026

🦋 Changeset detected

Latest commit: 4b62f0d

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

This PR includes changesets to release 15 packages
Name Type
@data-client/endpoint Minor
@data-client/rest Minor
@data-client/graphql Minor
@data-client/normalizr Minor
@data-client/core Minor
@data-client/react Minor
@data-client/vue Minor
@data-client/img Minor
example-benchmark Patch
example-benchmark-react Patch
normalizr-github-example Patch
normalizr-redux-example Patch
normalizr-relationships 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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

Size Change: +20 B (+0.02%)

Total Size: 81.1 kB

📦 View Changed
Filename Size Change
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB +20 B (+0.25%)
ℹ️ View Unchanged
Filename Size Change
examples/test-bundlesize/dist/App.js 1.46 kB 0 B
examples/test-bundlesize/dist/polyfill.js 307 B 0 B
examples/test-bundlesize/dist/rdcClient.js 10.8 kB +4 B (+0.04%)
examples/test-bundlesize/dist/react.js 59.7 kB 0 B
examples/test-bundlesize/dist/webpack-runtime.js 726 B 0 B

compressed-size-action

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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: 4b62f0d Previous: 959465a Ratio
data-client: getlist-100 142.86 ops/s (± 4.4%) 122.7 ops/s (± 5.8%) 0.86
data-client: getlist-500 41.84 ops/s (± 7.6%) 39.37 ops/s (± 4.9%) 0.94
data-client: update-entity 350.99 ops/s (± 11.5%) 285.71 ops/s (± 8.1%) 0.81
data-client: update-user 416.67 ops/s (± 8.6%) 285.71 ops/s (± 8.8%) 0.69
data-client: getlist-500-sorted 41.52 ops/s (± 8.9%) 37.74 ops/s (± 9.1%) 0.91
data-client: update-entity-sorted 333.33 ops/s (± 6.4%) 256.41 ops/s (± 4.9%) 0.77
data-client: update-entity-multi-view 357.14 ops/s (± 11.0%) 259.78 ops/s (± 8.7%) 0.73
data-client: list-detail-switch-10 9.42 ops/s (± 8.0%) 6.71 ops/s (± 7.2%) 0.71
data-client: update-user-10000 72.46 ops/s (± 16.0%) 65.15 ops/s (± 12.4%) 0.90
data-client: invalidate-and-resolve 35.78 ops/s (± 4.9%) 32.1 ops/s (± 5.6%) 0.90
data-client: unshift-item 219.81 ops/s (± 4.7%) 185.19 ops/s (± 5.8%) 0.84
data-client: delete-item 285.71 ops/s (± 5.8%) 243.9 ops/s (± 5.1%) 0.85
data-client: move-item 180.19 ops/s (± 11.5%) 149.25 ops/s (± 7.7%) 0.83

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

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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: 4b62f0d Previous: 959465a Ratio
normalizeLong 435 ops/sec (±3.87%) 428 ops/sec (±4.03%) 0.98
normalizeLong Values 402 ops/sec (±1.53%) 383 ops/sec (±1.64%) 0.95
normalizeLong Scalar 330 ops/sec (±1.97%) 342 ops/sec (±0.44%) 1.04
normalizeLong Scalar update 857 ops/sec (±0.14%) 859 ops/sec (±0.15%) 1.00
denormalizeLong 241 ops/sec (±4.20%) 228 ops/sec (±4.24%) 0.95
denormalizeLong Values 223 ops/sec (±3.62%) 226 ops/sec (±3.72%) 1.01
denormalizeLong donotcache 974 ops/sec (±0.09%) 1026 ops/sec (±0.11%) 1.05
denormalizeLong Values donotcache 728 ops/sec (±0.18%) 739 ops/sec (±0.17%) 1.02
denormalizeLong Scalar donotcache 902 ops/sec (±0.33%) 998 ops/sec (±0.15%) 1.11
denormalizeShort donotcache 500x 1526 ops/sec (±0.37%) 1457 ops/sec (±0.07%) 0.95
denormalizeShort 500x 712 ops/sec (±3.95%) 662 ops/sec (±4.71%) 0.93
denormalizeShort 500x withCache 7017 ops/sec (±0.14%) 6990 ops/sec (±1.00%) 1.00
queryShort 500x withCache 2846 ops/sec (±0.19%) 2980 ops/sec (±0.17%) 1.05
buildQueryKey All 55378 ops/sec (±0.60%) 49007 ops/sec (±0.94%) 0.88
query All withCache 6728 ops/sec (±0.29%) 6964 ops/sec (±0.46%) 1.04
denormalizeLong with mixin Entity 231 ops/sec (±4.33%) 231 ops/sec (±4.40%) 1
denormalizeLong withCache 5627 ops/sec (±0.22%) 6921 ops/sec (±0.14%) 1.23
denormalizeLong withCache (Scalar churn) 5593 ops/sec (±0.18%) 6870 ops/sec (±0.15%) 1.23
denormalizeLong Values withCache 4935 ops/sec (±0.23%) 6634 ops/sec (±0.91%) 1.34
denormalizeLong Scalar withCache 7629 ops/sec (±0.90%) 7162 ops/sec (±1.51%) 0.94
denormalizeLong Scalar update withCache 4065 ops/sec (±0.32%) 5539 ops/sec (±0.72%) 1.36
denormalizeLong All withCache 6500 ops/sec (±0.24%) 6456 ops/sec (±0.17%) 0.99
denormalizeLong Query-sorted withCache 6810 ops/sec (±0.25%) 7139 ops/sec (±0.45%) 1.05
denormalizeLongAndShort withEntityCacheOnly 1647 ops/sec (±0.22%) 1621 ops/sec (±0.80%) 0.98
denormalize bidirectional 50 5671 ops/sec (±5.36%) 4712 ops/sec (±4.83%) 0.83
denormalize bidirectional 50 donotcache 38873 ops/sec (±0.21%) 42300 ops/sec (±0.41%) 1.09
getResponse 4588 ops/sec (±0.77%) 5616 ops/sec (±1.71%) 1.22
getResponse (null) 9958759 ops/sec (±1.03%) 10296685 ops/sec (±0.44%) 1.03
getResponse (clear cache) 337 ops/sec (±0.39%) 228 ops/sec (±3.97%) 0.68
getSmallResponse 3460 ops/sec (±0.30%) 3756 ops/sec (±0.25%) 1.09
getSmallInferredResponse 2665 ops/sec (±0.13%) 2671 ops/sec (±0.21%) 1.00
getResponse Collection 4475 ops/sec (±0.64%) 5531 ops/sec (±1.07%) 1.24
get Collection 4563 ops/sec (±0.33%) 5264 ops/sec (±0.42%) 1.15
get Query-sorted 4615 ops/sec (±0.27%) 6450 ops/sec (±0.43%) 1.40
setLong 441 ops/sec (±0.25%) 448 ops/sec (±0.13%) 1.02
setLongWithMerge 248 ops/sec (±1.35%) 248 ops/sec (±1.60%) 1
setLongWithSimpleMerge 266 ops/sec (±0.81%) 267 ops/sec (±0.21%) 1.00
setSmallResponse 500x 934 ops/sec (±1.49%) 914 ops/sec (±0.10%) 0.98

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

@ntucker ntucker marked this pull request as ready for review April 30, 2026 00:35
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 2 potential issues.

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 3cb89ce. Configure here.

Comment thread packages/endpoint/src/normal.ts Outdated
Comment thread packages/endpoint/src/schemaTypes.ts
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.21%. Comparing base (752fc87) to head (4b62f0d).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3934   +/-   ##
=======================================
  Coverage   98.20%   98.21%           
=======================================
  Files         154      154           
  Lines        3013     3024   +11     
  Branches      601      601           
=======================================
+ Hits         2959     2970   +11     
  Misses         11       11           
  Partials       43       43           

☔ View full report in Codecov by Sentry.
📢 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.

ntucker added 2 commits April 30, 2026 08:25
- Document codemod import gate, class-field/const gaps, and TS key rules
- Correct denormalize type matching (interface method vs declare/property)
- Expand skipped cases; shorten Collection cleanup with blog deep link
- Add stable blog anchor for Collection consolidation heading
@ntucker ntucker merged commit 396d163 into master Apr 30, 2026
28 checks passed
@ntucker ntucker deleted the cursor/normalize-delegate-args-c1af branch April 30, 2026 13:41
@github-actions github-actions Bot mentioned this pull request Apr 29, 2026
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.

1 participant