Adding SDKs
This guide covers adding a new language SDK to event-spec.
What an SDK consists of
An event-spec SDK for a language has three parts:
- Core runtime — Client, Provider interface, Hook interface, context propagation, queue, dispatch
- Codegen template —
text/templatetemplate files that generate typed wrappers for that language - Provider implementations — adapters for specific analytics backends
Step 1 — Core runtime
The TypeScript SDK (sdk/typescript/packages/api) and the Kotlin SDK (sdk/kotlin/api/) are the reference implementations to follow alongside the Go analytics/ package.
For JVM/Gradle-based languages, use a multi-module Gradle build with a gradle/libs.versions.toml version catalog. See sdk/kotlin/ for the reference structure:
sdk/<language>/
├── settings.gradle.kts # include(":api", ":provider-<name>")
├── gradle/
│ └── libs.versions.toml # centralised dependency versions
└── api/
└── src/main/kotlin/...
For other languages, create a new directory:
sdk/<language>/
└── src/
├── client.ts / client.swift / ...
├── provider.ts
├── hooks.ts
├── context.ts
└── queue.ts
The runtime must implement:
Client— manages providers, hooks, and context; dispatchestrack,identify,group,page,aliasProviderinterface — the stable contract between the runtime and vendor adaptersHookinterface —before,after,error,finallylifecycle stages- Context merging — 4-level chain: global → transaction → client → invocation (each level overrides the previous)
- Event queue — batching and flush semantics
Step 2 — Codegen template
Templates live in codegen/<language>/:
codegen/swift/
├── engine.go # registers the "swift" language ID
├── templates/
│ ├── eventspec.swift.tmpl # entry point struct/class
│ └── event.swift.tmpl # per-event method + types
└── engine_test.go # golden test
engine.go must implement the codegen.Engine interface and register itself:
package swift
import "github.com/dejanradmanovic/event-spec/codegen"
func init() {
codegen.Register(
codegen.LangConfig{
ID: "swift",
Namer: SwiftNamer{},
TypeMapper: SwiftTypeMapper{},
FileExt: ".swift",
},
&Engine{},
)
}
type Engine struct{}
func (e *Engine) Generate(td codegen.TemplateData, outDir string) error {
// render templates into outDir
return nil
}
Import it with a blank import in cmd/event-spec/generate.go:
import _ "github.com/dejanradmanovic/event-spec/codegen/swift"
Golden tests
Golden tests verify codegen output is stable. The pattern used throughout the codebase is:
func TestGenerate_Swift(t *testing.T) {
events := testEvents()
outDir := t.TempDir()
if err := codegen.Run(events, "swift", outDir, "test-workspace", "test-source", ""); err != nil {
t.Fatalf("Run: %v", err)
}
compareOrUpdate(t, outDir, filepath.Join("testdata", "golden", "swift"))
}
Add expected output files to codegen/testdata/golden/swift/ and run with -update to generate them initially:
go test ./codegen/... -update
Step 3 — Provider implementations
Each provider for the new language follows the same pattern as the Go Amplitude provider. See Adding Providers for the structure.
Step 4 — Document it
- Add an SDK page at
docs/docs/sdks/<language>.md - Update the provider capability matrix in
docs/docs/providers/index.md - Update the codegen table in
docs/docs/concepts/codegen.md - Update the landing page SDK strip in
docs/src/pages/index.tsx - Add a language tab to every page that shows Go/TypeScript examples — see the Kotlin PR for the full list of pages to update
Checklist
- Core runtime implements the full Provider and Hook interfaces
- Codegen engine registered with
codegen.Register(config, engine)(two arguments) - Golden test data added and passing (
go test ./codegen/... -updateto generate) -
event-spec generate --lang <lang>works end-to-end - At least one provider implementation
- Built-in zero-dependency hooks (
sampling,validation) implemented inside the core runtime package - Hooks with external dependencies (logging, otel, etc.) live in separate packages (
sdk/<language>/packages/hooks-<name>/) so they don't bloat the core API - Documentation pages added
-
make test && make lintpasses