@sprinterai/typespec
Markdown DSL for defining entity types with fields, connections, scoring, and extraction config. Parse, compile, and generate TypeSpec definitions.
Install
pnpm add @sprinterai/typespecOverview
@sprinterai/typespec provides a Markdown-based DSL for defining entity types. Instead of writing JSON Schema and EntityTypeConfig objects by hand, you author a human-readable .md file and the package compiles it to the database-ready shape.
The package depends only on @sprinterai/core for type definitions.
Three Stages
The TypeSpec pipeline has three stages:
| Stage | Function | Direction |
|---|---|---|
| Parse | parseTypeSpec(markdown) | Markdown string to TypeSpec IR |
| Compile | compileTypeSpec(spec) | TypeSpec IR to JSON Schema + EntityTypeConfig |
| Generate | generateTypeSpec(record) | DB record back to Markdown string |
Markdown ──parse──▶ TypeSpec IR ──compile──▶ { slug, name, json_schema, config }
│
Markdown ◀──generate──────────────┘DSL Syntax
A TypeSpec file is a Markdown document with YAML frontmatter and structured sections:
---
type: opportunity
icon: target
color: #3B82F6
cardFields: [name, stage, value]
---
# Opportunity
A deal in the investment pipeline.
---
## Fields
- name: text (required) — The name of the deal or target company
- stage: select [Sourced, Screening, Due Diligence, Closed] (required) — Current pipeline stage
- value: currency (min: 0) — Estimated deal value in USD
sources: connected-entities, linked-documents
dependsOn: name
- probability: number (min: 0, max: 100) — Likelihood of closing as a percentage
- website: url — Company website URL
- tags: array — Categorization tags
## Connections
- portfolio-company → company (many) — Companies in this deal
- lead-partner → person (one) — Partner leading the deal
## Scoring
- Revenue Impact: weight 0.30, scale 1-10
- Strategic Fit: weight 0.25, scale 1-10
- Risk Level: weight 0.25, scale 1-10
- Time to Close: weight 0.20, scale 1-5Frontmatter
| Key | Required | Description |
|---|---|---|
type | Yes | Entity type slug (unique identifier) |
icon | No | Icon name for UI display |
color | No | Hex color for UI theming |
cardFields | No | Array of field names shown on entity cards |
isParent | No | Whether this type acts as a parent container |
hideFromDataNav | No | Hide from data navigation sidebar |
Fields
Each field is a list item with the format:
- field_name: type [options] (constraints) — Extraction instructions
sources: source1, source2
dependsOn: field1, field2Supported field types:
| Type | JSON Schema Output |
|---|---|
text | { type: "string" } |
long_text | { type: "string" } |
number | { type: "number" } |
currency | { type: "number" } with displayType: "currency" |
select | { type: "string", enum: [...] } |
multi_select | { type: "array", items: { type: "string", enum: [...] } } |
boolean | { type: "boolean" } |
date | { type: "string", format: "date" } |
url | { type: "string" } |
email | { type: "string" } |
array | { type: "array", items: { type: "string" } } |
Constraints: required, min: N, max: N, integer: true, label: Display Name.
Connections
Each connection follows the format:
- relationship_type → target_slug (one|many) — LabelUse * as the target slug for connections to any entity type.
Scoring
Each scoring criterion follows the format:
- Label Text: weight 0.30, scale 1-10The name is derived automatically from the label (e.g., "Revenue Impact" becomes revenue_impact).
API
parseTypeSpec
Parses a Markdown string into the TypeSpec intermediate representation.
import { parseTypeSpec } from "@sprinterai/typespec";
const spec = parseTypeSpec(markdownString);
// spec.slug, spec.name, spec.fields, spec.connections, spec.scoringcompileTypeSpec
Compiles a TypeSpec IR into the shape expected by the entity_types table.
import { compileTypeSpec } from "@sprinterai/typespec";
const { slug, name, json_schema, config } = compileTypeSpec(spec);
// json_schema: EntityJsonSchema with properties, required
// config: EntityTypeConfig with ui, fields, relations, scoringgenerateTypeSpec
Converts a database entity type record back into a Markdown string. Useful for exporting, diffing, or editing entity types as files.
import { generateTypeSpec } from "@sprinterai/typespec";
const markdown = generateTypeSpec({
slug: "opportunity",
name: "Opportunity",
json_schema: { /* ... */ },
config: { /* ... */ },
});entityTypeToTypeSpec / typeSpecToMarkdown
Lower-level functions if you need to work with the intermediate representation directly:
import { entityTypeToTypeSpec, typeSpecToMarkdown } from "@sprinterai/typespec";
// DB record → TypeSpec IR
const spec = entityTypeToTypeSpec(record);
// TypeSpec IR → Markdown string
const markdown = typeSpecToMarkdown(spec);Type Exports
| Type | Description |
|---|---|
TypeSpec | Full intermediate representation of an entity type |
TypeSpecField | A single field with name, type, options, constraints, extraction config |
TypeSpecFieldType | Union of supported field types ("text", "number", "select", etc.) |
TypeSpecConnection | A relationship to another entity type |
TypeSpecScoringCriterion | A scoring dimension with weight and scale |
CompiledEntityType | Output of compileTypeSpec: { slug, name, json_schema, config } |
Round-Trip Fidelity
The parse/compile/generate pipeline is designed for round-trip fidelity. You can parse a Markdown file, compile it to the database shape, store it, then generate the Markdown back out and get an equivalent document. This enables version-controlled entity type definitions alongside your code.
Testing
All three pipeline stages have co-located test suites. A shared makeSpec() factory in test-helpers.ts builds a minimal valid TypeSpec with safe defaults and accepts partial overrides.
import { makeSpec } from "./test-helpers";
// Minimal valid spec
const spec = makeSpec();
// Override specific fields
const specWithFields = makeSpec({
fields: [{ name: "status", type: "select", options: ["active", "inactive"] }],
});| Test file | What is covered |
|---|---|
compile.test.ts | 42 cases: all field type mappings to JSON Schema, required arrays, numeric constraints, field config (displayType, label, extraction), connections (cardinality, relation config), scoring criteria, UI config |
generate.test.ts | entityTypeToTypeSpec round-trip: field type inference, select/multi-select, connections, scoring, config passthrough, null/missing inputs |
markdown.test.ts | typeSpecToMarkdown and generateTypeSpec: frontmatter, prose, field serialization, connection serialization, scoring serialization, round-trip fidelity |