Skip to content

Standalone clients: --standalone writes a copy of the runtime into the output - #127

Merged
joadan merged 6 commits into
mainfrom
spike/standalone-client
Oct 27, 2026
Merged

joadan merged 6 commits into
mainfrom
spike/standalone-client

Conversation

@joadan

@joadan joadan commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Why

Clients built and shipped as separate packages by different teams pin nothing about the runtime: an app that uses several of them gets one Linq2GraphQL.Client version, whichever NuGet picks, which may not be the one each client was generated against. Today apps work around it by generating their own copies of the clients.

What

--standalone (-sa, or "standalone": true in the settings file) makes the generator write a copy of the runtime into Runtime/ in the output, with Linq2GraphQL.Client rewritten to <namespace>.Runtime in the runtime and the generated client. The client then references no Linq2GraphQL package, so clients of any generator version can share an app, next to package-mode clients.

  • The runtime source (Client, Subscriptions with --subscriptions, AI with --agent) is embedded in the generator at build time, so it always matches the templates.
  • Runtime/RuntimeInfo.cs records the runtime version and the packages the copy needs; the generator prints them as PackageReference lines.
  • A Runtime folder from an earlier standalone run is cleared before writing.
  • Package mode is unchanged.

Fix found by the spike

The safe-mode schema cache key now includes the schema type. Two runtime copies sharing an app's IMemoryCache and an endpoint read each other's cached schema and failed with InvalidCastException.

Tests

  • TestClientStandalone: checked-in standalone output, regenerated and checked by regenerate-test-clients.ps1 (-Check ignores the version line in RuntimeInfo.cs).
  • Linq2GraphQL.Tests.Standalone compiles the main suite's test files against the standalone client, agent tests included: 436 pass.
  • StandaloneGeneratorTests pins down what the generator emits; CoexistenceTests runs a standalone and a package client in one process with a shared cache.
  • Main suite: 498 pass.

Docs

Generator page, README and CHANGELOG describe the option and its trade-offs (runtime fixes arrive only by regenerating, each client has its own runtime types, the Runtime folder must not be edited).

Two clients that carry their own copy of the runtime share the app's
IMemoryCache. With the same endpoint they produced the same key, and the
second read the first's GraphQLSchema and failed with InvalidCastException.
scripts/spike-standalone.ps1 copies the runtime source and the generated
SampleClient into projects with no Linq2GraphQL package reference, rewriting
the namespace. The end-to-end tests run against the copy, and CoexistenceTests
loads a 1.0.30 client and two current clients into one process.
In standalone mode the generated client depends on no Linq2GraphQL package:
the runtime source (Client + Subscriptions, embedded in the generator at build
time) is written to Runtime/ with its namespace rewritten to
<namespace>.Runtime, and the generated client files point there too. Clients
generated by different generator versions can then share an app, because each
carries the runtime it was generated against.

- Subscriptions runtime is only copied when subscriptions are generated.
- Runtime/RuntimeInfo.cs records the runtime version and the packages it
  needs; the generator also prints them.
- A Runtime folder from an earlier standalone run is cleared before writing.
- Also settable as "standalone" in the config file.

Replaces the spike. TestClientStandalone is now generated by
regenerate-test-clients.ps1 (which ignores the version line in -Check),
Linq2GraphQL.Tests.Standalone compiles the main suite's tests against it, and
CoexistenceTests runs it next to the package-based client in one process.
@joadan
joadan merged commit 3b523e9 into main Oct 27, 2026
2 checks passed
@joadan
joadan deleted the spike/standalone-client branch October 27, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant