Replace direct ryu usage with zmij for float formatting#865
Draft
Copilot wants to merge 76 commits into
Draft
Conversation
Copilot
AI
changed the title
[WIP] Replace ryu crate with zmij
Replace direct Mar 18, 2026
ryu usage with zmij for float formatting
|
🐋 This PR was built and pushed to the following Docker images: Image Names: Platforms: Image Tags: Docker metadata{
"buildx.build.provenance/linux/amd64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/23482584243/attempts/1"
},
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1.22",
"digest": {
"sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
}
},
{
"uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Famd64",
"digest": {
"sha256": "329e54034ce498f9c6b345044e8f530c6691f99e94a92446f68c0adf9baa8464"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-03-24T09:47:12.991Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "20e5cb2f06fd49b83463db35b8ba8fbe263fb399",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-865",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
]
},
"environment": {
"github_actor": "kamilkisiela",
"github_actor_id": "8167190",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "e197521774a40772c629fd3d37d40d532be6c965",
"before": "40fa07a5b841199e4a8e201554fd5e62a10a66fc",
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
"created_at": "2024-07-02T08:52:28Z",
"description": "",
"html_url": "https://github.com/enterprises/the-guild",
"id": 187753,
"name": "The Guild",
"node_id": "E_kgDOAALdaQ",
"slug": "the-guild",
"updated_at": "2026-03-11T16:47:15Z",
"website_url": "https://the-guild.dev/"
},
"number": 865,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
"events_url": "https://api.github.com/orgs/graphql-hive/events",
"hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
"id": 182742256,
"issues_url": "https://api.github.com/orgs/graphql-hive/issues",
"login": "graphql-hive",
"members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
"node_id": "O_kgDOCuRs8A",
"public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
"repos_url": "https://api.github.com/orgs/graphql-hive/repos",
"url": "https://api.github.com/orgs/graphql-hive"
},
"pull_request": {
"_links": {
"comments": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/865/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/865"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/865"
},
"review_comment": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
},
"review_comments": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/e197521774a40772c629fd3d37d40d532be6c965"
}
},
"active_lock_reason": null,
"additions": 29,
"assignee": null,
"assignees": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
},
{
"avatar_url": "https://avatars.githubusercontent.com/in/1143301?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-swe-agent",
"id": 198982749,
"login": "Copilot",
"node_id": "BOT_kgDOC9w8XQ",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
],
"author_association": "NONE",
"auto_merge": null,
"base": {
"label": "graphql-hive:main",
"ref": "main",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sha": "2522e961a6dca22ad4417a9deefe306eabb2c614",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"body": "This change replaces direct `ryu` usage with `zmij` in the router codepaths that serialize floating-point values. The goal is to align float-to-string formatting with `zmij` while preserving existing output behavior.\n\n- **Dependency updates**\n - Swapped direct crate dependencies:\n - `lib/graphql-tools/Cargo.toml`: `ryu` → `zmij`\n - `lib/executor/Cargo.toml`: `ryu` → `zmij`\n - Updated lockfile edges so affected crates now resolve through `zmij`.\n\n- **Runtime formatting path migration**\n - Updated GraphQL AST minification float formatting:\n - `lib/graphql-tools/src/parser/query/minify.rs`\n - `ryu::Buffer` replaced with `zmij::Buffer`.\n - Updated JSON float writer in plan executor:\n - `lib/executor/src/json_writer.rs`\n - `ryu::Buffer` replaced with `zmij::Buffer`.\n\n- **Documentation/comments consistency**\n - Updated in-code comments in minifier implementation to reference `zmij` instead of `ryu`.\n\n- **Focused regression coverage**\n - Added targeted tests in `lib/executor/src/json_writer.rs` for:\n - finite float serialization\n - non-finite float (`NaN`) serialization to `null`\n\nExample of the core migration:\n\n```rust\nlet mut buffer = zmij::Buffer::new();\nlet s = buffer.format_finite(value);\nwriter.put(s.as_bytes());\n```\n\n<!-- START COPILOT CODING AGENT TIPS -->\n---\n\n💬 Send tasks to Copilot coding agent from [Slack](https://gh.io/cca-slack-docs) and [Teams](https://gh.io/cca-teams-docs) to turn conversations into code. Copilot posts an update in your thread when it's finished.",
"changed_files": 5,
"closed_at": null,
"comments": 1,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/865/comments",
"commits": 1,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/865/commits",
"created_at": "2026-03-18T14:42:51Z",
"deletions": 11,
"diff_url": "https://github.com/graphql-hive/router/pull/865.diff",
"draft": true,
"head": {
"label": "graphql-hive:copilot/replace-ryu-with-zmij",
"ref": "copilot/replace-ryu-with-zmij",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sha": "e197521774a40772c629fd3d37d40d532be6c965",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"html_url": "https://github.com/graphql-hive/router/pull/865",
"id": 3415271595,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/865",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "80f39fc394ea101635bf2c5be1a827e56c45f830",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7LkOir",
"number": 865,
"patch_url": "https://github.com/graphql-hive/router/pull/865.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 0,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/865/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/e197521774a40772c629fd3d37d40d532be6c965",
"title": "Replace direct `ryu` usage with `zmij` for float formatting",
"updated_at": "2026-03-24T09:34:02Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/865",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/in/1143301?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-swe-agent",
"id": 198982749,
"login": "Copilot",
"node_id": "BOT_kgDOC9w8XQ",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
},
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"custom_properties": {
"vanta_production_branch_name": "main"
},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/865/merge",
"github_ref_name": "865/merge",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "graphql-hive/router",
"github_repository_id": "891604244",
"github_repository_owner": "graphql-hive",
"github_repository_owner_id": "182742256",
"github_run_attempt": "1",
"github_run_id": "23482584243",
"github_run_number": "2071",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260309.50.1",
"github_runner_name": "GitHub Actions 1000684234",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_3ae3ac00-faac-4903-8640-18814519786a",
"github_server_url": "https://github.com",
"github_triggering_actor": "kamilkisiela",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/865/merge",
"github_workflow_sha": "20e5cb2f06fd49b83463db35b8ba8fbe263fb399",
"platform": "linux/amd64"
}
}
},
"buildx.build.provenance/linux/arm64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/23482584243/attempts/1"
},
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1.22",
"digest": {
"sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
}
},
{
"uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Farm64",
"digest": {
"sha256": "329e54034ce498f9c6b345044e8f530c6691f99e94a92446f68c0adf9baa8464"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-03-24T09:47:12.991Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "20e5cb2f06fd49b83463db35b8ba8fbe263fb399",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-865",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
]
},
"environment": {
"github_actor": "kamilkisiela",
"github_actor_id": "8167190",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "e197521774a40772c629fd3d37d40d532be6c965",
"before": "40fa07a5b841199e4a8e201554fd5e62a10a66fc",
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
"created_at": "2024-07-02T08:52:28Z",
"description": "",
"html_url": "https://github.com/enterprises/the-guild",
"id": 187753,
"name": "The Guild",
"node_id": "E_kgDOAALdaQ",
"slug": "the-guild",
"updated_at": "2026-03-11T16:47:15Z",
"website_url": "https://the-guild.dev/"
},
"number": 865,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
"events_url": "https://api.github.com/orgs/graphql-hive/events",
"hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
"id": 182742256,
"issues_url": "https://api.github.com/orgs/graphql-hive/issues",
"login": "graphql-hive",
"members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
"node_id": "O_kgDOCuRs8A",
"public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
"repos_url": "https://api.github.com/orgs/graphql-hive/repos",
"url": "https://api.github.com/orgs/graphql-hive"
},
"pull_request": {
"_links": {
"comments": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/865/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/865"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/865"
},
"review_comment": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
},
"review_comments": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/865"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/e197521774a40772c629fd3d37d40d532be6c965"
}
},
"active_lock_reason": null,
"additions": 29,
"assignee": null,
"assignees": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
},
{
"avatar_url": "https://avatars.githubusercontent.com/in/1143301?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-swe-agent",
"id": 198982749,
"login": "Copilot",
"node_id": "BOT_kgDOC9w8XQ",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
],
"author_association": "NONE",
"auto_merge": null,
"base": {
"label": "graphql-hive:main",
"ref": "main",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sha": "2522e961a6dca22ad4417a9deefe306eabb2c614",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"body": "This change replaces direct `ryu` usage with `zmij` in the router codepaths that serialize floating-point values. The goal is to align float-to-string formatting with `zmij` while preserving existing output behavior.\n\n- **Dependency updates**\n - Swapped direct crate dependencies:\n - `lib/graphql-tools/Cargo.toml`: `ryu` → `zmij`\n - `lib/executor/Cargo.toml`: `ryu` → `zmij`\n - Updated lockfile edges so affected crates now resolve through `zmij`.\n\n- **Runtime formatting path migration**\n - Updated GraphQL AST minification float formatting:\n - `lib/graphql-tools/src/parser/query/minify.rs`\n - `ryu::Buffer` replaced with `zmij::Buffer`.\n - Updated JSON float writer in plan executor:\n - `lib/executor/src/json_writer.rs`\n - `ryu::Buffer` replaced with `zmij::Buffer`.\n\n- **Documentation/comments consistency**\n - Updated in-code comments in minifier implementation to reference `zmij` instead of `ryu`.\n\n- **Focused regression coverage**\n - Added targeted tests in `lib/executor/src/json_writer.rs` for:\n - finite float serialization\n - non-finite float (`NaN`) serialization to `null`\n\nExample of the core migration:\n\n```rust\nlet mut buffer = zmij::Buffer::new();\nlet s = buffer.format_finite(value);\nwriter.put(s.as_bytes());\n```\n\n<!-- START COPILOT CODING AGENT TIPS -->\n---\n\n💬 Send tasks to Copilot coding agent from [Slack](https://gh.io/cca-slack-docs) and [Teams](https://gh.io/cca-teams-docs) to turn conversations into code. Copilot posts an update in your thread when it's finished.",
"changed_files": 5,
"closed_at": null,
"comments": 1,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/865/comments",
"commits": 1,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/865/commits",
"created_at": "2026-03-18T14:42:51Z",
"deletions": 11,
"diff_url": "https://github.com/graphql-hive/router/pull/865.diff",
"draft": true,
"head": {
"label": "graphql-hive:copilot/replace-ryu-with-zmij",
"ref": "copilot/replace-ryu-with-zmij",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sha": "e197521774a40772c629fd3d37d40d532be6c965",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"html_url": "https://github.com/graphql-hive/router/pull/865",
"id": 3415271595,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/865",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "80f39fc394ea101635bf2c5be1a827e56c45f830",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7LkOir",
"number": 865,
"patch_url": "https://github.com/graphql-hive/router/pull/865.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 0,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/865/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/e197521774a40772c629fd3d37d40d532be6c965",
"title": "Replace direct `ryu` usage with `zmij` for float formatting",
"updated_at": "2026-03-24T09:34:02Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/865",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/in/1143301?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-swe-agent",
"id": 198982749,
"login": "Copilot",
"node_id": "BOT_kgDOC9w8XQ",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
},
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"custom_properties": {
"vanta_production_branch_name": "main"
},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 9,
"forks_count": 9,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 61,
"open_issues_count": 61,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-03-24T09:34:00Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 5903,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 79,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-03-24T00:29:34Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 79,
"watchers_count": 79,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/865/merge",
"github_ref_name": "865/merge",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "graphql-hive/router",
"github_repository_id": "891604244",
"github_repository_owner": "graphql-hive",
"github_repository_owner_id": "182742256",
"github_run_attempt": "1",
"github_run_id": "23482584243",
"github_run_number": "2071",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260309.50.1",
"github_runner_name": "GitHub Actions 1000684234",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_3ae3ac00-faac-4903-8640-18814519786a",
"github_server_url": "https://github.com",
"github_triggering_actor": "kamilkisiela",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/865/merge",
"github_workflow_sha": "20e5cb2f06fd49b83463db35b8ba8fbe263fb399",
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-40093666-09b9-4d18-8f0a-bf29b11bcad7/builder-40093666-09b9-4d18-8f0a-bf29b11bcad70/w60qe50c9ceu47ryvso9mshx0",
"containerimage.descriptor": {
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:cec0f1d0f763e02cf9096851b5e0c90143fbec3690fbb68ad21a773f5a18f767",
"size": 1609
},
"containerimage.digest": "sha256:cec0f1d0f763e02cf9096851b5e0c90143fbec3690fbb68ad21a773f5a18f767",
"image.name": "ghcr.io/graphql-hive/router:pr-865,ghcr.io/graphql-hive/router:sha-20e5cb2"
} |
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
40fa07a to
e197521
Compare
Contributor
|
@copilot rebase and add a changeset (patch) |
- Add inbound router request deduplication for concurrent identical
GraphQL requests, with atomic in-flight key claiming and leader/joiner
response sharing.
- Feature behind `traffic_shaping.router.dedupe.enabled` (default: false
- but I'm open to change it. I made it 'all' and disabled by default as
anything that makes a request "unique" affects the request fingerprint
and therefore deduplication), and include current schema checksum in the
dedupe fingerprint to prevent cross-schema sharing.
- Restrict response sharing to `query` operations only
```yaml
traffic_shaping:
router:
dedupe:
enabled: true # default
headers: all # default
```
---
The deduplication key is built out of:
- request method and path
- selected request headers (based on dedupe header policy - see examples
below)
- normalized operation hash
- GraphQL variables hash
- GraphQL extensions hash
- schema checksum
Header policy: include all headers in the req fingerprint
```yaml
headers: all
```
Header policy: do not include headers in the req fingerprint
```yaml
headers: none
```
Header policy: include only some headers in the req fingerprint
```yaml
headers:
include:
- authorization
- cookie
```
Docs: graphql-hive/docs#62
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com> Agent-Logs-Url: https://github.com/graphql-hive/router/sessions/697ee76f-0b27-4111-9902-61d62a29e7b7
Contributor
Author
Done — I rebased this branch on top of latest |
511f910 to
e09e78a
Compare
Contributor
|
@copilot rebase |
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
add a dedicated benchmark config and CI scenario for inbound deduplication so it can be compared explicitly with `main`. the `default` and `plugins` benchmark configs no longer enable inbound deduplication, and the inbound-dedupe case now lives in its own `dedupe-inbound` config. <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/graphql-hive/router/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
…ction is disabled (#872) Related: #871 Closes ROUTER-278 Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
… prevent malformed JSON (#881) Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Closes ROUTER-279 Closes: #876 # Generating conflicting aliases when conflicts are across fragments Consider a schema like: ```graphql interface CommonInterface { id: ID! } type TypeA implements CommonInterface { id: ID! field: EnumA! } type TypeB implements CommonInterface { id: ID! field: EnumB! } type TypeC implements CommonInterface { id: ID! field: EnumC! } enum EnumA { VALUE_A1 VALUE_A2 VALUE_A3 } enum EnumB { VALUE_B1 VALUE_B2 VALUE_B3 } enum EnumC { VALUE_C1 VALUE_C2 VALUE_C3 } type Query { getTypes: CommonInterface } ``` Consider a query like: ```graphql query GetAllTypesWithConflictingField { getTypes { id # ✅ on CommonInterface — no conflict ... on TypeA { field } # field: EnumA ... on TypeB { userAlias1: field } # field: EnumB — user-provided alias ... on TypeC { userAlias2: field } # field: EnumC — user-provided alias } } ``` The Rust Planner Generates: ```graphql QueryPlan { Fetch(service: "service") { { getTypes { id __typename ... on TypeA { field } ... on TypeB { _internal_qp_alias_0: field } ... on TypeC { _internal_qp_alias_0: field } ... on TypeD { _internal_qp_alias_0: field } } } }, }, ``` The problem here is that this is invalid GraphQL syntax and causes the subgraph query to be malformed. Resulting in a response from the subgraph like below: ```graphql GraphQLError: Fields "_internal_qp_alias_0" conflict because they return conflicting types "EnumB" and "EnumC". Use different aliases on the fields to fetch both if this was intentional. ``` --- ### Explanation: Why `SafeSelectionSetMerger` Causes Identical Alias Names: The problem is about scope and state persistence of the merger's internal counter. ``` rust for (root_def_name, mismatch_path) in mismatches_paths { let mut merger = SafeSelectionSetMerger::default(); // Line 61: RESET EACH ITERATION // ... let next_alias = merger.safe_next_alias_name(&selection_set.items); } ``` A new `SafeSelectionSetMerger` is created for each mismatch in the list, which means its aliases_counter (initialized to 0) resets every iteration. When processing multiple mismatches across fragments: 1. Mismatch # 1 → merger starts at counter 0 → generates _internal_qp_alias_0 2. Mismatch # 2 → NEW merger starts at counter 0 → generates _internal_qp_alias_0 (COLLISION!) 3. Mismatch # 3 → NEW merger starts at counter 0 → generates _internal_qp_alias_0 (COLLISION!) --- --------- Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
This patch includes the fixes in the query planner including the fixes for mismatch handling so conflicting fields are tracked by response key (alias-aware), and internal alias rewrites restore the original client-facing key (alias-or-name) instead of always the schema field name. Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
> [!IMPORTANT] > Merging this pull request will create these releases # node-addon 0.0.17 (2026-04-01) ## Fixes - This patch includes the fixes in the query planner including the fixes for mismatch handling so conflicting fields are tracked by response key (alias-aware), and internal alias rewrites restore the original client-facing key (alias-or-name) instead of always the schema field name. Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Ref ROUTER-100 Ref ROUTER-118 Closes #340 Documentation graphql-hive/docs#94 # TLS Support Adds TLS support to Hive Router for both client and subgraph connections, including mutual TLS (mTLS) authentication. This allows secure communication between clients, the router, and subgraphs by encrypting data in transit and optionally verifying identities. ## TLS Directions TLS Support has implementations for the following 4 directions: ### Router -> Client - Regular TLS Router has an `identity` (`cert`, `key`), and client has `cert`, then Client validates the router's `identity` ### Client -> Router - mTLS Router has the `cert`, client has the `identity`, mTLS/Client Auth then the router validates the client's `identity` ### Subgraph -> Router - Regular TLS Subgraph has the `identity` (`cert`, `key`), and router has `cert`, then Router validates the subgraph's `identity`. ### Router -> Subgraph - mTLS Subgraph has the `cert`, router(which is the client this time) has the `identity`, then subgraph validates the router's `identity`. ## TLS Directions Diagram ```mermaid flowchart LR Client["Client"] Router["Router"] Subgraph["Subgraph"] %% Router -> Client: Regular TLS Router -- "TLS\n(cert_file + key_file)" --> Client Client -. "validates router identity\n(cert_file)" .-> Router %% Client -> Router: mTLS / Client Auth Client -- "mTLS\n(client identity)" --> Router Router -. "validates client identity\n(client_auth.cert_file)" .-> Client %% Subgraph -> Router: Regular TLS Subgraph -- "TLS\n(cert_file)" --> Router Router -. "validates subgraph identity\n(all/subgraphs.cert_file)" .-> Subgraph %% Router -> Subgraph: mTLS Router -- "mTLS\n(client_auth.cert_file + key_file)" --> Subgraph Subgraph -. "validates router identity\n(cert_file)" .-> Router ``` ## Configuration Structure ```yaml traffic_shaping: router: key_file: # Router server private key cert_file: # Router server certificate(s) client_auth: # mTLS: Client -> Router cert_file: # Trusted client CA certificate(s) all: # Default TLS for all subgraph connections cert_file: # Trusted subgraph CA certificate(s) client_auth: # mTLS: Router -> Subgraph cert_file: # Router client certificate(s) key_file: # Router client private key subgraphs: SUBGRAPH_NAME: # Per-subgraph TLS override cert_file: # Trusted subgraph CA certificate(s) client_auth: # mTLS: Router -> Subgraph cert_file: # Router client certificate(s) key_file: # Router client private key ``` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: theguild-bot <bot@the-guild.dev> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Closes #340 Ref ROUTER-118 - Add E2E tests HTTP/2 support for Subgraphs <-> Router and Router <-> Client - Implement `http2_only` flag to enable H2C for Subgraphs <-> Router --------- Co-authored-by: theguild-bot <bot@the-guild.dev> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
…#942) Bumps the cargo group with 1 update in the / directory: [rustls-webpki](https://github.com/rustls/webpki). Bumps the cargo group with 2 updates in the /apollo-router-workspace directory: [rustls-webpki](https://github.com/rustls/webpki) and [thin-vec](https://github.com/mozilla/thin-vec). Updates `rustls-webpki` from 0.103.12 to 0.103.13 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/rustls/webpki/releases">rustls-webpki's releases</a>.</em></p> <blockquote> <h2>0.103.13</h2> <ul> <li><strong>Fix reachable panic in parsing a CRL</strong>. This was reported to us as <a href="https://github.com/rustls/webpki/security/advisories/GHSA-82j2-j2ch-gfr8">GHSA-82j2-j2ch-gfr8</a>. Users who don't use CRLs are not affected.</li> <li>For name constraints on URI names, we incorrectly processed excluded subtrees in a way which inverted the desired meaning. See <a href="https://redirect.github.com/rustls/webpki/pull/471">rustls/webpki#471</a>. This was a case missing in the fix for <a href="https://github.com/advisories/GHSA-965h-392x-2mh5">https://github.com/advisories/GHSA-965h-392x-2mh5</a>.</li> </ul> <h2>What's Changed</h2> <ul> <li>Actually fail closed for URI matching against excluded subtrees by <a href="https://github.com/djc"><code>@djc</code></a> in <a href="https://redirect.github.com/rustls/webpki/pull/473">rustls/webpki#473</a></li> <li>Prepare 0.103.13 by <a href="https://github.com/ctz"><code>@ctz</code></a> in <a href="https://redirect.github.com/rustls/webpki/pull/474">rustls/webpki#474</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13">https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/rustls/webpki/commit/2879b2ce7a476181ac3050f73fe0835f04728e86"><code>2879b2c</code></a> Prepare 0.103.13</li> <li><a href="https://github.com/rustls/webpki/commit/2c49773d823f48c87db30db7a66c25993c847007"><code>2c49773</code></a> Improve tests for padding of <code>BitStringFlags</code></li> <li><a href="https://github.com/rustls/webpki/commit/4e3c0b393a7bfb9cbe6dcdc8309cbadf8ee00c51"><code>4e3c0b3</code></a> Correct validation of BIT STRING constraints</li> <li><a href="https://github.com/rustls/webpki/commit/39c91d2525a542a7f651a1a62c3462e8115cc39e"><code>39c91d2</code></a> Actually fail closed for URI matching against excluded subtrees</li> <li>See full diff in <a href="https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13">compare view</a></li> </ul> </details> <br /> Updates `rustls-webpki` from 0.103.12 to 0.103.13 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/rustls/webpki/releases">rustls-webpki's releases</a>.</em></p> <blockquote> <h2>0.103.13</h2> <ul> <li><strong>Fix reachable panic in parsing a CRL</strong>. This was reported to us as <a href="https://github.com/rustls/webpki/security/advisories/GHSA-82j2-j2ch-gfr8">GHSA-82j2-j2ch-gfr8</a>. Users who don't use CRLs are not affected.</li> <li>For name constraints on URI names, we incorrectly processed excluded subtrees in a way which inverted the desired meaning. See <a href="https://redirect.github.com/rustls/webpki/pull/471">rustls/webpki#471</a>. This was a case missing in the fix for <a href="https://github.com/advisories/GHSA-965h-392x-2mh5">https://github.com/advisories/GHSA-965h-392x-2mh5</a>.</li> </ul> <h2>What's Changed</h2> <ul> <li>Actually fail closed for URI matching against excluded subtrees by <a href="https://github.com/djc"><code>@djc</code></a> in <a href="https://redirect.github.com/rustls/webpki/pull/473">rustls/webpki#473</a></li> <li>Prepare 0.103.13 by <a href="https://github.com/ctz"><code>@ctz</code></a> in <a href="https://redirect.github.com/rustls/webpki/pull/474">rustls/webpki#474</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13">https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/rustls/webpki/commit/2879b2ce7a476181ac3050f73fe0835f04728e86"><code>2879b2c</code></a> Prepare 0.103.13</li> <li><a href="https://github.com/rustls/webpki/commit/2c49773d823f48c87db30db7a66c25993c847007"><code>2c49773</code></a> Improve tests for padding of <code>BitStringFlags</code></li> <li><a href="https://github.com/rustls/webpki/commit/4e3c0b393a7bfb9cbe6dcdc8309cbadf8ee00c51"><code>4e3c0b3</code></a> Correct validation of BIT STRING constraints</li> <li><a href="https://github.com/rustls/webpki/commit/39c91d2525a542a7f651a1a62c3462e8115cc39e"><code>39c91d2</code></a> Actually fail closed for URI matching against excluded subtrees</li> <li>See full diff in <a href="https://github.com/rustls/webpki/compare/v/0.103.12...v/0.103.13">compare view</a></li> </ul> </details> <br /> Updates `thin-vec` from 0.2.14 to 0.2.16 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/mozilla/thin-vec/blob/main/RELEASES.md">thin-vec's changelog</a>.</em></p> <blockquote> <h1>Version 0.2.16 (2026-04-14)</h1> <ul> <li>Fix reserve() on auto arrays in gecko-ffi mode.</li> <li>Fix two double-drop issues with ThinVec::clear() and ThinVec::into_iter() when the Drop implementation of the item panics.</li> </ul> <h1>Version 0.2.15 (2026-04-08)</h1> <ul> <li>Support AutoTArrays created from Rust in Gecko FFI mode.</li> <li>Add extract_if.</li> <li>Add const new() support behind feature flag.</li> <li>Fix <code>thin_vec</code> macro not being hygienic when recursing</li> <li>Improve extend() performance.</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/mozilla/thin-vec/commit/3c96f1e335e695e6f6d43a3d383a9ea9f1f9d586"><code>3c96f1e</code></a> chore: Bump version to v0.2.16</li> <li><a href="https://github.com/mozilla/thin-vec/commit/df64748355222525c344ecd9d2c9f59a662e1678"><code>df64748</code></a> Fix two panic=unwind issues.</li> <li><a href="https://github.com/mozilla/thin-vec/commit/4e3a217b7fb27e1e8dc08a7307b831f9928e21a5"><code>4e3a217</code></a> ci: Ignore msrv job for now since it just hangs trying to pull deps.</li> <li><a href="https://github.com/mozilla/thin-vec/commit/63c2f5fcf2a5d84f23ed45bbbd053fe1bd2cc96f"><code>63c2f5f</code></a> gecko-ffi: Fix auto-t-array push.</li> <li><a href="https://github.com/mozilla/thin-vec/commit/679781320925000f5dfe34fdea9c440ade34561a"><code>6797813</code></a> tests: Appease clippy.</li> <li><a href="https://github.com/mozilla/thin-vec/commit/af81b17ad58c806af61c2a85e1cc53242a10d2e0"><code>af81b17</code></a> ci: Don't use actions-rs/{cargo,clippy-check} as it's not allowed in mozilla/...</li> <li><a href="https://github.com/mozilla/thin-vec/commit/360f9ef0c91a1068c0acbd5a0fd206911dec5ef3"><code>360f9ef</code></a> Update repository URL and various cleanups</li> <li><a href="https://github.com/mozilla/thin-vec/commit/70bcca0960a7e11056fa3281445d08052421dab5"><code>70bcca0</code></a> chore: Bump version to v0.2.15</li> <li><a href="https://github.com/mozilla/thin-vec/commit/322423b7a6951346e76fcdaa20ee01a91033e180"><code>322423b</code></a> Fix miri error on extract_if().</li> <li><a href="https://github.com/mozilla/thin-vec/commit/eca5334c291cc65885bf524f61e5fe4a679a4152"><code>eca5334</code></a> Don't make push_unchecked public.</li> <li>Additional commits viewable in <a href="https://github.com/mozilla/thin-vec/compare/v0.2.14...v0.2.16">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/graphql-hive/router/network/alerts). </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Arda TANRIKULU <ardatanrikulu@gmail.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
…2 updates (#945) Bumps the npm_and_yarn group with 2 updates in the / directory: [axios](https://github.com/axios/axios) and [follow-redirects](https://github.com/follow-redirects/follow-redirects). Updates `axios` from 1.14.0 to 1.15.2 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/axios/axios/releases">axios's releases</a>.</em></p> <blockquote> <h2>v1.15.2</h2> <p>This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in <code>allowedSocketPaths</code> allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs.</p> <h2>🔒 Security Fixes</h2> <ul> <li><strong>Prototype Pollution Hardening (HTTP Adapter):</strong> Hardened the Node HTTP adapter and <code>resolveConfig</code>/<code>mergeConfig</code>/validator paths to read only own properties and use null-prototype config objects, preventing polluted <code>auth</code>, <code>baseURL</code>, <code>socketPath</code>, <code>beforeRedirect</code>, and <code>insecureHTTPParser</code> from influencing requests. (<strong><a href="https://redirect.github.com/axios/axios/issues/10779">#10779</a></strong>)</li> <li><strong>SSRF via <code>socketPath</code>:</strong> Rejects non-string <code>socketPath</code> values and adds an opt-in <code>allowedSocketPaths</code> config option to restrict permitted Unix domain socket paths, returning <code>AxiosError</code> <code>ERR_BAD_OPTION_VALUE</code> on mismatch. (<strong><a href="https://redirect.github.com/axios/axios/issues/10777">#10777</a></strong>)</li> <li><strong>Supply-chain Hardening:</strong> Added <code>.npmrc</code> with <code>ignore-scripts=true</code>, lockfile lint CI, non-blocking reproducible build diff, scoped CODEOWNERS, expanded <code>SECURITY.md</code>/<code>THREATMODEL.md</code> with provenance verification (<code>npm audit signatures</code>), 60-day resolution policy, and maintainer incident-response runbook. (<strong><a href="https://redirect.github.com/axios/axios/issues/10776">#10776</a></strong>)</li> </ul> <h2>🚀 New Features</h2> <ul> <li><strong><code>allowedSocketPaths</code> Config Option:</strong> New request config option (and TypeScript types) to allowlist Unix domain socket paths used by the Node http adapter; backwards compatible when unset. (<strong><a href="https://redirect.github.com/axios/axios/issues/10777">#10777</a></strong>)</li> </ul> <h2>🐛 Bug Fixes</h2> <ul> <li><strong>Keep-alive Socket Memory Leak:</strong> Installs a single per-socket <code>error</code> listener tracking the active request via <code>kAxiosSocketListener</code>/<code>kAxiosCurrentReq</code>, eliminating per-request listener accumulation, <code>MaxListenersExceededWarning</code>, and linear heap growth under concurrent or long-running keep-alive workloads (fixes <a href="https://redirect.github.com/axios/axios/issues/10780">#10780</a>). (<strong><a href="https://redirect.github.com/axios/axios/issues/10788">#10788</a></strong>)</li> </ul> <h2>🔧 Maintenance & Chores</h2> <ul> <li><strong>Changelog:</strong> Updated <code>CHANGELOG.md</code> with v1.15.1 release notes. (<strong><a href="https://redirect.github.com/axios/axios/issues/10781">#10781</a></strong>)</li> </ul> <p><a href="https://github.com/axios/axios/compare/v1.15.1...v1.15.2">Full Changelog</a></p> <h2>v1.15.1</h2> <p>This release ships a coordinated set of security hardening fixes across headers, body/redirect limits, multipart handling, and XSRF/prototype-pollution vectors, alongside a broad sweep of bug fixes, test migrations, and threat-model documentation updates.</p> <h2>🔒 Security Fixes</h2> <ul> <li><strong>Header Injection Hardening:</strong> Tightened validation and sanitisation across request header construction to close the header-injection attack surface. (<strong><a href="https://redirect.github.com/axios/axios/issues/10749">#10749</a></strong>)</li> <li><strong>CRLF Stripping in Multipart Headers:</strong> Correctly strips CR/LF from multipart header values to prevent injection via field names and filenames. (<strong><a href="https://redirect.github.com/axios/axios/issues/10758">#10758</a></strong>)</li> <li><strong>Prototype Pollution / Auth Bypass:</strong> Replaced unsafe <code>in</code> checks with <code>hasOwnProperty</code> to prevent authentication bypass via prototype pollution on config objects, with additional regression tests. (<strong><a href="https://redirect.github.com/axios/axios/issues/10761">#10761</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10760">#10760</a></strong>)</li> <li><strong><code>withXSRFToken</code> Truthy Bypass:</strong> Short-circuits on any truthy non-boolean value, so an ambiguous config no longer silently leaks the XSRF token cross-origin. (<strong><a href="https://redirect.github.com/axios/axios/issues/10762">#10762</a></strong>)</li> <li><strong><code>maxBodyLength</code> With Zero Redirects:</strong> Enforces <code>maxBodyLength</code> even when <code>maxRedirects</code> is set to <code>0</code>, closing a bypass path for oversized request bodies. (<strong><a href="https://redirect.github.com/axios/axios/issues/10753">#10753</a></strong>)</li> <li><strong>Streamed Response <code>maxContentLength</code> Bypass:</strong> Applies <code>maxContentLength</code> to streamed responses that previously bypassed the cap. (<strong><a href="https://redirect.github.com/axios/axios/issues/10754">#10754</a></strong>)</li> <li><strong>Follow-up CVE Completion:</strong> Completes an earlier incomplete CVE fix to fully close the regression window. (<strong><a href="https://redirect.github.com/axios/axios/issues/10755">#10755</a></strong>)</li> </ul> <h2>🚀 New Features</h2> <ul> <li><strong>AI-Based Docs Translations:</strong> Initial scaffold for AI-assisted translations of the documentation site. (<strong><a href="https://redirect.github.com/axios/axios/issues/10705">#10705</a></strong>)</li> <li><strong><code>Location</code> Request Header Type:</strong> Adds <code>Location</code> to <code>CommonRequestHeadersList</code> for accurate typing of redirect-aware requests. (<strong><a href="https://redirect.github.com/axios/axios/issues/7528">#7528</a></strong>)</li> </ul> <h2>🐛 Bug Fixes</h2> <ul> <li><strong>FormData Handling:</strong> Removes <code>Content-Type</code> when no boundary is present on <code>FormData</code> fetch requests, supports multi-select fields, cancels <code>request.body</code> instead of the source stream on fetch abort, and fixes a recursion bug in form-data serialisation. (<strong><a href="https://redirect.github.com/axios/axios/issues/7314">#7314</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10676">#10676</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10702">#10702</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10726">#10726</a></strong>)</li> <li><strong>HTTP Adapter:</strong> Handles socket-only request errors without leaking keep-alive listeners. (<strong><a href="https://redirect.github.com/axios/axios/issues/10576">#10576</a></strong>)</li> <li><strong>Progress Events:</strong> Clamps <code>loaded</code> to <code>total</code> for computable upload/download progress events. (<strong><a href="https://redirect.github.com/axios/axios/issues/7458">#7458</a></strong>)</li> <li><strong>Types:</strong> Aligns <code>runWhen</code> type with the runtime behaviour in <code>InterceptorManager</code> and makes response header keys case-insensitive. (<strong><a href="https://redirect.github.com/axios/axios/issues/7529">#7529</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10677">#10677</a></strong>)</li> <li><strong><code>buildFullPath</code>:</strong> Uses strict equality in the base/relative URL check. (<strong><a href="https://redirect.github.com/axios/axios/issues/7252">#7252</a></strong>)</li> <li><strong><code>AxiosURLSearchParams</code> Regex:</strong> Improves the regex used for param serialisation to avoid edge-case mismatches. (<strong><a href="https://redirect.github.com/axios/axios/issues/10736">#10736</a></strong>)</li> <li><strong>Resilient Value Parsing:</strong> Parses out header/config values instead of throwing on malformed input. (<strong><a href="https://redirect.github.com/axios/axios/issues/10687">#10687</a></strong>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/axios/axios/blob/v1.x/CHANGELOG.md">axios's changelog</a>.</em></p> <blockquote> <h2>v1.15.2 - April 21, 2026</h2> <p>This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in <code>allowedSocketPaths</code> allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs.</p> <h2>🔒 Security Fixes</h2> <ul> <li><strong>Prototype Pollution Hardening (HTTP Adapter):</strong> Hardened the Node HTTP adapter and <code>resolveConfig</code>/<code>mergeConfig</code>/validator paths to read only own properties and use null-prototype config objects, preventing polluted <code>auth</code>, <code>baseURL</code>, <code>socketPath</code>, <code>beforeRedirect</code>, and <code>insecureHTTPParser</code> from influencing requests. (<strong><a href="https://redirect.github.com/axios/axios/issues/10779">#10779</a></strong>)</li> <li><strong>SSRF via <code>socketPath</code>:</strong> Rejects non-string <code>socketPath</code> values and adds an opt-in <code>allowedSocketPaths</code> config option to restrict permitted Unix domain socket paths, returning <code>AxiosError</code> <code>ERR_BAD_OPTION_VALUE</code> on mismatch. (<strong><a href="https://redirect.github.com/axios/axios/issues/10777">#10777</a></strong>)</li> <li><strong>Supply-chain Hardening:</strong> Added <code>.npmrc</code> with <code>ignore-scripts=true</code>, lockfile lint CI, non-blocking reproducible build diff, scoped CODEOWNERS, expanded <code>SECURITY.md</code>/<code>THREATMODEL.md</code> with provenance verification (<code>npm audit signatures</code>), 60-day resolution policy, and maintainer incident-response runbook. (<strong><a href="https://redirect.github.com/axios/axios/issues/10776">#10776</a></strong>)</li> </ul> <h2>🚀 New Features</h2> <ul> <li><strong><code>allowedSocketPaths</code> Config Option:</strong> New request config option (and TypeScript types) to allowlist Unix domain socket paths used by the Node http adapter; backwards compatible when unset. (<strong><a href="https://redirect.github.com/axios/axios/issues/10777">#10777</a></strong>)</li> </ul> <h2>🐛 Bug Fixes</h2> <ul> <li><strong>Keep-alive Socket Memory Leak:</strong> Installs a single per-socket <code>error</code> listener tracking the active request via <code>kAxiosSocketListener</code>/<code>kAxiosCurrentReq</code>, eliminating per-request listener accumulation, <code>MaxListenersExceededWarning</code>, and linear heap growth under concurrent or long-running keep-alive workloads (fixes <a href="https://redirect.github.com/axios/axios/issues/10780">#10780</a>). (<strong><a href="https://redirect.github.com/axios/axios/issues/10788">#10788</a></strong>)</li> </ul> <h2>🔧 Maintenance & Chores</h2> <ul> <li><strong>Changelog:</strong> Updated <code>CHANGELOG.md</code> with v1.15.1 release notes. (<strong><a href="https://redirect.github.com/axios/axios/issues/10781">#10781</a></strong>)</li> </ul> <p><a href="https://github.com/axios/axios/compare/v1.15.1...v1.15.2">Full Changelog</a></p> <hr /> <h2>v1.15.1 - April 19, 2026</h2> <p>This release ships a coordinated set of security hardening fixes across headers, body/redirect limits, multipart handling, and XSRF/prototype-pollution vectors, alongside a broad sweep of bug fixes, test migrations, and threat-model documentation updates.</p> <h2>🔒 Security Fixes</h2> <ul> <li> <p><strong>Header Injection Hardening:</strong> Tightened validation and sanitisation across request header construction to close the header-injection attack surface. (<strong><a href="https://redirect.github.com/axios/axios/issues/10749">#10749</a></strong>)</p> </li> <li> <p><strong>CRLF Stripping in Multipart Headers:</strong> Correctly strips CR/LF from multipart header values to prevent injection via field names and filenames. (<strong><a href="https://redirect.github.com/axios/axios/issues/10758">#10758</a></strong>)</p> </li> <li> <p><strong>Prototype Pollution / Auth Bypass:</strong> Replaced unsafe <code>in</code> checks with <code>hasOwnProperty</code> to prevent authentication bypass via prototype pollution on config objects, with additional regression tests. (<strong><a href="https://redirect.github.com/axios/axios/issues/10761">#10761</a></strong>, <strong><a href="https://redirect.github.com/axios/axios/issues/10760">#10760</a></strong>)</p> </li> <li> <p><strong><code>withXSRFToken</code> Truthy Bypass:</strong> Short-circuits on any truthy non-boolean value, so an ambiguous config no longer silently leaks the XSRF token cross-origin. (<strong><a href="https://redirect.github.com/axios/axios/issues/10762">#10762</a></strong>)</p> </li> <li> <p><strong><code>maxBodyLength</code> With Zero Redirects:</strong> Enforces <code>maxBodyLength</code> even when <code>maxRedirects</code> is set to <code>0</code>, closing a bypass path for oversized request bodies. (<strong><a href="https://redirect.github.com/axios/axios/issues/10753">#10753</a></strong>)</p> </li> <li> <p><strong>Streamed Response <code>maxContentLength</code> Bypass:</strong> Applies <code>maxContentLength</code> to streamed responses that previously bypassed the cap. (<strong><a href="https://redirect.github.com/axios/axios/issues/10754">#10754</a></strong>)</p> </li> <li> <p><strong>Follow-up CVE Completion:</strong> Completes an earlier incomplete CVE fix to fully close the regression window. (<strong><a href="https://redirect.github.com/axios/axios/issues/10755">#10755</a></strong>)</p> </li> </ul> <h2>🚀 New Features</h2> <ul> <li><strong>AI-Based Docs Translations:</strong> Initial scaffold for AI-assisted translations of the documentation site. (<strong><a href="https://redirect.github.com/axios/axios/issues/10705">#10705</a></strong>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/axios/axios/commit/582934382e4e0e0bcb679c628071a4203e93cf57"><code>5829343</code></a> chore(release): prepare release 1.15.2 (<a href="https://redirect.github.com/axios/axios/issues/10789">#10789</a>)</li> <li><a href="https://github.com/axios/axios/commit/4709a48fa2717ba97f43f5432d48ca4e26c2d326"><code>4709a48</code></a> fix: added fix for memory leak in sockets (<a href="https://redirect.github.com/axios/axios/issues/10788">#10788</a>)</li> <li><a href="https://github.com/axios/axios/commit/be3336014e01f9a4fc1f8aef15303cf7daaf58db"><code>be33360</code></a> chore: update changelog (<a href="https://redirect.github.com/axios/axios/issues/10781">#10781</a>)</li> <li><a href="https://github.com/axios/axios/commit/47915144662f2733e6c051bdcb895a8c8f0586aa"><code>4791514</code></a> fix: more header pollutions (<a href="https://redirect.github.com/axios/axios/issues/10779">#10779</a>)</li> <li><a href="https://github.com/axios/axios/commit/6feafcff6c2dbafe206161c5d09e38e1d36af66f"><code>6feafcf</code></a> fix: socket issue (<a href="https://redirect.github.com/axios/axios/issues/10777">#10777</a>)</li> <li><a href="https://github.com/axios/axios/commit/302e2739c602f00e323d4f3f5c79500647633a73"><code>302e273</code></a> docs: update docs, add a couple actions etc (<a href="https://redirect.github.com/axios/axios/issues/10776">#10776</a>)</li> <li><a href="https://github.com/axios/axios/commit/ac42446be51300fe214ba3c6e40cc95f34fd6871"><code>ac42446</code></a> chore(release): prepare release 1.15.1 (<a href="https://redirect.github.com/axios/axios/issues/10767">#10767</a>)</li> <li><a href="https://github.com/axios/axios/commit/908f2206b6bfeff67236784abce85935698ac1d9"><code>908f220</code></a> docs: update threatmodel (<a href="https://redirect.github.com/axios/axios/issues/10765">#10765</a>)</li> <li><a href="https://github.com/axios/axios/commit/f93f8155250c2e066205521eda05ae22983a1f6d"><code>f93f815</code></a> docs: added docs around potential decompressions bomb (<a href="https://redirect.github.com/axios/axios/issues/10763">#10763</a>)</li> <li><a href="https://github.com/axios/axios/commit/1728aa1b15b8857f970611fd8983c06b423fc486"><code>1728aa1</code></a> fix: short-circuits on any truthy non-boolean in withXSRFToken (<a href="https://redirect.github.com/axios/axios/issues/10762">#10762</a>)</li> <li>Additional commits viewable in <a href="https://github.com/axios/axios/compare/v1.14.0...v1.15.2">compare view</a></li> </ul> </details> <br /> Updates `follow-redirects` from 1.15.11 to 1.16.0 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/follow-redirects/follow-redirects/commit/0c23a223067201c368035e82954c11eb2578a33b"><code>0c23a22</code></a> Release version 1.16.0 of the npm package.</li> <li><a href="https://github.com/follow-redirects/follow-redirects/commit/844c4d302ac963d29bdb5dc1754ec7df3d70d7f9"><code>844c4d3</code></a> Add sensitiveHeaders option.</li> <li><a href="https://github.com/follow-redirects/follow-redirects/commit/5e8b8d024e2c76f804a284258e585ecb49a575be"><code>5e8b8d0</code></a> ci: add Node.js 24.x to the CI matrix</li> <li><a href="https://github.com/follow-redirects/follow-redirects/commit/7953e2255aa0b93602eed3804f3bc5e6923a03af"><code>7953e22</code></a> ci: upgrade GitHub Actions to use setup-node@v6 and checkout@v6</li> <li><a href="https://github.com/follow-redirects/follow-redirects/commit/86dc1f86e4b56bcd642c78384d51f10f123aea75"><code>86dc1f8</code></a> Sanitizing input.</li> <li>See full diff in <a href="https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/graphql-hive/router/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: knope-bot[bot] <152252888+knope-bot[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
How to install: https://nexte.st/docs/installation/pre-built-binaries/ Closes #819 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: Dotan Simha <dotan@the-guild.dev> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
#951) Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
The `http.server` span now includes:
- `client.address` and `client.port` from a configurable request header
- `network.peer.address` and `network.peer.port` from the address of the
incoming connection
```yaml
telemetry:
client_identification:
# Default - use socket peer only
ip_header: null
# Header name - use the left-most valid IP from the header
ip_header: x-forwarded-for
# Trusted proxies - only trust the header when the socket peer is trusted
ip_header:
name: x-forwarded-for
trusted_proxies:
- 10.0.0.0/8
- 192.168.0.0/16
```
In trusted proxies scenario, the Router scans the configured header from
right to left, skips trusted proxy IP ranges, and records the first
non-trusted IP as `client.address`.
If no valid client IP can be resolved, the Router falls back to the
socket peer address.
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Removes `bollard` dependency and e2e Docker test helper. Implements an in-process redis server for `response_cache` plugin tests and switch tests to inline config. --- We have a test failure from time to time and we rely on Docker yada yada, so I created a tiny redis server for mocking as we really only use it for a single plugin test. Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
#882) The benchmark suite now uses `wrk` for lower-overhead HTTP load testing while preserving response validation. This keeps throughput measurements focused on router performance and still fails the benchmark when responses have invalid status codes, GraphQL errors, or unexpected response bodies. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
…fragment being dropped (#946) - [x] Run `cargo clippy --fix` to auto-fix lint issues - [x] Run `cargo fmt` to fix formatting issues - [x] Add node-addon to changeset - [x] Fix directive semantics: propagate parent fragment directives into specific sub-fragments (only adding directives not already present) instead of wrapping in an outer fragment — avoids redundant nesting when sub-fragment already carries the same condition as the parent - [x] Fix regression test to actually exercise `expand_abstract_fragment` using a concrete parent type (`account(id:)`) with an abstract type fragment (`... on Node`) - [x] Update insta snapshots for the updated test and the affected normalization snapshot --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ardatan <20847995+ardatan@users.noreply.github.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Arda TANRIKULU <ardatanrikulu@gmail.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Implements Coprocessor feature to allow external service to intercept and modify GraphQL requests/responses at different stages of the request pipeline. Design doc: https://guild-oss.slack.com/docs/TAYJ1FSUA/F0ASCC492KS - [x] context #937 - [x] public schema sdl - [ ] execution stage (future - when demand) - [ ] subgraph stage (future - when demand) - [ ] tls (for http/2) (future - when demand) - [ ] different endpoints per stage (future - when demand) - [x] benchmark (~11k drops to ~9.2k rps) --- This PR introduces Coprocessor API. The goal is to let users extend Router in language agnostic way. ## Building blocks - `Stage` trait that represents a step in the request's pipeline. Every `Stage` implementation defines how to build the http request to coprocessor service, how to parse the response and how to apply mutations. Every stage can `"continue"` or `"break": <status_code>` the flow early. - `CoprocessorRuntime` wires these stages into one API that is called by Hive Router. - `RequestContext` - introduced in #937 Every stage has configurable `include` object, so users can opt into the pieces of data they need. The `include`'s set of fields is different for every stage. ```yaml include: body: true headers: true ``` The `context` (that represents `RequestContext`) in all of stages allows users to select what should be passed to the coprocessor service. ```yaml include: context: true # all context: ["hive::operation::name"] # only operation's name ``` Currently, we support those stages (they run in this order): - `router.request` - `graphql.request` - `graphql.analysis` - `graphql.response` - `router.response` Those stages are covered in the [design doc](https://guild-oss.slack.com/docs/TAYJ1FSUA/F0ASCC492KS). The design is forward‑compatible, adding new stages (like execution or subgraph stages) can be added later by implementing the Stage trait. ## Coprocessor service A coprocessor service can be accessed over http or unix domain socket with http/1, http/2 or h2c protocol. When using UDS with Router, it's in user's hand to create or truncate the socket file. Hive Router only "talks" to it. ## Observability I covered metrics, spans and logs around coprocessor runtime. Metrics: - `hive.router.coprocessor.requests_total{"coprocessor.stage"}` - Total number of coprocessor requests - `hive.router.coprocessor.duration{"coprocessor.stage"}` - Duration of coprocessor requests - `hive.router.coprocessor.errors_total{"coprocessor.stage"}` - Total number of coprocessor errors - http/uds client of coprocessor api, supports also the `http.client` metrics so users will know about http status codes, responses etc. Spans: - `coprocessor` span with `coprocessor.stage` and `coprocessor.id` attributes wraps all stage calls - `http.client` span wraps http requests to coprocessor service Logs: - Every coprocessor response, error, request action has corresponding log line (usually with `debug` level, except `error` for errors) with attributes allowing to correlate coprocessor logs with http logs and graphql logs. There's a link :) Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
GHSA-w5hq-g745-h8pq The `uuid` library is used by `@graphql-hive/laboratory` so it's part of Hive Router, but UUIDs are never user-controlled. The second instance of `uuid` is inside fed audit's dependencies within the apollo/* libs, that are not user-controlled and also a dev dependency so it has zero impact. Closes #950 Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
…e spread directives (#956) Closes #939 Fixes query normalization for fragment spreads and preserves `@include` and `@skip` directives on fragment spreads. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Ref ROUTER-282 --------- Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Co-authored-by: kamilkisiela <8167190+kamilkisiela@users.noreply.github.com>
Contributor
Author
Rebased. Branch is now up to date and pushed at |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
main💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.