Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/modernize-graphql-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ghx-dev/core": minor
---

Upgrade the GraphQL runtime to version 17 and execute generated typed document nodes directly through the existing transport contract.
2 changes: 1 addition & 1 deletion docs/repository-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ ghx/
| `gql/batch.ts` | Batch query/mutation builder | `buildBatchQuery()`, `buildBatchMutation()` |
| `gql/builders.ts` | Per-capability mutation builders | `OPERATION_BUILDERS` |
| `gql/domains/*.ts` | Domain operation modules | `run*` operation handlers |
| `gql/operations/*.generated.ts` | Generated operation SDKs | Operation-specific `getSdk()` |
| `gql/operations/*.generated.ts` | Generated operation types and documents | Typed operation document nodes |
| `gql/generated/common-types.generated.ts` | Generated scalar types | Type aliases |

### CLI & Agent Interface
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@
"devDependencies": {
"@biomejs/biome": "^2.5.6",
"@changesets/cli": "^2.31.1",
"@graphql-codegen/cli": "^7.2.0",
"@graphql-codegen/near-operation-file-preset": "^5.2.1",
"@graphql-codegen/schema-ast": "^6.1.0",
"@graphql-codegen/typescript-operations": "^6.1.2",
"@graphql-codegen/typed-document-node": "^7.1.0",
"@nx/js": "23.1.1",
"@typescript/native": "catalog:",
"@types/node": "catalog:",
"graphql": "^16.14.2",
"lefthook": "^2.1.10",
"nx": "23.1.1",
"oxlint": "1.76.0",
Expand Down
21 changes: 2 additions & 19 deletions packages/core/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,11 @@ const config = {
extension: ".generated.ts",
baseTypesPath: "./base-types.js",
},
// Note: typescript-graphql-request generates a `SdkFunctionWrapper` type with
// `variables?: any`. This is hardcoded in the plugin template and cannot be
// changed without a custom plugin or fork. Since generated files are never
// edited manually and Biome linting is disabled for this project, this `any`
// does not affect CI or runtime behavior. Tracked as a known limitation.
//
// The `add` plugin injects the TypedDocumentString import that v7 of
// typescript-graphql-request requires (it emits `new TypedDocumentString()`
// but does not generate the import itself when using near-operation-file preset).
documentTransforms: [addTypenameSelectionDocumentTransform],
plugins: [
{
add: {
content: 'import { TypedDocumentString } from "./typed-document-string.js"',
},
},
"typescript-operations",
"typescript-graphql-request",
],
plugins: ["typescript-operations", "typed-document-node"],
config: {
useTypeImports: true,
documentMode: "string",
documentMode: "documentNode",
preResolveTypes: true,
defaultScalarType: "any",
nonOptionalTypename: true,
Expand Down
10 changes: 6 additions & 4 deletions packages/core/docs/architecture/graphql-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ graph TB
end

subgraph Transport["Transport Layer"]
GCL[GraphqlClient<br/>query + mutate]
GCL[GraphqlClient<br/>query + queryRaw]
GT[GraphqlTransport<br/>execute interface]
end

Expand Down Expand Up @@ -57,9 +57,9 @@ interface GraphqlTransport {
}
```

`createGraphqlClient(transport)` wraps this into a `GraphqlClient` with `query()` and `mutate()` methods.
`createGraphqlClient(transport)` wraps this into a `GraphqlClient` with `query()` and `queryRaw()` methods.

**Default transport**: `createTokenTransport(token, endpoint?)` — uses `graphql-request` to send queries to GitHub's GraphQL API.
**Default transport**: `createTokenTransport(token, endpoint?)` — uses the platform `fetch` API to send queries to GitHub's GraphQL API.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## GithubClient Facade

Expand Down Expand Up @@ -115,10 +115,12 @@ pnpm run gql:verify
```

The pipeline uses `@graphql-codegen/cli` with:
- `typescript` plugin — generates base types from the GitHub schema
- `typescript-operations` — generates per-operation input/output types
- `typed-document-node` — generates typed GraphQL document nodes
- `near-operation-file-preset` — co-locates generated types near their `.graphql` files

The Codegen CLI runs with GraphQL 16 in the private root toolchain because its latest configuration and near-operation packages have not declared GraphQL 17 peer support yet. Published `@ghx-dev/core` runtime code uses GraphQL 17.

## Batch Query Building

For chained operations, `batch.ts` provides utilities to combine multiple GraphQL operations into a single request using aliases:
Expand Down
2 changes: 1 addition & 1 deletion packages/core/docs/guides/custom-graphql-transport.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Custom GraphQL Transport

By default, ghx uses `graphql-request` to send queries to GitHub's GraphQL API. You can replace this with your own transport for enterprise endpoints, proxies, request signing, or test mocking.
By default, ghx uses the platform `fetch` API to send queries to GitHub's GraphQL API. You can replace this with your own transport for enterprise endpoints, proxies, request signing, or test mocking.

## The Transport Interface

Expand Down
10 changes: 1 addition & 9 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,10 @@
},
"dependencies": {
"ajv": "^8.20.0",
"graphql": "^16.14.2",
"graphql-request": "^7.4.0",
"graphql-tag": "^2.12.7",
"graphql": "^17.0.2",
"js-yaml": "^5.2.2"
},
"devDependencies": {
"@graphql-codegen/cli": "^7.2.0",
"@graphql-codegen/near-operation-file-preset": "^5.2.1",
"@graphql-codegen/schema-ast": "^6.1.0",
"@graphql-codegen/typescript": "^6.1.0",
"@graphql-codegen/typescript-graphql-request": "^7.1.0",
"@graphql-codegen/typescript-operations": "^6.1.2",
"@graphql-typed-document-node/core": "^3.2.0",
"@opencode-ai/sdk": "^1.18.10",
"@types/node": "catalog:",
Expand Down
20 changes: 4 additions & 16 deletions packages/core/scripts/generate-gql-artifacts.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/* global console, process */
import { spawnSync } from "node:child_process"
import { readdirSync, readFileSync, writeFileSync } from "node:fs"
import { join, relative, resolve } from "node:path"
import { join, resolve } from "node:path"

function fixGeneratedImportExtensions(packageRoot) {
const opsDir = join(packageRoot, "src", "gql", "operations")
fixGeneratedArtifactsInDir(opsDir, opsDir)
fixGeneratedArtifactsInDir(opsDir)
}

function fixGeneratedArtifactsInDir(dir, opsRoot) {
function fixGeneratedArtifactsInDir(dir) {
const entries = readdirSync(dir, { withFileTypes: true })

for (const entry of entries) {
const entryPath = join(dir, entry.name)
if (entry.isDirectory()) {
fixGeneratedArtifactsInDir(entryPath, opsRoot)
fixGeneratedArtifactsInDir(entryPath)
continue
}
if (!entry.isFile() || !entry.name.endsWith(".generated.ts")) {
Expand All @@ -29,18 +29,6 @@ function fixGeneratedArtifactsInDir(dir, opsRoot) {
.replace(/^\s+\| { __typename\?: ["']NotificationThread["'] }\r?$/gm, "")
.replace(/^\s+\| { __typename\?: ["']RepositoryDependabotAlertsThread["'] }\r?$/gm, "")

// Fix TypedDocumentString import path for files in subdirectories.
// The `add` plugin injects `./typed-document-string.js` for every file,
// but files in subdirectories (e.g. fragments/) need a deeper relative path.
const depth = relative(opsRoot, dir).split("/").filter(Boolean).length
if (depth > 0) {
const correctPrefix = "../".repeat(depth)
fixed = fixed.replace(
/from ["']\.\/typed-document-string\.js["']/,
`from "${correctPrefix}typed-document-string.js"`,
)
}

if (fixed !== content) {
writeFileSync(entryPath, fixed, "utf8")
}
Expand Down
19 changes: 10 additions & 9 deletions packages/core/src/gql/builders.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { print } from "graphql"
import { IssueAssigneesUpdateDocument } from "./operations/issue-assignees-update.generated.js"
import { IssueCommentCreateDocument } from "./operations/issue-comment-create.generated.js"
import { IssueLabelsUpdateDocument } from "./operations/issue-labels-update.generated.js"
Expand All @@ -8,24 +9,24 @@ import { PrCommentResolveDocument } from "./operations/pr-comment-resolve.genera
import { PrCommentUnresolveDocument } from "./operations/pr-comment-unresolve.generated.js"
import type { GraphqlVariables } from "./transport.js"

export const PR_COMMENT_REPLY_MUTATION = PrCommentReplyDocument
export const PR_COMMENT_REPLY_MUTATION = print(PrCommentReplyDocument)

export const PR_COMMENT_RESOLVE_MUTATION = PrCommentResolveDocument
export const PR_COMMENT_RESOLVE_MUTATION = print(PrCommentResolveDocument)

export const PR_COMMENT_UNRESOLVE_MUTATION = PrCommentUnresolveDocument
export const PR_COMMENT_UNRESOLVE_MUTATION = print(PrCommentUnresolveDocument)

const ISSUE_UPDATE_MUTATION = IssueUpdateDocument
const ISSUE_UPDATE_MUTATION = print(IssueUpdateDocument)

const ISSUE_LABELS_UPDATE_BY_ID_MUTATION = IssueLabelsUpdateDocument
const ISSUE_LABELS_UPDATE_BY_ID_MUTATION = print(IssueLabelsUpdateDocument)

const ISSUE_ASSIGNEES_UPDATE_BY_ID_MUTATION = IssueAssigneesUpdateDocument
const ISSUE_ASSIGNEES_UPDATE_BY_ID_MUTATION = print(IssueAssigneesUpdateDocument)

const ISSUE_MILESTONE_SET_BY_ID_MUTATION = IssueMilestoneSetDocument
const ISSUE_MILESTONE_SET_BY_ID_MUTATION = print(IssueMilestoneSetDocument)

const ISSUE_COMMENT_CREATE_MUTATION = IssueCommentCreateDocument
const ISSUE_COMMENT_CREATE_MUTATION = print(IssueCommentCreateDocument)

export type BuiltOperation = {
mutation: string | { toString(): string }
mutation: string
variables: GraphqlVariables
}

Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/gql/document-registry.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type DocumentNode, print } from "graphql"
import { IssueAssigneesAddDocument } from "./operations/issue-assignees-add.generated.js"
import { IssueAssigneesLookupDocument } from "./operations/issue-assignees-lookup.generated.js"
import { IssueAssigneesLookupByNumberDocument } from "./operations/issue-assignees-lookup-by-number.generated.js"
Expand Down Expand Up @@ -68,8 +69,7 @@ import { RepoViewDocument } from "./operations/repo-view.generated.js"
import { UserNodeIdDocument } from "./operations/user-node-id.generated.js"

// Unified document map — contains lookup queries, mutations, and query operations.
// Values are TypedDocumentString instances (which extend String) since codegen v7.
const DOCUMENTS: Record<string, string | { toString(): string }> = {
const DOCUMENTS: Record<string, DocumentNode> = {
// Resolution lookup queries (Phase 1)
IssueAssigneesLookup: IssueAssigneesLookupDocument,
IssueAssigneesLookupByNumber: IssueAssigneesLookupByNumberDocument,
Expand Down Expand Up @@ -150,7 +150,7 @@ export function getDocument(operationName: string): string {
if (!doc) {
throw new Error(`No document registered for operation: ${operationName}`)
}
return String(doc)
return print(doc)
}

export function getLookupDocument(operationName: string): string {
Expand Down
Loading