feat: move TypeScript client codegen into the SDK module#9
Draft
TomChv wants to merge 7 commits into
Draft
Conversation
Add plumbing to handle client generation directly inside the typescript-sdk module. Move the client codegen binary as a helper of this codegen. Signed-off-by: Vasek - Tom C <tom@dagger.io>
Add the workspace-oriented client generators and validate them end-to-end against a v1.0.0-beta.6 engine: - generateClient(ws, module, path) and generateAllClient(ws) @generate, plus the codegen/config-updator container builders and the clientDirectory helper. - Serve the one bound module, not its dependencies: a client binds exactly one module, so serveModuleDependencies serves that module (its closure comes along via includeDependencies). GIT_SOURCE embeds source+refPin (portable); LOCAL_SOURCE/DIR_SOURCE take the workspace-only guarded serve. - Allow DIR_SOURCE in the codegen guard: workspace-local modules resolve to DIR_SOURCE at runtime, so fail-closed was wrong. - e2e: dependency-bearing fixtures (client/app -> client/dep), client checks asserting client.gen.ts + <dep>.gen.ts + package.json + tsconfig.json, and helperTestsCheck so `dagger check` also runs the Go helper tests. Two beta.6 workarounds remain until the engine ships the phase-1 primitives: schema uses module-facing introspectionSchemaJSON and resolution uses the polyfill. Remote (GIT_SOURCE) bound-module introspection is blocked on a requester-session issue; see design/client-gen.md. Signed-off-by: Tom Chauveau <tom@dagger.io>
daae756 to
71b3bbb
Compare
Wire client generation to the phase-1 engine primitives and verify end-to-end
against the dev engine (dagger-dev2, v1.0.0):
- Generate from ModuleSource.clientSchemaIntrospectionJSON (the client-facing
schema) instead of the module-facing introspectionSchemaJSON workaround.
- Resolve each registered client's bound module through
CurrentModuleAsSDKClient.moduleSource (honors pin/remote refs) instead of
polyfill path resolution.
Dang consumption details: iterate the client list with a nested projection
`.{{ path, moduleSource.{{ ... }} }}`. A projected object field is a narrowed
record, not a live handle, and the schema File can't be projected; so select
`clientSchemaIntrospectionJSON.{{ contents }}` and write it in the codegen
container via withNewFile. clientDirectory now takes the schema contents +
provenance pieces rather than a ModuleSource handle.
All 14 checks green on dagger-dev2.
Signed-off-by: Tom Chauveau <tom@dagger.io>
71b3bbb to
8d31ef1
Compare
A standalone client now generates:
- dagger.gen.ts — core Dagger types only (named to match the Go SDK's
dagger.gen.go)
- <module>.gen.ts — one per module, including the bound module (e.g. hello)
and its dependencies; each carries its own types plus the prototype
augmentations it contributes to the extendable types (Client/Binding/Env).
Previously the bound module's types were folded into client.gen.ts alongside
core; only dependencies were split. client.gen.ts is no longer produced for a
client. Module codegen is unchanged (keeps its own types in client.gen.ts).
- generator: for ClientConfig, split all modules (schema.DependencyNames) and
render the core file as dagger.gen.ts.
- templates: per-module files import the core file by name via a new CoreFile
func ("dagger.gen" for clients, "client.gen" for module codegen), so module
golden tests are unaffected.
- e2e: client checks assert dagger.gen.ts + client-app.gen.ts + client-dep.gen.ts.
Verified: Go golden tests + all 14 checks green on the dev engine (dagger-dev2).
Signed-off-by: Tom Chauveau <tom@dagger.io>
Member
Author
|
PR needs dagger/dagger#13646 merged and released in order to load |
Regenerating a client no longer clobbers the user's package.json: - Generation now reads the client dir's current config (package.json, tsconfig.json, deno.json) instead of starting from an empty directory, so config-updator sees what the user already has. - config-updator preserves a @dagger.io/dagger dependency the user has pointed at a local ref (./sdk, file:, link:, workspace:, git/URL) instead of overwriting it with the engine-version pin — so a dev/unreleased engine can use a local bundle. A plain version pin is still refreshed to track the engine. - Client config files are written pretty-printed (2-space indent, key order preserved) rather than a single minified line. Module config edits keep sjson's in-place format. e2e: generateClientRespectsExistingCheck binds a client dir that already pins @dagger.io/dagger to ./sdk and asserts it survives regeneration, pretty-printed. Verified: 15/15 checks green on the dev engine (dagger-dev2). Signed-off-by: Tom Chauveau <tom@dagger.io>
Replace the remaining toJSON(...) calls with JSON.encode(...) so the module uses one JSON-serialization builtin throughout. Signed-off-by: Tom Chauveau <tom@dagger.io>
…ate hygiene The engine's client-facing schema now exposes the bound module as a namespaced dependency (dag.<module>()) rather than promoting its functions to Query, and it no longer pulls the bound module's *internal* dependencies into the client schema. So a client for a single module is dagger.gen.ts + <module>.gen.ts only (a module cannot even return an external dependency's type). Drop the client-dep.gen.ts assertion accordingly and simplify the fixture. Also make the generation checks survive a prior `dagger generate`: they now assert a path is generated (added OR modified) instead of strictly added, since `dagger generate` writes real output into the fixture/client dirs. Gitignore the client output dir so it doesn't dirty the tree. All 16 checks green on the dev engine, including generate-client-compiles-check. Signed-off-by: Tom Chauveau <tom@dagger.io>
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.
Move TypeScript client codegen into the SDK module
Moves TypeScript client generation out of
dagger/dagger's engine runtime and into this.dangSDK module. The module now generates a typed client for a bound module itself — receiving the schema + module metadata from the engine as plain data and running codegen in an ordinary container, with no nested engine session.Important
Depends on two engine PRs, both gated
v1.0.0-0:ModuleSource.clientSchemaIntrospectionJSON(the client-facing schema: core + the bound module only) andCurrentModuleAsSDKClient.moduleSource(resolves the bound module at generation time, honoring pin/remote refs).Workspace.moduleSource(path), the runtime field the generated client calls to serve its bound module from a plainconnect()session (design:hack/designs/generated-client-module-loading.md).This PR requires an engine that ships both.
What's in it
helpers/codegen— the TypeScript client generator ported fromdagger/dagger'scmd/codegenas a standalone, engine-free Go module (nodagger.io/daggerdependency). Reads a pre-computed introspection schema + ameta.json(module name, engine version, bound module) and emits the client bindings. The upstream live-engine query is replaced by themeta.jsoninput. Upstream golden tests ported to guard fidelity.helpers/config-updator— new client config writers producing a scoped npm package:package.json(@dagger.io/<module>-clientname,@dagger.io/daggerpinned to the engine version,type:module,typescript), plustsconfig.json/deno.json.typescript-sdk.dang— the dang wiring:generateClient(ws, module, path)— the workspace analogue ofmod(ws, path).generate(ws).generateAllClient(ws) @generate— regenerates every registered client (currentModule.asSDK.clients), resolving each bound module viaclient.moduleSourceand generating fromclientSchemaIntrospectionJSON.generateAll→generateAllModulefor symmetry.codegenBuilder/configUpdatorBuildercontainer builders (compiled like the existing helpers).e2e —
generateClientCheck,generateAllClientCheck,generateClientRespectsExistingCheck,generateClientCompilesCheck, andhelperTestsCheck(runs the Go helper tests insidedagger check), plus dependency-bearing fixtures (client/app→client/dep).Client output layout
A generated client is a scoped package:
dagger.gen.ts— core Dagger types (named to match the Go SDK'sdagger.gen.go)<module>.gen.ts— one per module: the bound module (e.g.hello.gen.ts) and each dependency; each carries its own types plus the prototype augmentations it contributes toQuery/Client/Binding/Env.Key design decisions
@dagger.io/dagger; no TypeScript library bundle is vendored here.serveBoundModulebootstrap serves exactly that module (no dependency loop, noincludeDependencies). ALOCAL_SOURCE/DIR_SOURCEmodule is resolved against the workspace by its workspace-root-relative path (dag.currentWorkspace().moduleSource(path)) — cwd-independent, so it works anywhere in the project tree; aGIT_SOURCEmodule serves from its canonical ref + pin (dag.moduleSource(ref, { refPin })), which resolves from anywhere. A local-bound client shipped away from the project tree is unservable by design (remote-only) — seehack/designs/generated-client-module-loading.md.Full rationale in
design/client-gen.md.Testing
go test ./...in each helper (also run insidedagger checkviahelperTestsCheck), including a golden test for theserveBoundModulebootstrap (local path vs. git ref + pin).generateClientCompilesCheck, which additionally requires #13663: it type-checks the generateddag.currentWorkspace().moduleSource(...)against the engine'sWorkspace.moduleSource, so it only passes once the engine ships that field.Known limitations / follow-ups
@dagger.io/daggerversion on a dev/unreleased engine isn't on npm.generateClientCompilesCheckbuilds it from a pinneddagger/daggercommit; the client'spackage.jsonrespects an existing local./sdkref (seedesign/client-bundle.md).config-updatorbut isn't wired intoclientDirectoryyet.