Vandor/vxt

Go Bindings

Typed Go binding workflow for vxt document-mode templates.

Go Bindings

The bind package generates typed Go wrappers for document-mode .vxt templates. Generated bindings are useful when application code should call a template with a typed Input struct instead of raw map[string]any values.

Bindings are library-only. They are generated by Go code that calls github.com/vandordev/vxt/bind; vxt does not provide a CLI.

When to Use Bindings

Use generated bindings when:

  • one document template is part of a Go module
  • callers should get compile-time field names for template input
  • generated code should wrap compile, validate, plan, and write calls
  • local @use definitions should be embedded into the generated package

Use the raw runtime API when:

  • templates are loaded dynamically
  • input shape is not known at build time
  • the caller wants direct control over every runtime stage

Current bindings are document-mode only. Single-file bindings are not implemented.

Generated Package Shape

For a document named service, bind.Generate emits one Go source file under .vxt/service_gen.go. The generated package name is the PackageName from the generation request.

Generated code includes:

  • an Input struct matching document @input declarations
  • public Go structs for document @type declarations
  • embedded main document source
  • embedded local @use source documents supplied in the request
  • wrapper functions for Compile, Validate, Plan, and Write

Example consumer code:

plan, err := servicevxt.Plan(servicevxt.Input{
	Entity: servicevxt.Entity{
		Name:          "User",
		PackageName:   "user",
		HasRepository: true,
	},
})
if err != nil {
	panic(err)
}

The generated Plan wrapper returns a runtime.Plan. The generated Write wrapper writes through a caller-provided output target.

Generate Bindings

Call bind.Generate with a package name, the main document source, and any local @use sources.

out, err := bind.Generate(bind.Request{
	PackageName: "servicevxt",
	Document: source.Source{
		ID:   "service.vxt",
		Path: "service.vxt",
		Text: serviceTemplate,
	},
	Uses: map[string]source.Source{
		"./schema.vxt": {
			ID:   "schema.vxt",
			Path: "schema.vxt",
			Text: schemaTemplate,
		},
	},
})
if err != nil {
	panic(err)
}

Output.Files contains generated files. Each file has a path and content. The path currently uses the .vxt/ prefix.

Write Generated Output

Use bind.WriteOutput when you already have a generated bind.Output and want to write it into a specific directory:

report, err := bind.WriteOutput(out, ".vxt", bind.WriteOptions{})
if err != nil {
	panic(err)
}

_ = report

Use bind.GenerateToDir to generate and write in one call:

report, err := bind.GenerateToDir(bind.Request{
	PackageName: "servicevxt",
	Document:    mainSource,
	Uses:        useSources,
}, ".vxt", bind.WriteOptions{})
if err != nil {
	panic(err)
}

The write report records created, overwritten, and removed files. Stale generated files owned by the same binding can be removed during reconciliation.

Dry Run

Set bind.WriteOptions{DryRun: true} to report planned write actions without creating the output directory or writing files.

report, err := bind.GenerateToDir(bind.Request{
	PackageName: "servicevxt",
	Document:    mainSource,
	Uses:        useSources,
}, ".vxt", bind.WriteOptions{DryRun: true})
if err != nil {
	panic(err)
}

fmt.Println(report.DryRun)
fmt.Println(report.Actions)

Dry-run reports use the same planning path as normal writes, but stop before filesystem mutation.

Local @use Embedding

When a document has @use "./schema.vxt", the caller must provide that source in bind.Request.Uses under the same path string.

The generator compiles the main document, collects local use paths, recompiles with a resolver, and embeds the resolved sources into the generated package. This lets the generated package compile and plan without reading schema.vxt from disk at runtime.

Current behavior is local-source embedding only. There is no package-style @use, registry lookup, or network resolution.

Current Limitations

  • Bindings are generated for document mode only.
  • Single-file binding generation is not implemented.
  • Generated bindings are Go-only.
  • @use support is local-path source embedding supplied by the caller.
  • The generator does not define trust policy or hook execution policy.
  • Generated source is intended to be committed with the consuming Go module.