Skip to content

refactor(sdk): bundle graphql types and make graphql a dev dependency - #2816

Open
toiroakr wants to merge 4 commits into
feat/enluminefrom
refactor/graphql-dev-dependency
Open

toiroakr wants to merge 4 commits into
feat/enluminefrom
refactor/graphql-dev-dependency

Conversation

@toiroakr

@toiroakr toiroakr commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Move graphql (17.0.2) from dependencies to devDependencies of @tailor-platform/sdk, so the SDK no longer installs it into users' node_modules and users no longer end up with two graphql versions side by side.
  • Inline the DocumentNode types that the HTTP adapter and the executor query option use into the published declarations under dist/vendor/graphql, with the graphql LICENSE next to them, and expose them through a types-only ./vendor/graphql/* export so that declaration output that mentions a DocumentNode type resolves for consumers.
  • Type the executor query option as string | DocumentNode (the bundled type) instead of the type taken from @urql/core, whose graphql import resolves to any when graphql is not installed. @urql/core and its wonka dependency are no longer used and are removed.
  • Keep the declaration layout independent of the build machine: the inlined files and the workspace packages get fixed locations (dist/vendor/graphql, dist/shared, dist/tailor-proto) instead of paths that mirror where the package manager stores dependencies, and the //#region comments no longer contain that store path.

graphql is used at runtime nowhere in the SDK: the HTTP adapter bundle and the query REPL use @0no-co/graphql.web. The only other use is import type { DocumentNode } from "graphql" in the HTTP adapter and executor type declarations.

Dependencies

Compatibility

I packed the tarball and type-checked consumer projects with tsc --strict and no skipLibCheck, unless noted:

  • Without graphql installed: createHttpAdapter with a HttpAdapterTypedDocumentNode type-checks, mismatched variables are rejected, and DocumentNode is not any. In an executor, query: 1 is a type error and query: "query { a }" type-checks.
  • declaration: true with export const definitions = doc.definitions (from a HttpAdapterTypedDocumentNode) compiles without graphql installed and emits readonly import("@tailor-platform/sdk/vendor/graphql/language/ast").DefinitionNode[], which resolves through the new ./vendor/graphql/* export. Without that export it failed with TS2742.
  • With graphql 16.11.0 and 17.0.2: DocumentNode, TypedQueryDocumentNode and TypedDocumentNode from @graphql-typed-document-node/core are accepted by HTTP adapter input handlers, and resp.data is inferred. A parse() result is accepted by the executor query option.
  • With graphql 15.9.0 the documents are rejected by HTTP adapters, which is also the case on 2.36.0 because the types resolve to the SDK's own graphql 17. For the executor query option, a parse() result is now rejected with skipLibCheck: true; without skipLibCheck the @0no-co/graphql.web declarations already fail to compile with graphql 15.
  • Installing graphql 16.11.0 and the SDK with npm: on 2.36.0 node_modules/@tailor-platform/sdk/node_modules/graphql (17.0.2), @urql/core and wonka are installed; with this change none of them is.
  • A project that imports graphql without declaring it fails to resolve it with pnpm on 2.36.0 already; with npm it resolved through hoisting and stops resolving after this change (ERR_MODULE_NOT_FOUND). The changeset is patch and tells users to declare graphql themselves.
  • Passing a DocumentNode to the executor query option type-checks but is stringified to [object Object] by the executor schema on 2.36.0 already; this change does not alter it.

The only non-test use of graphql is the DocumentNode type in the HTTP
adapter declarations, and the query REPL no longer loads graphql at
runtime, so users do not need a copy of graphql installed by the SDK.

The declaration build now inlines the graphql types under dist/vendor/graphql
with its LICENSE, and keeps the emitted layout independent of where the
package manager stores dependencies on the build machine.
…ctory

The workspace package layout was cut at the first packages/ segment of the
build path, so a checkout located under another packages/ directory emitted
declarations outside dist/shared and dist/tailor-proto.

The changeset now says when the missing graphql install affects users.
@changeset-bot

changeset-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bb8d271

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

This PR includes changesets to release 2 packages
Name Type
@tailor-platform/sdk Patch
@tailor-platform/create-sdk Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@tailor-platform/create-sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/create-sdk@bb8d271

@tailor-platform/eslint-plugin-sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/eslint-plugin-sdk@bb8d271

@tailor-platform/sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk@bb8d271

@tailor-platform/sdk-plugin-frontend

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-frontend@bb8d271

@tailor-platform/sdk-plugin-seed

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-seed@bb8d271

@tailor-platform/sdk-plugin-setup

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-setup@bb8d271

@tailor-platform/sdk-plugin-tailordb-erd

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-tailordb-erd@bb8d271

commit: bb8d271

@toiroakr
toiroakr added this pull request to stack #2845 October 28, 2026 01:41
@github-actions

This comment has been minimized.

@toiroakr
toiroakr marked this pull request as ready for review October 28, 2026 01:59
@toiroakr
toiroakr requested review from a team as code owners October 28, 2026 01:59
Comment thread packages/sdk/package.json
"@types/semver": "7.8.0",
"@vitest/coverage-v8": "5.0.5",
"eslint-plugin-zod": "5.2.0",
"graphql": "17.0.2",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without graphql installed, the executor's operation.query (kind: "graphql") is no longer type-checked. Its type comes from @urql/core, whose document types rely on @0no-co/graphql.web's /*@ts-ignore*/ import * as GraphQL from 'graphql'; once graphql can't be resolved they accept any value. Until now the SDK's own graphql resolved that import.

