# Codoc Unified Documentation Schema
A unified JSON schema for documenting Ruby (Yardoc) and TypeScript (TypeDoc) codebases.
## Design Principles
1. **Simplicity over completeness** - Captures what's needed for rendering documentation, not every AST detail
2. **Language-neutral where possible** - Common abstractions for similar concepts
3. **Language-specific where necessary** - Ruby concerns, TypeScript interfaces, etc.
4. **Flat references** - Uses `ref` IDs for cross-linking instead of deep nesting
5. **Human-readable** - Clear property names, sensible defaults
6. **Linkable** - Symbol table and external link mappings enable documentation generators to create hyperlinks
## Core Entity Types
| `module` | Namespace/container | Module | Namespace, Module |
| `class` | Class definition | Class | Class |
| `interface` | Type contract | - | Interface |
| `type` | Type alias | - | Type alias |
| `enum` | Enumeration | - | Enum |
| `method` | Instance method | Method | Method |
| `function` | Standalone function | Module function | Function |
| `property` | Instance property | `attr_*` | Property |
| `constant` | Constant value | Constant | Const |
## Type References
Types are represented uniformly with a `kind` discriminator:
```json
// Simple type
{ "name": "String", "kind": "simple" }
// Generic type: Array<String>
{ "name": "Array", "kind": "generic", "args": [{ "name": "String", "kind": "simple" }] }
{ "name": "String", "kind": "simple" },
{ "name": "null", "kind": "simple" }
]}
// Nullable shorthand
{ "name": "String", "kind": "simple", "nullable": true }
```
## Documentation Fields
Every entity can have a `docs` object:
```json
{
"docs": {
"summary": "Brief one-liner",
"description": "Full description with markdown",
"examples": [{ "code": "...", "language": "ruby" }],
"see": [{ "text": "Related", "ref": "SomeClass" }],
"notes": ["Important note"],
"deprecated": { "message": "Use X instead", "replacement": "X" },
"tags": { "custom_tag": "value" }
}
}
```
## Ruby-Specific Features
### Concerns and Mixins
```json
{
"kind": "module",
"isConcern": true,
"instanceMethods": [...],
"classMethods": [...]
}
```
### Module Inclusion
```json
{
"kind": "class",
"includes": [{ "name": "Enumerable", "kind": "simple" }],
"prepends": [{ "name": "SomePrepend", "kind": "simple" }]
}
```
### Hash Options (`@option` tags)
```json
{
"name": "options",
"type": { "name": "Hash", "kind": "simple" },
"options": [
{ "name": "format", "type": { "name": "Symbol", "kind": "simple" }, "description": "..." },
{ "name": "prefix", "type": { "name": "String", "kind": "simple" }, "required": false }
]
}
```
### Yield Blocks
```json
{
"yields": {
"description": "Yields for configuration",
"params": [{ "name": "config", "type": { "name": "Config", "kind": "simple" } }],
"returns": { "name": "void", "kind": "simple" }
}
}
```
## TypeScript-Specific Features
### Generic Type Parameters
```json
{
"typeParams": [
{
"name": "T",
"constraint": { "name": "Element", "kind": "simple" },
"default": { "name": "HTMLElement", "kind": "simple" }
}
]
}
```
### Interfaces with Index Signatures
```json
{
"kind": "interface",
"indexSignatures": [{
"keyType": { "name": "string", "kind": "simple" },
"valueType": { "name": "unknown", "kind": "simple" }
}]
}
```
### Method Overloads
```json
{
"kind": "method",
"overloads": [
{ "params": [...], "returns": {...} },
{ "params": [...], "returns": {...} }
]
}
```
## ID Convention
Entity IDs follow a consistent pattern:
- `ModuleName` - Top-level module
- `ModuleName::ClassName` - Nested class (Ruby style, works for both)
- `ClassName#methodName` - Instance method
- `ClassName.methodName` - Static/class method
- `ClassName.propertyName` - Property
## Linking System
The schema provides a comprehensive linking system for documentation generators.
### Symbol Table
The `symbols` array provides a flat lookup table of all documented entities:
```json
{
"symbols": [
{
"id": "EventBus",
"name": "EventBus",
"qualifiedName": "EventBus",
"kind": "class",
"parent": null,
"file": "src/EventBus.ts",
"line": 25,
"signature": "EventBus<TSource = unknown>"
},
{
"id": "EventBus#on",
"name": "on",
"qualifiedName": "EventBus#on",
"kind": "method",
"parent": "EventBus",
"file": "src/EventBus.ts",
"line": 45,
"signature": "on(eventNames, callback, signal?)"
}
]
}
```
### ID Prefix
When combining documentation from multiple sources (e.g., a project with both Ruby and TypeScript), use `idPrefix`:
```json
{
"metadata": {
"name": "my-project",
"language": "typescript",
"idPrefix": "ts:"
}
}
```
All entity IDs will be prefixed: `ts:EventBus`, `ts:EventBus#on`, etc.
### Internal References
Use `ref` in `TypeReference` to link to documented entities:
```json
{
"type": {
"name": "EventBus",
"kind": "simple",
"ref": "EventBus"
}
}
```
### External Symbol Link Mappings
Configure external documentation links via `externalLinks` in metadata:
```json
{
"metadata": {
"externalLinks": {
"typescript": {
"baseUrl": "https://developer.mozilla.org/en-US/docs/Web/API",
"urlTemplate": "{baseUrl}/{name}",
"symbols": {
"EventTarget": "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget",
"Element": "https://developer.mozilla.org/en-US/docs/Web/API/Element"
}
},
"ruby": {
"baseUrl": "https://ruby-doc.org/core-3.3.0",
"urlTemplate": "{baseUrl}/{qualifiedName}.html",
"transform": {
"nameSeparator": "/",
"methodPrefix": "#method-i-",
"classMethodPrefix": "#method-c-"
}
},
"rails": {
"baseUrl": "https://api.rubyonrails.org/classes",
"urlTemplate": "{baseUrl}/{qualifiedName}.html",
"transform": { "nameSeparator": "/" },
"symbols": {
"ActiveRecord::Base": "https://api.rubyonrails.org/classes/ActiveRecord/Base.html"
}
}
}
}
}
```
Reference external types with `package` and `qualifiedName`:
```json
{
"type": {
"name": "EventTarget",
"kind": "simple",
"package": "typescript",
"qualifiedName": "EventTarget"
}
}
```
### Unresolved References
The `unresolvedReferences` array lists symbols that couldn't be resolved:
```json
{
"unresolvedReferences": [
{
"name": "JQuery",
"qualifiedName": "JQuery",
"package": "jquery",
"referencedFrom": ["HybridElement"]
}
]
}
```
This helps documentation generators identify missing external link mappings.
### Link Resolution Algorithm
For a documentation generator to resolve a type reference:
1. If `ref` is set → look up in `symbols` array (internal link)
2. If `package` is set → look up in `externalLinks[package]`:
- Check `symbols[qualifiedName]` for explicit URL
- Otherwise, apply `urlTemplate` with `transform` rules
3. If neither → unlinked type name
## Comparison with TypeDoc JSON
| Type representation | Complex nested structure with `kindString` | Simple `kind` discriminator |
| References | Numeric IDs with separate symbol table | String IDs inline |
| Documentation | Split across `comment.summary`, `comment.blockTags` | Unified `docs` object |
| Visibility | `flags.isPrivate`, `flags.isProtected` | Single `visibility` field |
| Source location | Nested `sources[0].fileName` | Flat `location.file` |
## Root Structure
```json
{
"schema": "codoc/v1",
"metadata": {
"name": "project-name",
"version": "1.0.0",
"language": "typescript",
"generatedAt": "2025-12-16T10:00:00Z",
"sourceRoot": "src",
"idPrefix": "ts:",
"files": ["..."],
"externalLinks": { "..." }
},
"entities": [ "..." ],
"symbols": [ "..." ],
"unresolvedReferences": [ "..." ]
}
```
## Files
- `schema.json` - JSON Schema definition
- `examples/typescript-example.json` - TypeScript example with external link mappings
- `examples/ruby-example.json` - Ruby example with external link mappings