Skip to main content

Code Generation

The codegen layer reads event specs from the registry and generates language-native typed wrappers — functions, property structs, and enum constants that make typos a compile error.

How it works

The engine uses Go's text/template package. Language-specific concerns (naming conventions, type mapping, import paths) live entirely inside .tmpl files, so adding a new language only requires a new template set.

Naming conventions

LanguageProperty namingType naming
GoPascalCasePascalCase
TypeScriptcamelCasePascalCase
KotlincamelCasePascalCase

Enum values follow the pattern <EventName><PropertyName><Value>. For example, a category property with value electronics on product_viewed generates:

  • Go: ProductViewedCategoryElectronics
  • TypeScript: ProductViewedCategory.Electronics
  • Kotlin: ProductViewedCategory.ELECTRONICS

Type mapping

Spec typeGo typeTypeScript typeKotlin type
stringstringstringString
numberfloat64numberDouble
integerint64numberLong
booleanboolbooleanBoolean
objectmap[string]anyRecord<string, unknown>Map<String, Any?>
array[]anyunknown[]List<Any?>

Optional properties (not required: true) become pointer types in Go (*string, *float64) and nullable types in Kotlin (String?, Double?).

Generated output

Go

generated/
├── eventspec.go # EventSpec struct, New() constructor
└── ecommerce/
└── product_viewed.go # typed method, enum consts, property struct

eventspec.go:

// Code generated by event-spec. DO NOT EDIT.
package analytics

import core "github.com/dejanradmanovic/event-spec/analytics"

type EventSpec struct{ client *core.Client }

func New(client *core.Client) *EventSpec { return &EventSpec{client: client} }

product_viewed.go:

// Code generated by event-spec. DO NOT EDIT.
// Source: product_viewed (v1-0-0)

type ProductViewedCategory string

const (
ProductViewedCategoryClothing ProductViewedCategory = "clothing"
ProductViewedCategoryElectronics ProductViewedCategory = "electronics"
ProductViewedCategoryOther ProductViewedCategory = "other"
)

type ProductViewedProperties struct {
Category ProductViewedCategory
ProductId string
Currency *string // optional
}

func (es *EventSpec) ProductViewed(
ctx context.Context,
props ProductViewedProperties,
opts ...core.TrackOption,
) error {
return es.client.Track(ctx, core.Event{
Name: "Product Viewed",
Properties: map[string]any{
"category": string(props.Category),
"product_id": props.ProductId,
"currency": props.Currency,
},
}, opts...)
}

TypeScript

src/analytics/generated/
├── index.ts
└── ecommerce/
└── product_viewed.ts

product_viewed.ts:

// Code generated by event-spec. DO NOT EDIT.
// Source: product_viewed (v1-0-0)

export enum ProductViewedCategory {
Clothing = 'clothing',
Electronics = 'electronics',
Other = 'other',
}

export interface ProductViewedProperties {
category: ProductViewedCategory;
productId: string;
currency?: string;
}

export function productViewed(
client: Client,
props: ProductViewedProperties,
): Promise<void> {
return client.track('Product Viewed', {
category: props.category,
product_id: props.productId,
currency: props.currency,
});
}

Kotlin

generated/
├── EventSpec.kt # EventSpec class
└── ProductViewed.kt # enum, data class, suspend extension fun

EventSpec.kt:

// Code generated by event-spec. DO NOT EDIT.

package analytics

import io.eventspec.analytics.Client

class EventSpec(internal val client: Client)

ProductViewed.kt:

// Code generated by event-spec. DO NOT EDIT.
// Source: product_viewed (v1-0-0)

package analytics

enum class ProductViewedCategory(val value: String) {
CLOTHING("clothing"),
ELECTRONICS("electronics"),
OTHER("other"),
}

data class ProductViewedProperties(
val category: ProductViewedCategory,
val productId: String,
val currency: String? = null,
)

suspend fun EventSpec.productViewed(props: ProductViewedProperties, opts: TrackOptions? = null) {
client.track(
Event(
name = "Product Viewed",
properties = mapOf(
"category" to props.category.value,
"product_id" to props.productId,
"currency" to props.currency,
),
),
opts,
)
}

Usage:

val es = EventSpec(client)
es.productViewed(ProductViewedProperties(
category = ProductViewedCategory.ELECTRONICS,
productId = "SKU-123",
))

Version selection

When multiple versions of an event exist in the registry, codegen selects the latest active version by default. A source config can pin a specific version:

sources/web-app.yaml
events:
- ecommerce/product_viewed@1-0-0 # pin to v1
- ecommerce/** # latest active for everything else

Supported languages

LanguageStatus
Go✅ Available
TypeScript✅ Available
Kotlin✅ Available
Swift❌ Planned
Python❌ Planned
Rust❌ Planned

Running codegen

# With a source config
event-spec generate web-app

# Ad-hoc flags
event-spec generate --lang go --out ./generated
event-spec generate --lang typescript --out ./src/analytics/generated
event-spec generate --lang kotlin --out ./generated

See CLI — generate for full flag documentation.