createExecutor({
  name: "bad",
  trigger: scheduleTrigger({ cron: "0 0 * * *" }),
  operation: { kind: "graphql", query: 1 },
});

In an npm or pnpm project that installs the packed SDK without graphql, tsc --strict rejects this on the base (Type 'number' is not assignable to type 'DocumentInput<unknown, AnyVariables>') and accepts it on this PR. Typing query with the inlined DocumentNode, as the HTTP adapter does, would avoid relying on graphql being resolvable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by b460618: query is now typed string | DocumentNode with the bundled types, and @urql/core is removed.
With the packed SDK installed without graphql, your query: 1 example now fails tsc --strict with TS2769 (Type 'number' is not assignable to type 'string | DocumentNode').
A string query compiles cleanly, and the published .d.mts files have no references to graphql or @urql.

unbundle: true,
root: "src",
deps: { neverBundle: externalDeps },
deps: { neverBundle: externalDeps, dts: { alwaysBundle: [/^graphql(\/|$)/] } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inlined types under dist/vendor/graphql aren't reachable through exports, so with declaration: true a project can no longer emit an inferred type that reaches them, even when it installs graphql itself:

declare const doc: HttpAdapterTypedDocumentNode<{ user: { id: string } }, { id: string }>;
export const definitions = doc.definitions;

With graphql@17.0.2 installed via pnpm, this emits on the base and fails on this PR with TS2883 (The inferred type of 'definitions' cannot be named without a reference to 'DefinitionNode' from './node_modules/@tailor-platform/sdk/dist/vendor/graphql/language/ast.mjs').

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b460618 adds ./vendor/graphql/* as a types-only export.
With the packed SDK, your definitions example with declaration: true now emits import("@tailor-platform/sdk/vendor/graphql/language/ast").DefinitionNode[] instead of failing with TS2883.

Comment thread packages/sdk/tsdown.config.ts Outdated
onSuccess: (config) => {
const vendorDir = path.join(config.outDir, "vendor/graphql");
mkdirSync(vendorDir, { recursive: true });
copyToOutDir(config.outDir, "node_modules/graphql/LICENSE", "vendor/graphql/LICENSE");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_modules/graphql/ is repeated here and in the //#region regex below instead of reusing GRAPHQL_DIR, and vendor/graphql appears here, on L26 and on L89. Sharing constants for both, and deriving the LICENSE destination from vendorDir, would keep them in sync.

Comment thread packages/sdk/tsdown.config.ts Outdated
];

const GRAPHQL_DIR = "node_modules/graphql/";
const WORKSPACE_DIR = /^(?:.*\/)?packages\//;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next to GRAPHQL_DIR, WORKSPACE_DIR reads like a directory path, but it's a pattern for everything up to the last packages/. A name like UP_TO_PACKAGES_DIR would say what it strips.

@dqn dqn assigned toiroakr and unassigned dqn Oct 28, 2026
@toiroakr
toiroakr requested a balanced review from Copilot October 28, 2026 10:06

This comment was marked as outdated.

Type the executor query option with the bundled DocumentNode instead of
the type taken from @urql/core, so it is checked without graphql
installed, and drop @urql/core and its wonka dependency.

Publish the bundled declarations under a types-only ./vendor/graphql/*
export so declaration output that mentions a DocumentNode type resolves
for consumers.

Share the vendor directory constants in tsdown.config.ts and rename
WORKSPACE_DIR to UP_TO_PACKAGES_DIR. Skip wildcard exports in the vitest
alias generation and the zod isolation and public API JSDoc checks.

This comment was marked as off-topic.

Resolve the packages/sdk/package.json conflicts by keeping the removal of @urql/core and the graphql devDependency from this branch together with the amaro, eslint-plugin-zod, oxfmt, oxlint and oxlint-tsgolint updates from the base branch. Regenerate pnpm-lock.yaml.
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Code Metrics Report (packages/sdk)

Type instantiations changed in 8 cases

Case Base Now Change
🔻 executor-baseline 604 601 -0.50% (-3)
🔺 executor-schedule 1380 1384 +0.29% (+4)
🔺 executor-webhook 39 43 +10.26% (+4)
🔺 executor-record-created 3211 3215 +0.12% (+4)
🔺 executor-record-updated 3211 3215 +0.12% (+4)
🔺 executor-record-deleted 3211 3215 +0.12% (+4)
🔺 executor-resolver 455 459 +0.88% (+4)
🔺 executor-operation-gql 35 39 +11.43% (+4)
executor-baseline
createExecutor({
    name: "baseline",
    description: "Baseline executor",
    trigger: incomingWebhookTrigger(),
    operation: {
      kind: "function",
      body: async () => {
        console.log("Baseline executed");
      },
    },
  })
executor-schedule
return createExecutor({
  name: "executor0",
  description: "Scheduled executor 0",
  trigger: scheduleTrigger({ cron: "0 0 * * *" }),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { log(message: "executed") }`,
    variables: () => ({}),
  },
});
executor-webhook
return createExecutor({
  name: "executor0",
  description: "Webhook executor 0",
  trigger: incomingWebhookTrigger(),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { process(data: $data) }`,
    variables: () => ({ data: "webhook-data" }),
  },
});
executor-record-created
return createExecutor({
  name: "executor0",
  description: "Record created trigger executor 0",
  trigger: recordCreatedTrigger({ type: dummyType }),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { log(message: "executed") }`,
    variables: () => ({}),
  },
});
executor-record-updated
return createExecutor({
  name: "executor0",
  description: "Record updated trigger executor 0",
  trigger: recordUpdatedTrigger({ type: dummyType }),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { log(message: "executed") }`,
    variables: () => ({}),
  },
});
executor-record-deleted
return createExecutor({
  name: "executor0",
  description: "Record deleted trigger executor 0",
  trigger: recordDeletedTrigger({ type: dummyType }),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { log(message: "executed") }`,
    variables: () => ({}),
  },
});
executor-resolver
return createExecutor({
  name: "executor0",
  description: "Resolver executed trigger executor 0",
  trigger: resolverExecutedTrigger({ resolver: dummyResolver }),
  operation: {
    kind: "graphql",
    appName: "my-app",
    query: `mutation { log(message: "executed") }`,
    variables: () => ({}),
  },
});
executor-operation-gql
const query = `
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

return createExecutor({
  name: "executor0",
  description: "GQL operation executor 0",
  trigger: incomingWebhookTrigger(),
  operation: {
    kind: "graphql",
    query,
    variables: () => ({ id: "1" }),
  },
});
| | [main](https://github.com/tailor-platform/sdk/tree/main) ([0c17d81](https://github.com/tailor-platform/sdk/commit/0c17d810f027455841e28433306661ae944fe976)) | [#2816](https://github.com//pull/2816) ([527bbed](https://github.com/tailor-platform/sdk/commit/527bbedbf9fe8fa8f48515886c326a72726bacca)) | +/- | |------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------:|------:| | **Coverage** | 89.2% | [89.1%](https://github.com/tailor-platform/sdk/actions/runs/41544101771/artifacts/13188870361) | -0.1% | | **Code to Test Ratio** | 1:0.5 | 1:0.5 | +0.0 |
Details
  |                    | main (0c17d81) | #2816 (527bbed) |  +/-  |
  |--------------------|----------------|-----------------|-------|
- | Coverage           |          89.2% |           89.1% | -0.1% |
  |   Files            |            578 |             578 |     0 |
  |   Lines            |          25815 |           25625 |  -190 |
- |   Covered          |          23035 |           22853 |  -182 |
+ | Code to Test Ratio |          1:0.5 |           1:0.5 |  +0.0 |
  |   Code             |         212577 |          212627 |   +50 |
+ |   Test             |         120407 |          120458 |   +51 |

Code coverage of files in pull request scope (96.5% → 100.0%)

Files Coverage +/- Status
packages/sdk/src/cli/query/repl-editor.ts 100.0% +3.4% affected
packages/sdk/src/configure/services/executor/operation.ts 0.0% 0.0% modified

SDK Configure Bundle Size

main (0c17d81) #2816 (527bbed) +/-
configure-index-size 13.82KB 13.82KB 0KB
dependency-chunks-size 25.76KB 25.76KB 0KB
total-bundle-size 39.58KB 39.58KB 0KB

Runtime Performance

main (0c17d81) #2816 (527bbed) +/-
Generate Median 1,894ms 1,536ms -358ms
Generate Max 2,288ms 2,227ms -61ms
Apply Build Median 2,053ms 1,634ms -419ms
Apply Build Max 2,079ms 1,662ms -417ms

Type Performance (instantiations)

main (0c17d81) #2816 (527bbed) +/-
tailordb-baseline 955 955 0
resolver-baseline 4,641 4,641 0
executor-baseline 848 601 -247
tailordb-basic 2,385 2,385 0
tailordb-optional 1,167 1,167 0
tailordb-relation 3,808 3,808 0
tailordb-validate 186 186 0
tailordb-hooks 999 999 0
tailordb-object 1,892 1,892 0
tailordb-enum 574 574 0
resolver-basic 1,022 1,022 0
resolver-nested 3,405 3,405 0
resolver-array 1,954 1,954 0
executor-schedule 1,388 1,384 -4
executor-webhook 47 43 -4
executor-record-created 3,219 3,215 -4
executor-record-updated 3,219 3,215 -4
executor-record-deleted 3,219 3,215 -4
executor-resolver 463 459 -4
executor-operation-function 30 30 0
executor-operation-gql 43 39 -4
executor-operation-webhook 50 50 0
executor-operation-workflow 211 211 0

Reported by octocov

@toiroakr toiroakr assigned dqn and unassigned toiroakr Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants