Skip to content

Implementing Keel

Typegen contract

Generate .ts, .d.ts, and keel/1 JSON from page and action types. Consume them from any language.

Page ids plus kotlinx.serialization payload types are the contract. Typegen walks @KeelType / @KeelAction (or a live PageRegistry + ActionRegistry) and emits two artifacts.

TypeScript

Default --format ts writes interfaces the pack imports. --format dts writes the same declarations for a .d.ts entry.

export interface HomePage { greeting: string }
export interface HarborPages {
  "harbor.home": HomePage
}
export type HarborPageId = keyof HarborPages
export interface HarborActions {
  "harbor.setName": { in: SetNameIn; out: SetNameOut }
}

JSON

--emit-json path writes a language-agnostic document another codegen reads without parsing TypeScript. A running host serves the same document at GET /__keel/schema.

{
  "format": "keel/1",
  "pagesName": "Pages",
  "pages": {
    "harbor.home": {
      "type": "HomePage",
      "path": "/",
      "methods": [
        "GET"
      ]
    }
  },
  "actions": {
    "harbor.setName": {
      "in": "SetNameIn",
      "out": "SetNameOut"
    }
  },
  "types": {
    "HomePage": {
      "kind": "object",
      "fields": {
        "greeting": "string"
      }
    }
  }
}

keel-pack prefers .pages on this document. A raw id array or a scraped export interface *Pages block still works as a fallback.

Scaffold

pnpm exec keel-scaffold 127.0.0.1:8090 ./pack
pnpm exec keel-scaffold example.com ./pack --id midnight

That fetches /__keel/schema and writes blank pages in the framework you choose (--framework, default svelte). Gradle generateKeelTypes remains the offline classpath scan when no host is running. The live schema carries real page paths; the classpath scan uses placeholder /__keel-typegen/… paths because @KeelType does not store the host route.

Gradle

keelTypegen
keelTypegen {
  output.set(layout.projectDirectory.file("pack/src/lib/page-types.ts"))
  json.set(layout.projectDirectory.file("pack/src/lib/page-types.json"))
  format.set("ts")
  pagesName.set("HarborPages")
  packages.add("dev.kolektiv.keel.samples.harbor")
}

CLI equivalent:

TypegenCli --output page-types.ts --format ts --emit-json page-types.json --pages-name HarborPages --package dev.example.app

The emitter walks SerialDescriptor for payload shapes.