Skip to content

Latest commit

 

History

History
277 lines (197 loc) · 6.88 KB

File metadata and controls

277 lines (197 loc) · 6.88 KB

Codegen - Effect Schema Generation

Generate type-safe Effect Schema class definitions from your Tana supertags. This enables building applications with full type safety based on your Tana workspace structure.

Quick Start

# Generate schemas for all supertags
supertag codegen generate -o ./generated/schemas.ts

# Generate for specific supertags only
supertag codegen generate -o ./generated/todo.ts --tags Todo Meeting

# Preview without writing files
supertag codegen generate -o ./generated/schemas.ts --dry-run

Commands

codegen generate

Generate Effect Schema classes from supertag definitions.

supertag codegen generate -o <output-path> [options]

Required:

  • -o, --output <path> - Output file path

Options:

  • -t, --tags <tags...> - Filter to specific supertag names
  • --split - Generate separate file per supertag (creates directory structure)
  • --optional <strategy> - How to handle optional fields: option (default), undefined, or nullable
  • --no-metadata - Exclude supertag metadata comments from output
  • -d, --dry-run - Preview generated code without writing files
  • -w, --workspace <name> - Use specific workspace (default: main)

Output Format

Single File (Default)

supertag codegen generate -o ./schemas.ts

Generates all supertags in one file:

/**
 * Generated by supertag-cli codegen
 * Source: main workspace
 * Generated: 2025-12-28T12:00:00.000Z
 */

import { Schema } from "effect";

/**
 * Todo supertag
 * Fields: 4
 */
export class Todo extends Schema.Class<Todo>("Todo")({
  id: Schema.String,
  title: Schema.optionalWith(Schema.String, { as: "Option" }),
  dueDate: Schema.optionalWith(Schema.DateFromString, { as: "Option" }),
  completed: Schema.optionalWith(Schema.Boolean, { as: "Option" }),
  priority: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}

Split Files

supertag codegen generate -o ./schemas/index.ts --split

Creates a directory structure:

schemas/
├── index.ts      # Re-exports all schemas
├── Todo.ts       # Todo schema
├── Meeting.ts    # Meeting schema
└── Person.ts     # Person schema

Type Mapping

Tana field types are mapped to Effect Schema types:

Tana Type Effect Schema Notes
text Schema.String Default for unknown types
number Schema.Number Numeric values
date Schema.DateFromString Parses ISO date strings
checkbox Schema.Boolean True/false values
url Schema.String.pipe(Schema.pattern(/^https?:\/\//)) URL validation
email Schema.String Email addresses
reference Schema.String Node ID references
options Schema.String Option field values

Optional Field Strategies

Control how optional fields are represented:

Option (Default)

supertag codegen generate -o ./schemas.ts --optional option
// Uses Effect's Option type - recommended for Effect-based apps
title: Schema.optionalWith(Schema.String, { as: "Option" }),

Undefined

supertag codegen generate -o ./schemas.ts --optional undefined
// Standard TypeScript optional - simpler but less type-safe
title: Schema.optional(Schema.String),

Nullable

supertag codegen generate -o ./schemas.ts --optional nullable
// Allows null values - for APIs that use null
title: Schema.optionalWith(Schema.String, { nullable: true }),

Supertag Inheritance

Supertags that extend other supertags generate proper class inheritance:

// Base supertag
export class Task extends Schema.Class<Task>("Task")({
  id: Schema.String,
  title: Schema.optionalWith(Schema.String, { as: "Option" }),
  status: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}

// Child supertag extends parent
export class WorkTask extends Task.extend<WorkTask>("WorkTask")({
  project: Schema.optionalWith(Schema.String, { as: "Option" }),
  assignee: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}

Using Generated Schemas

Decoding Data

import { Schema } from "effect";
import { Todo } from "./schemas";

// Decode unknown data
const decodeTodo = Schema.decodeUnknown(Todo);

const result = decodeTodo({
  id: "abc123",
  title: "Buy groceries",
  completed: false,
});

// result is Effect<Todo, ParseError>

With Effect Runtime

import { Effect, Either } from "effect";
import { Todo } from "./schemas";

const program = Effect.gen(function* () {
  const todo = yield* Schema.decode(Todo)({
    id: "abc123",
    title: "Important task",
    priority: "high",
  });

  console.log(todo.title); // Option<string>
  return todo;
});

Effect.runPromise(program);

Type Extraction

import { Schema } from "effect";
import { Todo } from "./schemas";

// Extract the decoded type
type TodoType = Schema.Schema.Type<typeof Todo>;

// Extract the encoded type (for serialization)
type TodoEncoded = Schema.Schema.Encoded<typeof Todo>;

Example Application

See the TUI Todo example for a complete application demonstrating:

  • Generated schemas from Tana supertags
  • SQLite database queries using supertag-cli indexed data
  • Creating new nodes via Tana Input API
  • Terminal UI with Ink (React for CLIs)
cd examples/tui-todo
bun install
bun run start

Integration with supertag-cli

The codegen feature reads from the supertag-cli database, which is populated by:

# Export from Tana
supertag-export run

# Index the export
supertag sync index

Supertag definitions including field types and inheritance are extracted during indexing and stored in the supertag_metadata and supertag_fields tables.

Best Practices

  1. Regenerate after schema changes: When you modify supertags in Tana, re-export and regenerate schemas.

  2. Use version control: Commit generated schemas to track changes over time.

  3. Consider split mode for large workspaces: With many supertags, split mode keeps files manageable.

  4. Match optional strategy to your codebase: Use option for Effect-heavy code, undefined for simpler TypeScript.

  5. Filter to relevant supertags: Use --tags to generate only what you need.

Troubleshooting

"No supertags found"

Ensure you've indexed your Tana export:

supertag sync index

"Unknown field type"

Fields with unrecognized types default to Schema.String. This is safe but may not validate as expected.

Schema compilation errors

Run with --dry-run first to preview the output. Check for:

  • Reserved TypeScript keywords in field names
  • Special characters in supertag names
  • Circular inheritance (not supported)

See Also