From 11a76e18e7767d99b3b0ed6ce0bbf7d62080df63 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Mon, 1 Sep 2025 02:12:21 -0700 Subject: [PATCH] September2025 Edition & Changelog --- changelogs/September2025.md | 215 ++++++++++++++++ scripts/generate-contributor-list.mjs | 336 ++++++++++++++++++++++++++ spec/GraphQL.md | 2 +- 3 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 changelogs/September2025.md create mode 100644 scripts/generate-contributor-list.mjs diff --git a/changelogs/September2025.md b/changelogs/September2025.md new file mode 100644 index 000000000..70eec1db6 --- /dev/null +++ b/changelogs/September2025.md @@ -0,0 +1,215 @@ +# September 2025 Changelog + +This describes the set of changes since the last edition of the GraphQL +specification, [October2021](https://spec.graphql.org/October2021/) (see +[prior changelog](./October2021.md)). It's intended to ease the review of +changes since the last edition for reviewers or curious readers, but is not +normative. Please read the +[specification document](https://spec.graphql.org/September2025/) itself for +full detail and context. + +## Thank you, contributors! + +The last few years have seen GraphQL reach a wide breath of use, powering +internal and external APIs at startups and enterprises, and integrating with +various platforms and tools. Work contributed to GraphQL has fit into two +priorities: + +1. **Maintain a stable base.** With a huge ecosystem built atop GraphQL, it's + our responsibility to put stability above all else. The vast majority of work + has fixed inconsistencies, improved behavior in edge and corner cases, + improve security, and avoid performance pitfalls. + +2. **Provide a productive expressive query language.** GraphQL was designed with + API consumers in mind, and this spec release includes significant + improvements to the GraphQL query engine and language. + +Significant contributions have been made since the last edition of the spec, and +both of these priories remain active areas of investment with many exciting RFCs +still in the works. + +Over 100 commits made to the GraphQL spec since the last edition, most of which +have dozens or hundreds of comments! It's a huge amount of work to champion +these changes, and a great responsibility to evolve the technical foundations +for this community and ecosystem. + +Thank you! + +- [@leebyron](https://github.com/leebyron), Editor + +## Contributors + +Anyone is welcome to join working group meetings and contribute to GraphQL. See +[Contributing.md](https://github.com/graphql/graphql-spec/blob/main/CONTRIBUTING.md) +for more information. Thank you to these community members for their technical +contribution to this edition of the GraphQL specification. + +| Author | Github | +| ------------------ | -------------------------------------------------------- | +| Alex Reilly | [@twof](https://github.com/twof) | +| Andreas Marek | [@andimarek](https://github.com/andimarek) | +| Ben Kraft | [@benjaminjkraft](https://github.com/benjaminjkraft) | +| Benedikt Franke | [@spawnia](https://github.com/spawnia) | +| Benjie | [@benjie](https://github.com/benjie) | +| Benoit 'BoD' Lubek | [@BoD](https://github.com/BoD) | +| dondonz | [@dondonz](https://github.com/dondonz) | +| dugenkui | [@dugenkui03](https://github.com/dugenkui03) | +| Glen | [@tempire](https://github.com/tempire) | +| Ivan Goncharov | [@IvanGoncharov](https://github.com/IvanGoncharov) | +| Ivan Maximov | [@sungam3r](https://github.com/sungam3r) | +| James Bellenger | [@jbellenger](https://github.com/jbellenger) | +| Jan Melcher | [@Yogu](https://github.com/Yogu) | +| Jason Dent | [@Jason3S](https://github.com/Jason3S) | +| Jeff Auriemma | [@bignimbus](https://github.com/bignimbus) | +| Jovi De Croock | [@JoviDeCroock](https://github.com/JoviDeCroock) | +| Kevin Smithson | [@smitt04](https://github.com/smitt04) | +| Lee Byron | [@leebyron](https://github.com/leebyron) | +| Mark Larah | [@magicmark](https://github.com/magicmark) | +| Martin Bonnin | [@martinbonnin](https://github.com/martinbonnin) | +| Michael Staib | [@michaelstaib](https://github.com/michaelstaib) | +| Mike Solomon | [@msolomon](https://github.com/msolomon) | +| PascalSenn | [@PascalSenn](https://github.com/PascalSenn) | +| Renée | [@goto-bus-stop](https://github.com/goto-bus-stop) | +| Rob Richard | [@robrichard](https://github.com/robrichard) | +| Roman Ivantsov | [@rivantsov](https://github.com/rivantsov) | +| Shane Krueger | [@Shane32](https://github.com/Shane32) | +| Stephen Spalding | [@fotoetienne](https://github.com/fotoetienne) | +| Thomas Heyenbrock | [@thomasheyenbrock](https://github.com/thomasheyenbrock) | +| Yaacov Rydzinski | [@yaacovCR](https://github.com/yaacovCR) | + +Generated with: + +```sh +node scripts/generate-contributor-list.mjs October2021..HEAD +``` + +## Notable contributions + +A few notable changes in this edition: + +- OneOf input objects, aka "input unions" + ([#825](https://github.com/graphql/graphql-spec/pull/825)) +- Schema coordinates as a standard for GraphQL tooling and reporting + ([#794](https://github.com/graphql/graphql-spec/pull/794)) +- Descriptions on documents, in motivation for AI-consumers + ([#1170](https://github.com/graphql/graphql-spec/pull/1170)) +- Broader support for deprecation across a GraphQL Schema + ([#805](https://github.com/graphql/graphql-spec/pull/805) + [#1040](https://github.com/graphql/graphql-spec/pull/1040) + [#1053](https://github.com/graphql/graphql-spec/pull/1053) + [#1142](https://github.com/graphql/graphql-spec/pull/1142)) +- GraphQL language has been updated to support the full Unicode range + ([#849](https://github.com/graphql/graphql-spec/pull/849)) +- Countless editorial improvements! The spec is much easier to read and + contribute to, with fewer ambiguities. + +## Changeset + +- [Github: all Accepted RFC PRs merged since last spec cut](https://github.com/graphql/graphql-spec/pulls?q=is%3Apr+is%3Amerged+base%3Amain+merged%3A2021-10-01..2025-09-03+label%3A%22%F0%9F%8F%81+Accepted+%28RFC+3%29%22) +- [Github: all Editorial PRs merged since last spec cut](https://github.com/graphql/graphql-spec/pulls?page=1&q=is%3Apr+is%3Amerged+base%3Amain+merged%3A2021-10-01..2025-09-03+label%3A%22%E2%9C%8F%EF%B8%8F+Editorial%22) +- [Github: all changes since last spec cut](https://github.com/graphql/graphql-spec/compare/October2021...f29fbcd2ab5af763fce7ad62896eb62465a669b3) + +Listed in reverse-chronological order (latest commit on top). + +| Hash | Change | Authors | +| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [f29fbcd](https://github.com/graphql/graphql-spec/commit/f29fbcd2ab5af763fce7ad62896eb62465a669b3) | gpt guided style guide improvements (#1190) | Lee Byron | +| [0157a79](https://github.com/graphql/graphql-spec/commit/0157a79b8873f0f575d254270ee700f205b0f7b2) | Fix link format for input coercion rules (#1189) | Yaacov Rydzinski | +| [f70abe2](https://github.com/graphql/graphql-spec/commit/f70abe24469d0c8bd374211c8eb621cd421b69f9) | Recommend that order of unordered collections is maintained where possible (#1092) | Benjie Lee Byron | +| [11c6664](https://github.com/graphql/graphql-spec/commit/11c6664e2ea65eddd812bac73b9e7341b4610292) | condense the spec defs appendix (#1186) | Lee Byron | +| [10331b0](https://github.com/graphql/graphql-spec/commit/10331b0f303a1df76d465f153704bc106ed449e6) | RFC: OneOf Input Objects (#825) | Benjie Michael Staib Shane Krueger Yaacov Rydzinski Glen Lee Byron | +| [cd0b8bd](https://github.com/graphql/graphql-spec/commit/cd0b8bd14c0a741ee2ea92d24b97985c6522e5c9) | Pull copyright and license into repo-level LICENSE.md (#1172) | Lee Byron | +| [1e29b8a](https://github.com/graphql/graphql-spec/commit/1e29b8aea0e098663e43dba4693e88c6b033f6f8) | Schema Coordinates (#794) | Mark Larah Benjie Gillam Lee Byron Lee Byron Martin Bonnin Benoit 'BoD' Lubek | +| [bc4ddea](https://github.com/graphql/graphql-spec/commit/bc4ddea0c9121a3e4effd4bbdcc0ebdfaae6e79a) | Fix CoerceArgumentValues() hasValue (#1056) | Benjie | +| [b1a23de](https://github.com/graphql/graphql-spec/commit/b1a23de96f86865b2bc7f8bd613900d4afd10cc0) | Fix typo in merged "executable documents" PR (#1176) | Benjie Lee Byron | +| [9939469](https://github.com/graphql/graphql-spec/commit/9939469d1315dc2f7458c885699996e2a78516f1) | Minor editorial tweaks following the merge of #1039 (#1175) | Benjie | +| [468d848](https://github.com/graphql/graphql-spec/commit/468d84881646b9da00511c7ce68febdfc9a1875b) | editorial: move normative clause above example in descriptions (#1173) | Lee Byron | +| [7eb8983](https://github.com/graphql/graphql-spec/commit/7eb8983955fbbc047da301d6a58c450a175cea5b) | [RFC] Default value coercion rules (#793) | Benjie Lee Byron | +| [fca6653](https://github.com/graphql/graphql-spec/commit/fca66537c5e1f0743eb67de76d520cde0892e6dc) | Add descriptions to executable documents 2025 Update (#1170) | Stephen Spalding Ivan Goncharov Glen Lee Byron | +| [17e2a47](https://github.com/graphql/graphql-spec/commit/17e2a4739c2189b653de3f7c721bf1fad7ac721a) | Replace `ExecuteSelectionSet` with `ExecuteCollectedFields` (#1039) | Benjie Yaacov Rydzinski Lee Byron | +| [e71805e](https://github.com/graphql/graphql-spec/commit/e71805e0745b61239824475fdfff8f0fd116b83f) | Fixed unclear wording in the validation section (#1096) | PascalSenn Benjie Lee Byron | +| [ccf23e3](https://github.com/graphql/graphql-spec/commit/ccf23e304bc4314511cb0712b0787d2528783337) | Define Data Collections used in the spec (#1102) | Benjie Lee Byron | +| [ac483bd](https://github.com/graphql/graphql-spec/commit/ac483bdda45b8fe09fabaa50940e4cdfa24a0372) | Add validation rule that operation types exist (#955) | Ben Kraft Benjie Gillam Shane Krueger | +| [883c759](https://github.com/graphql/graphql-spec/commit/883c7592d88579fbd13070d5c17f25f621abb8d7) | Do not exclude schema keyword if schema has description (#1167) | Benjie | +| [df9f4f8](https://github.com/graphql/graphql-spec/commit/df9f4f8330109e1aca06af27b78ac1055a27e3a0) | Define "execution result" and "request error result" (#1159) | Rob Richard Benjie | +| [9d0710e](https://github.com/graphql/graphql-spec/commit/9d0710e050f10c1ff5d0c659ec289713a6ebe8b9) | Add Appendix C - Specified Type System Definitions (#1037) | Martin Bonnin Benjie Lee Byron | +| [a44d4ec](https://github.com/graphql/graphql-spec/commit/a44d4ec24e4c1263ab984a201cf9207c5a4dd549) | [RFC] Prevent @skip and @include on root subscription selection set (#860) | Benjie | +| [3b0d8e6](https://github.com/graphql/graphql-spec/commit/3b0d8e606609b196d9ae643f4e0f0707700d46cc) | Add 'Assert' consistency to algorithm format check (#1168) | Benjie | +| [646f937](https://github.com/graphql/graphql-spec/commit/646f937c5cf005fec43a4a3749e0c5b5406cb601) | make `includeDeprecated` non nullable (#1142) | Martin Bonnin | +| [73d8b26](https://github.com/graphql/graphql-spec/commit/73d8b2624270b3cc22255125479ad10a7591f6d3) | Implementations may not deprecate a field that the interface hasn't deprecated (#1053) | Benjie Lee Byron | +| [1f690d4](https://github.com/graphql/graphql-spec/commit/1f690d48149505c8a0fa94f2f9a525b69d876ace) | Clarify 'Values of Correct Type' rule relates to literals (#1118) | Benjie | +| [a1884bb](https://github.com/graphql/graphql-spec/commit/a1884bb9386b6105ff81ad628a08b57e1237ccd5) | 3.13 Directive validation edits (#1089) | James Bellenger Benjie | +| [c855454](https://github.com/graphql/graphql-spec/commit/c855454d518ecd7fd2eaf8e7d696f0c94104709a) | 3.10 Input Objects - clarify lists are permitted as Input fields (#1068) | Mike Solomon Benjie | +| [586cbed](https://github.com/graphql/graphql-spec/commit/586cbedc4e38d27512965bd641a0588658dd6d99) | Add 'extensions' to request (#976) | Benjie Lee Byron | +| [4390617](https://github.com/graphql/graphql-spec/commit/43906173238470b76173948053bfcb2a2bfc2e99) | Consistently use 'response name' not 'response key' (#1147) | Benjie Lee Byron | +| [521ef5b](https://github.com/graphql/graphql-spec/commit/521ef5b200e61db7edc6c72dd4619af15eaa872a) | Rename field error to execution error; define response position (#1152) | Benjie Lee Byron | +| [4abd86e](https://github.com/graphql/graphql-spec/commit/4abd86e33c70bd880e6f5605422c59343f3aa30c) | Consistently use result map when referring to objectTypes selection set result (#1148) | Rob Richard | +| [0ab3d4b](https://github.com/graphql/graphql-spec/commit/0ab3d4b80103beb265777f95d202312a7dd7d63d) | Ensure algorithm steps are always wrapped in braces (#1146) | Benjie | +| [b7c57ea](https://github.com/graphql/graphql-spec/commit/b7c57ea7f218474701c52fd9d1ee383600414343) | Fix choice of language used in algorithm (#1134) | Benjie | +| [b41339a](https://github.com/graphql/graphql-spec/commit/b41339a0fd22fb3ca9a1a6110334a7094f1f7643) | Editorial: Add response stream to Response Section (#1135) | Rob Richard | +| [a1c025f](https://github.com/graphql/graphql-spec/commit/a1c025f3b2d9c69ad208dfc5a28639746c60ee09) | Field merging validation: clarify pair members are distinct (#1136) | Andreas Marek | +| [e9ac8c8](https://github.com/graphql/graphql-spec/commit/e9ac8c8ce46fda6759a29adc61949b9b9356344a) | Editorial: move "Path" to it's own section (#1129) | Rob Richard Benjie | +| [2073bc8](https://github.com/graphql/graphql-spec/commit/2073bc888d92692cd465db47cc544109db9c4181) | Change 'original' to 'previous' to clarify multiple extensions (#1123) | Benjie | +| [5bf400e](https://github.com/graphql/graphql-spec/commit/5bf400e3b2867e6366fc27616b777ccd3388bbfd) | "data" and "errors" appear in the "response", not the "result" (#1130) | Benjie | +| [c37a4a4](https://github.com/graphql/graphql-spec/commit/c37a4a400e2ff61ade66c1c5947e0845a6b85c9f) | Editorial changes for Event Streams (#1099) | Lee Byron | +| [34730e8](https://github.com/graphql/graphql-spec/commit/34730e86a151643f51106ac1a1f35985d3da8caa) | Make the reason argument in `@deprecated` non-nullable (#1040) | Martin Bonnin | +| [df1acea](https://github.com/graphql/graphql-spec/commit/df1acea882d28df22184a7c12449864b239b9dd7) | Fix coercion table for list (#1057) | Benjie | +| [7073e3a](https://github.com/graphql/graphql-spec/commit/7073e3a096c0a3fea9e605079a885d988a59d79f) | enhance(ResolveFieldValue): note that list items may be async (#1066) | Yaacov Rydzinski | +| [e5bddd9](https://github.com/graphql/graphql-spec/commit/e5bddd9c31fcd6796698e4fd059514a5a035bf20) | Fix punctuation in grammar rule (#1084) | Benjie | +| [a80f9ff](https://github.com/graphql/graphql-spec/commit/a80f9ff13b7ef9957d93ebe09e2dc4f2c93aede4) | Consistently spell 'implementers' (#1087) | Benjie | +| [497e333](https://github.com/graphql/graphql-spec/commit/497e333c35a29dbe1ab3d1f24c31a59789843fb6) | Fix reference mistake in subscription execution (#994) | Jan Melcher | +| [7485a34](https://github.com/graphql/graphql-spec/commit/7485a342b1c5f0ba4dbbe270d1f6863f55f7180b) | Reformat the parts of an execution request (#1090) | Benjie | +| [8076f1e](https://github.com/graphql/graphql-spec/commit/8076f1e32a305188b41a71c2fec382b72186b424) | chore: add clarifying note for composite and expand term (#1078) | Jovi De Croock Benjie | +| [b5ecff0](https://github.com/graphql/graphql-spec/commit/b5ecff0b7dfd1ebe8bb8a3de7e9180b93da73c53) | Add definition of "selection set" and clarify serial execution examples (#1032) | Benjie | +| [32d24f6](https://github.com/graphql/graphql-spec/commit/32d24f6fda912253d395639561a88deae0863a8e) | Be strict about error paths format (#1073) | Martin Bonnin Benjie | +| [4ab71e3](https://github.com/graphql/graphql-spec/commit/4ab71e330a41742d0b169be94d42055fad26b130) | Add missing . (#1088) | Benjie | +| [a5da8bb](https://github.com/graphql/graphql-spec/commit/a5da8bb39e3ae44e38e7744e0b7bcb2dba453f97) | ID must always serialize as String would (#1086) | Benjie | +| [0ba7cdf](https://github.com/graphql/graphql-spec/commit/0ba7cdf781125779498df3b45a5b97a697a439ba) | Enforce consistent punctuation in algorithms (#1069) | Benjie | +| [8682a86](https://github.com/graphql/graphql-spec/commit/8682a86cd66cdc6d054c678a3f3ef7a32457e5fc) | Fix 'response error' -> 'request error' (#1016) | Benjie | +| [feac5a5](https://github.com/graphql/graphql-spec/commit/feac5a54c6a95c1d4f7804bfaeb268c8bd206f2c) | Fix punctuation in some algorithms (#1067) | Yaacov Rydzinski | +| [56d6107](https://github.com/graphql/graphql-spec/commit/56d61073137caac3dbea6ec8c3652cc3c8b90d86) | Fix heading level for Required Arguments validation rule (#1055) | Renée | +| [3adfcca](https://github.com/graphql/graphql-spec/commit/3adfcca73644234fbbbb062a5cec9e7703419a9f) | Add explicit definition for BlockString (#1042) | Benjie | +| [6b7c2c4](https://github.com/graphql/graphql-spec/commit/6b7c2c45d7136f7fa727cd64b994d2573d4e8014) | Remove "subscriptions is a significant change" sentence (#983) | Roman Ivantsov Benjie Roman Ivantsov | +| [a5cc6ea](https://github.com/graphql/graphql-spec/commit/a5cc6ea0c9f3850f58ef1947d8eda8c6e593b95a) | Clarify that selection sets cannot be empty (#1025) | Benjie | +| [afc0a35](https://github.com/graphql/graphql-spec/commit/afc0a35d271ba9502c3c68aeda6e6c6fbc223774) | Add links to contributed custom scalar specs at scalars.graphql.org (#1009) | dondonz <13839920+dondonz@users.noreply.github.com> Benjie | +| [4e93488](https://github.com/graphql/graphql-spec/commit/4e93488096479c3fcfcc905126c8d157ad2e8c4c) | Fix ambiguity around when schema definition may be omitted (#987) | Benjie Lee Byron | +| [12b7ad7](https://github.com/graphql/graphql-spec/commit/12b7ad7f0fe6ac3996fd5a2bc564357cd2dcb0bc) | add explanation about argument name uniqueness. (#891) | dugenkui Benjie Gillam | +| [559063c](https://github.com/graphql/graphql-spec/commit/559063cb37c14ed74050f73efd3971ee13ff134d) | Change 'server' to 'service' (#1005) | Benjie | +| [cbb8354](https://github.com/graphql/graphql-spec/commit/cbb83545cf8eeb5106856dda8f9d0b750f553124) | Fix broken license link (#1007) | Lee Byron | +| [e736f78](https://github.com/graphql/graphql-spec/commit/e736f78b3cb5c8abb1d6b2ec5e5102de455f98ed) | Add a style guide to the specification (#1003) | Benjie | +| [193fba3](https://github.com/graphql/graphql-spec/commit/193fba3912a43a5e2c9741f4269d279a018e2829) | field merging - field TYPES must not differ (#979) | Roman Ivantsov Roman Ivantsov Benjie Lee Byron | +| [3d03cab](https://github.com/graphql/graphql-spec/commit/3d03cab7a8ffc455387f486fbab5155c2a7c7f3e) | P30: Fixed the explanatory text for algorithm checking uniqueness of non-repeatable directives (#975) | Roman Ivantsov Roman Ivantsov Lee Byron | +| [342b838](https://github.com/graphql/graphql-spec/commit/342b8381ee0ebd25071697e2cc7e784d539700c7) | P34: implementing field type is either exact match or of covariant type (#974) | Roman Ivantsov Roman Ivantsov Lee Byron | +| [ab865f9](https://github.com/graphql/graphql-spec/commit/ab865f95c000b48bdbe9134c4e53573f1996a5c1) | Provide explicit ref to Value Completion section (#982) | Roman Ivantsov Benjie Gillam Roman Ivantsov | +| [edda836](https://github.com/graphql/graphql-spec/commit/edda83613a999c562afbfec6fba2accb96f058a4) | Changed 'must NOT BE' to 'must not be' (#980) | Roman Ivantsov Roman Ivantsov | +| [3aa021f](https://github.com/graphql/graphql-spec/commit/3aa021fb3651710508a37e13c71b7268189982f9) | separate out IsSubType from IsValidImplementationFieldType (#977) | Yaacov Rydzinski | +| [47a6bfd](https://github.com/graphql/graphql-spec/commit/47a6bfdba35ad9b96cba501a52593d3f04c8c5e9) | Editorial: Clarify intro for inline fragments (#969) | Lee Byron | +| [3885a64](https://github.com/graphql/graphql-spec/commit/3885a64f3dc0f9cb64b43aaf1f77e661d98f4dca) | Editorial: Error Terminology (#966) | Lee Byron Roman Ivantsov Benjie Gillam | +| [9a96fc4](https://github.com/graphql/graphql-spec/commit/9a96fc40f2307af15eecc3a257f85ec49adf50d9) | Editorial: Clarity about subject being a GraphQL service or system (#965) | Lee Byron Roman Ivantsov | +| [57bd86d](https://github.com/graphql/graphql-spec/commit/57bd86d779482e9167d2113a9ba926e2ecb74dcc) | Editorial: Fix reference to object in interface introspection (#964) | Lee Byron Roman Ivantsov | +| [299ce69](https://github.com/graphql/graphql-spec/commit/299ce69388aa7fb0012246e377950cf3fd41439d) | Editorial: Remove inaccurate statement about line terminator within tokens (#963) | Lee Byron Roman Ivantsov | +| [1b8fe7a](https://github.com/graphql/graphql-spec/commit/1b8fe7a68f3552a614ab467ea1313947e9f7b1fc) | Leaf field selections clarification (#958) | Benjie Gillam Roman Ivantsov Lee Byron | +| [a91e158](https://github.com/graphql/graphql-spec/commit/a91e15832682931585b8dad46fc8996df1d29735) | Light editorial around delivery agnostic subscriptions (#959) | Benjie Gillam Roman Ivantsov | +| [bb95060](https://github.com/graphql/graphql-spec/commit/bb950608a161af5fc255835bcce10bc160ae1bc8) | Move punctuation outside quotation marks (#962) | Benedikt Franke | +| [4de8782](https://github.com/graphql/graphql-spec/commit/4de87822d2bc807b746c6a37f6a99bb54d705185) | Clarify query shorthand relationship with directives (#873) | Benjie Gillam Lee Byron | +| [94f73f4](https://github.com/graphql/graphql-spec/commit/94f73f48c12a6bdafa9dc729a89482aa7917303f) | Allow deprecation of input values (field args, directive args, input fields) (#805) | Ivan Goncharov Kevin Smithson Lee Byron Ivan Maximov Stephen Spalding | +| [6b69577](https://github.com/graphql/graphql-spec/commit/6b69577b6e854099ce2595849e0b887c13fe300c) | use `findDog` query from example schema only after defining it (#927) | Thomas Heyenbrock | +| [7dd73e7](https://github.com/graphql/graphql-spec/commit/7dd73e7607665bc84dbac504ae83058c996cffcf) | Define request, note it is transport independent (#949) | Benjie Gillam | +| [84ec339](https://github.com/graphql/graphql-spec/commit/84ec33914393bb46e49e5d7a7fb05e0626daa7a8) | RFC: Allow full unicode range (#849) | Lee Byron Andreas Marek | +| [00b88f0](https://github.com/graphql/graphql-spec/commit/00b88f05c4ae049f649e163a4914c94e58a784bf) | Fix formatting | Lee Byron | +| [a61e35d](https://github.com/graphql/graphql-spec/commit/a61e35d301229c494b8bbd69ab0140b33884a95a) | consistent indentation and punctuation (#925) | Thomas Heyenbrock | +| [266fcca](https://github.com/graphql/graphql-spec/commit/266fcca386518d4b05ed58f2d8f57f1fdaaad7f1) | Rename VariableDefinitions to VariablesDefinition (#916) | Ivan Maximov | +| [7908822](https://github.com/graphql/graphql-spec/commit/7908822d4cbae5ad7136437ca577e8bc56f7efab) | Format the spec with prettier (#727) | Benjie Gillam | +| [c18590c](https://github.com/graphql/graphql-spec/commit/c18590c477d6817d73e19b6c248c9fcc62b03017) | Fix typo in Type System section (#905) | Benoit Lubek | +| [60323c9](https://github.com/graphql/graphql-spec/commit/60323c90dc2699266be7da7f0d4ab293953cd733) | fix typo (#896) | Alex Reilly | +| [357bb72](https://github.com/graphql/graphql-spec/commit/357bb727b5a9dc5b1ed132cc4ffa27c309fb8258) | Start next working draft | Lee Byron | + +Generated with: + +```sh +git log October2021..f29fbcd2ab5af763fce7ad62896eb62465a669b3 --format="| [%h](https://github.com/graphql/graphql-spec/commit/%H) | %s | %an <%ae> %(trailers:key=Co-authored-by,valueonly,separator=%x20)" -- spec +``` + +## Diff + +[Github: diff from last spec cut](https://github.com/graphql/graphql-spec/compare/October2021...f29fbcd2ab5af763fce7ad62896eb62465a669b3?w=1) diff --git a/scripts/generate-contributor-list.mjs b/scripts/generate-contributor-list.mjs new file mode 100644 index 000000000..41c68a313 --- /dev/null +++ b/scripts/generate-contributor-list.mjs @@ -0,0 +1,336 @@ +import { execFileSync } from "node:child_process"; +import https from "node:https"; + +// ---------- flags / utils +const RAW_ARGS = process.argv.slice(2); +const DEBUG = RAW_ARGS.includes("--debug"); +const logd = (...xs) => { if (DEBUG) console.error(...xs) }; + +const collator = new Intl.Collator("en", { sensitivity: "base" }); +const cmp = (a, b) => collator.compare(a, b); +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +const toLower = (s) => (s || "").toLowerCase(); +function die(msg, code = 1) { console.error(msg); process.exit(code) } + +function normalizeName(s) { + return (s || "") + .normalize("NFKD") + .replace(/\p{Diacritic}/gu, "") + .replace(/\s+/g, " ") + .trim() + .toLowerCase(); +} +function pickBetterName(current, candidate) { + if (!current) return (candidate || "").trim(); + const c = current.trim(); + const d = (candidate || "").trim(); + if (!c && d) return d; + const spaceC = /\s/.test(c), spaceD = /\s/.test(d); + if (spaceD && !spaceC) return d; + if (d.length > c.length) return d; + return c; +} +function sanitizeDisplayName(raw, fallback) { + const s = (raw || "").trim(); + if (!s) return fallback; + if (/moved\s+to\s+@/i.test(s)) return fallback; + if (/@/.test(s)) return fallback; + if (/^\s*[-–—]+\s*$/.test(s)) return fallback; + return s; +} + +function parseArgs() { + const args = RAW_ARGS.filter(x => x !== "--debug"); + if (args.length === 0) die("Usage: node scripts/generate-contributor-list.mjs [-- ] [--debug]"); + const dd = args.indexOf("--"); + return { range: args[0], paths: dd === -1 ? [] : args.slice(dd + 1) }; +} + +function execGit(argv, opts = {}) { + try { + return execFileSync("git", argv, { encoding: "utf8", maxBuffer: 1024 * 1024 * 400, ...opts }); + } catch (e) { + die(`git ${argv.join(" ")} failed: ${e.message}`); + } +} +function repoNameWithOwner() { + let url = ""; + try { url = execGit(["remote", "get-url", "origin"]).trim(); } catch { } + const m = url.match(/github\.com[:/](?[^/]+)\/(?[^/]+?)(?:\.git)?$/i); + if (m?.groups) return `${m.groups.owner}/${m.groups.repo}`; + die("Could not determine GitHub repo from 'origin' remote."); +} +function revList(range, paths) { + const args = ["rev-list", range]; + if (paths.length) args.push("--", ...paths); + const out = execGit(args); + return out.split(/\r?\n/).filter(Boolean); +} +function parseCoAuthorLines(message) { + const out = []; + const re = /^[ \t]*Co-authored-by:\s*(.+?)\s*<([^>]+)>/gim; + let m; + while ((m = re.exec(message))) out.push({ name: m[1].trim(), email: m[2].trim() }); + return out; +} +function loginFromNoreply(email) { + const m = email.toLowerCase().match(/^(?:\d+\+)?([a-z0-9-]+)@users\.noreply\.github\.com$/i); + return m ? m[1] : ""; +} +function candidateHandlesFromEmailAndName(email, name) { + const cands = new Set(); + const local = email.split("@")[0]; + const bare = local.replace(/[._]/g, ""); + const bareNoDigits = bare.replace(/\d+$/, ""); + cands.add(bare); cands.add(bareNoDigits); + const parts = local.split(/[._-]+/).filter(Boolean); + if (parts.length >= 2) { + const first = parts[0], last = parts[parts.length - 1]; + cands.add(`${first}${last}`); + cands.add(`${first}-${last}`); + cands.add(`${first}_${last}`); + cands.add(`${first[0]}${last}`); + if (last.length >= 3) cands.add(`${first}${last.slice(0, 3)}`); + } + const nameParts = name.split(/\s+/).filter(Boolean); + if (nameParts.length >= 2) { + const f = nameParts[0].replace(/[^A-Za-z0-9-]/g, ""); + const l = nameParts[nameParts.length - 1].replace(/[^A-Za-z0-9-]/g, ""); + if (f && l) { + cands.add(`${f}${l}`); + cands.add(`${f}-${l}`); + cands.add(`${f[0]}${l}`); + } + } + const q = name.match(/'([^']{1,39})'/); if (q) cands.add(q[1]); + const p = name.match(/\(([^) ]{1,39})\)/); if (p) cands.add(p[1]); + return Array.from(cands).filter(s => /^[A-Za-z0-9-]{2,39}$/.test(s)); +} + +// ---------- GraphQL +const REPO = repoNameWithOwner(); +const [OWNER, NAME] = REPO.split("/"); +function getToken() { + const env = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || ""; + if (env) return env; + try { return execFileSync("gh", ["auth", "token"], { encoding: "utf8" }).trim(); } catch { return ""; } +} +const TOKEN = getToken(); +if (!TOKEN) console.error("Warning: no GITHUB_TOKEN/GH_TOKEN (or gh auth token). Resolution will be limited."); + +async function graphql(query, variables) { + const body = JSON.stringify(variables ? { query, variables } : { query }); + const options = { + hostname: "api.github.com", + path: "/graphql", + method: "POST", + headers: { + "User-Agent": "contributors-table-graphql", + "Authorization": `Bearer ${TOKEN}`, + "Content-Type": "application/json", + "Content-Length": Buffer.byteLength(body), + }, + }; + return await new Promise((resolve) => { + const req = https.request(options, (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (c) => (data += c)); + res.on("end", () => { + try { + const json = JSON.parse(data || "{}"); + if (json.errors && DEBUG) console.error("GraphQL errors:", JSON.stringify(json.errors, null, 2)); + resolve(json); + } catch { resolve({}); } + }); + }); + req.on("error", () => resolve({})); + req.write(body); + req.end(); + }); +} + +// Batch fetch commit author + message for SHAs; count primary-author occurrences per login +async function fetchCommitsByOidBatch(oids) { + const out = new Map(); // oid -> { login | "", name, email, message } + const authorCount = new Map(); // login -> # of primary authored commits in range + const chunkSize = 40; + for (let i = 0; i < oids.length; i += chunkSize) { + const chunk = oids.slice(i, i + chunkSize); + const fields = chunk.map((oid, idx) => ` + c${idx}: object(oid: "${oid}") { + ... on Commit { + message + author { user { login } name email } + } + }`).join("\n"); + const q = `query($owner:String!, $name:String!) { repository(owner:$owner, name:$name) { ${fields} } }`; + const res = await graphql(q, { owner: OWNER, name: NAME }); + const repo = res?.data?.repository || {}; + for (let idx = 0; idx < chunk.length; idx++) { + const node = repo[`c${idx}`]; + if (!node) continue; + const info = { + login: node?.author?.user?.login || "", + name: node?.author?.name || "", + email: node?.author?.email || "", + message: node?.message || "", + }; + out.set(chunk[idx], info); + const L = info.login; + if (L) authorCount.set(L, (authorCount.get(L) || 0) + 1); + } + } + return { commitInfo: out, authorCount }; +} + +// GraphQL user search helpers (users only) +async function searchUsersByNameExact(name) { + if (!TOKEN) return ""; + const queryStr = `"${name.replace(/"/g, '\\"')}" in:name type:user`; + const q = `query($q:String!){ search(type: USER, query: $q, first: 25) { nodes { ... on User { login name } } } }`; + const r = await graphql(q, { q: queryStr }); + const nodes = r?.data?.search?.nodes ?? []; + const target = normalizeName(name); + for (const it of nodes) { + if (!it?.login) continue; + if (normalizeName(it.name || "") === target) return it.login; + } + return ""; +} +async function searchUsersByLoginToken(token) { + if (!TOKEN) return ""; + const q = `query($q:String!){ search(type: USER, query: $q, first: 5) { nodes { ... on User { login name } } } }`; + const r = await graphql(q, { q: `${token} in:login type:user` }); + const items = r?.data?.search?.nodes ?? []; + if (items.length === 1) return items[0]?.login || ""; + return ""; +} +async function fetchProfileNames(logins) { + const out = new Map(); + const chunkSize = 40; + for (let i = 0; i < logins.length; i += chunkSize) { + const chunk = logins.slice(i, i + chunkSize); + const fields = chunk.map((login, idx) => `u${idx}: user(login: "${login}") { login name }`).join("\n"); + const q = `query { ${fields} }`; + const r = await graphql(q); + const data = r?.data || {}; + for (let idx = 0; idx < chunk.length; idx++) { + const u = data[`u${idx}`]; + out.set(chunk[idx], (u?.name || "").trim()); + } + } + return out; +} + +// ---------- main +async function main() { + const { range, paths } = parseArgs(); + + const shas = revList(range, paths); + if (!shas.length) die("No commits in the specified range/path."); + + // 1) Commit info + primary author counts + const { commitInfo, authorCount } = await fetchCommitsByOidBatch(shas); + + // 2) Collect authors and co-authors + const loginBestName = new Map(); // login -> name hint + const pool = []; // [{ name, email }] to resolve (co-authors + primaries with missing login) + + for (const sha of shas) { + const info = commitInfo.get(sha); + if (!info) continue; + const { login, name, email, message } = info; + + if (login) { + loginBestName.set(login, pickBetterName(loginBestName.get(login) || "", name)); + } else { + const guess = loginFromNoreply(email); + if (guess) loginBestName.set(guess, pickBetterName(loginBestName.get(guess) || "", name)); + else pool.push({ name, email }); + } + for (const ca of parseCoAuthorLines(message)) pool.push(ca); + } + + // 3) Resolve pool (GraphQL users search only) + const emailToLogin = new Map(); // emailLower -> login + const concurrency = 8; + let idx = 0; + + async function worker() { + while (idx < pool.length) { + const i = idx++; + const { name, email } = pool[i]; + const ekey = toLower(email); + if (emailToLogin.has(ekey)) continue; + + let login = loginFromNoreply(email); + if (!login) login = await searchUsersByNameExact(name); + if (!login) { + const cands = candidateHandlesFromEmailAndName(email, name); + for (const cand of cands) { + const solo = await searchUsersByLoginToken(cand); + if (solo) { login = solo; break; } + } + } + if (!login && DEBUG) logd(`Unresolved: "${name}" <${email}>`); + emailToLogin.set(ekey, login || ""); + if (login) loginBestName.set(login, pickBetterName(loginBestName.get(login) || "", name)); + + if (i % 10 === 0) await sleep(60); + } + } + await Promise.all(Array.from({ length: concurrency }, worker)); + + // 4) Build candidate rows (resolved only), fetch profile names + const resolvedLogins = Array.from(loginBestName.keys()); + const profileNames = await fetchProfileNames(resolvedLogins); + + const candidates = resolvedLogins.map(login => { + const prof = (profileNames.get(login) || "").trim(); + const hint = (loginBestName.get(login) || "").trim(); + const display = sanitizeDisplayName(prof || hint || login, prof || login); + return { login, display, authorCommits: authorCount.get(login) || 0 }; + }); + + // 5) Collapse duplicate people with the same display name + const byDisplay = new Map(); // normName -> best candidate + const score = (x) => (x.authorCommits > 0 ? 2 : 0) + (x.display.toLowerCase() !== x.login.toLowerCase() ? 1 : 0); + for (const c of candidates) { + const key = normalizeName(c.display); + if (!byDisplay.has(key)) { byDisplay.set(key, c); continue; } + const cur = byDisplay.get(key); + if (score(c) > score(cur) || (score(c) === score(cur) && c.login.toLowerCase() < cur.login.toLowerCase())) { + if (DEBUG) logd(`Collapsed duplicate "${c.display}": keeping ${c.login} over ${cur.login}`); + byDisplay.set(key, c); + } + } + const resolvedRows = Array.from(byDisplay.values()) + .filter((v, i, arr) => arr.findIndex(x => x.login.toLowerCase() === v.login.toLowerCase()) === i) + .map(({ display, login }) => ({ name: display, gh: `[@${login}](https://github.com/${login})`, login })); + + // 6) Unmatched → show email (dedupe by name+email) + const unmatched = []; + const seenUnk = new Set(); + for (const { name, email } of pool) { + const login = emailToLogin.get(toLower(email)); + if (login) continue; + const nm = sanitizeDisplayName(name || "(Unknown)", name || "(Unknown)"); + const key = normalizeName(nm) + "|" + email.toLowerCase(); + if (seenUnk.has(key)) continue; + seenUnk.add(key); + unmatched.push({ name: nm, gh: email, login: "" }); + } + + // 7) Merge, sort, output + const allRows = [...resolvedRows, ...unmatched]; + allRows.sort((a, b) => cmp(a.name, b.name)); + + console.log("| Author | Github"); + console.log("| ----------------------------- | ---------------------------------"); + for (const r of allRows) { + console.log(`| ${r.name} | ${r.gh}`); + } +} + +main().catch((e) => die(String(e))); diff --git a/spec/GraphQL.md b/spec/GraphQL.md index 7ac0717ac..5bd5428af 100644 --- a/spec/GraphQL.md +++ b/spec/GraphQL.md @@ -1,6 +1,6 @@ # GraphQL -_Current Working Draft_ +_September 2025 Edition_ **Introduction**