Skip to content

chore: version packages#9

Merged
Tom Tang (qwerzl) merged 1 commit into
masterfrom
changeset-release/master
Apr 15, 2026
Merged

chore: version packages#9
Tom Tang (qwerzl) merged 1 commit into
masterfrom
changeset-release/master

Conversation

@photon-action-bot
Copy link
Copy Markdown
Contributor

@photon-action-bot photon-action-bot Bot commented Apr 14, 2026

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to master, this PR will be updated.

Releases

@vellum-docs/cli@0.3.0

Minor Changes

  • 2a9986c: Add vellum build --watch with per-template invalidation

    Edit a template or a source file and Vellum re-renders only the output files affected by the change. A docs project that wires vellum build --watch alongside the host's dev server (Next.js, Mintlify, etc.) no longer needs a restart loop to see TSDoc edits.

    vellum build --watch

    How invalidation works

    Templates are instrumented while rendering. The symbol(), symbols(), and module() globals record what each .vel file actually read:

    • symbol(id) - the looked-up SymbolId,
    • module(path) - the module path,
    • symbols(query) - the verbatim query and the resulting ids.

    On a source-file change, Vellum diffs the file's old vs new symbols (source-position-insensitive content hash) and re-renders templates whose recorded reads intersect the diff. Added symbols are re-matched against each template's prior queries so a new symbols({ tag: 'foo' }) result triggers the right renders.

    Template edits re-render only that template. Config changes tear down the watcher and re-prime. Render and extract errors log and keep the previous output on disk - the watcher stays alive.

    API changes (minor - pre-1.0)

    TemplateEngine.render returns RenderResult, not string. Custom engines update the return shape:

    // before
    async render(source, ctx): Promise<string> { ... }
    
    // after
    async render(source, ctx): Promise<RenderResult> {
      return { output }
      // or { output, reads } when ctx.reads was supplied
    }

    SymbolIndex gains symbolsByFile and removeByFile. Custom index implementations need both. InMemorySymbolIndex now tracks per-file provenance via Symbol.source.file.

    matchesQuery(sym, query) is exported from @vellum-docs/core - the exact predicate InMemorySymbolIndex.symbols() uses, reusable by tooling that needs to test a single symbol against a query.

    New exports (@vellum-docs/core)

    • TemplateReads, createTemplateReads()
    • SymbolDiff, diffSymbols, hashSymbol, mergeDiffs, emptyDiff, isEmptyDiff
    • DependencyGraph
    • Vellum.extractLanguage, Vellum.renderTemplate, Vellum.listTemplates

    New exports (@vellum-docs/cli)

    • runWatch, WatchCommandOptions

    Watcher details

    File watching uses chokidar v4 with Nuxt-style granular filtering: node_modules, .git, .turbo, dist, build, coverage, and the resolved outDir are ignored anywhere in the tree, even if a user's sources[lang].include would otherwise cover them. Events are debounced 100ms; awaitWriteFinish handles editor save races.

    Known tradeoff

    Per-file TS extraction isn't meaningful in isolation (cross-file type graph), so extractLanguage re-runs the extractor over the whole program on each batch. Invalidation still narrows the render set - which is what dominates latency for a typical docs project - but we don't yet skip extraction for unchanged files beyond the existing content-hash cache.

Patch Changes

  • 3840674: Refactor CLI to citty + consola

    Replaced hand-rolled argv parsing and console.log with two unjs libraries:

    • citty drives argument parsing, subcommand routing, and help generation. vellum --help and vellum build --help now produce consistent typed output instead of a static string.
    • consola handles every user-facing log through a shared tagged logger. Output is color-coded by level (success, info, warn, error) with a uniform [vellum] prefix.

    No breaking CLI surface changes — vellum build, --watch, --config, --cwd, --no-strict all behave identically. --no-strict now comes from citty's boolean-negation convention instead of being parsed by hand.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

@vellum-docs/core@0.3.0

Minor Changes

  • 2a9986c: Add vellum build --watch with per-template invalidation

    Edit a template or a source file and Vellum re-renders only the output files affected by the change. A docs project that wires vellum build --watch alongside the host's dev server (Next.js, Mintlify, etc.) no longer needs a restart loop to see TSDoc edits.

    vellum build --watch

    How invalidation works

    Templates are instrumented while rendering. The symbol(), symbols(), and module() globals record what each .vel file actually read:

    • symbol(id) - the looked-up SymbolId,
    • module(path) - the module path,
    • symbols(query) - the verbatim query and the resulting ids.

    On a source-file change, Vellum diffs the file's old vs new symbols (source-position-insensitive content hash) and re-renders templates whose recorded reads intersect the diff. Added symbols are re-matched against each template's prior queries so a new symbols({ tag: 'foo' }) result triggers the right renders.

    Template edits re-render only that template. Config changes tear down the watcher and re-prime. Render and extract errors log and keep the previous output on disk - the watcher stays alive.

    API changes (minor - pre-1.0)

    TemplateEngine.render returns RenderResult, not string. Custom engines update the return shape:

    // before
    async render(source, ctx): Promise<string> { ... }
    
    // after
    async render(source, ctx): Promise<RenderResult> {
      return { output }
      // or { output, reads } when ctx.reads was supplied
    }

    SymbolIndex gains symbolsByFile and removeByFile. Custom index implementations need both. InMemorySymbolIndex now tracks per-file provenance via Symbol.source.file.

    matchesQuery(sym, query) is exported from @vellum-docs/core - the exact predicate InMemorySymbolIndex.symbols() uses, reusable by tooling that needs to test a single symbol against a query.

    New exports (@vellum-docs/core)

    • TemplateReads, createTemplateReads()
    • SymbolDiff, diffSymbols, hashSymbol, mergeDiffs, emptyDiff, isEmptyDiff
    • DependencyGraph
    • Vellum.extractLanguage, Vellum.renderTemplate, Vellum.listTemplates

    New exports (@vellum-docs/cli)

    • runWatch, WatchCommandOptions

    Watcher details

    File watching uses chokidar v4 with Nuxt-style granular filtering: node_modules, .git, .turbo, dist, build, coverage, and the resolved outDir are ignored anywhere in the tree, even if a user's sources[lang].include would otherwise cover them. Events are debounced 100ms; awaitWriteFinish handles editor save races.

    Known tradeoff

    Per-file TS extraction isn't meaningful in isolation (cross-file type graph), so extractLanguage re-runs the extractor over the whole program on each batch. Invalidation still narrows the render set - which is what dominates latency for a typical docs project - but we don't yet skip extraction for unchanged files beyond the existing content-hash cache.

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

@vellum-docs/engine-nunjucks@0.3.0

Minor Changes

  • 2a9986c: Add vellum build --watch with per-template invalidation

    Edit a template or a source file and Vellum re-renders only the output files affected by the change. A docs project that wires vellum build --watch alongside the host's dev server (Next.js, Mintlify, etc.) no longer needs a restart loop to see TSDoc edits.

    vellum build --watch

    How invalidation works

    Templates are instrumented while rendering. The symbol(), symbols(), and module() globals record what each .vel file actually read:

    • symbol(id) - the looked-up SymbolId,
    • module(path) - the module path,
    • symbols(query) - the verbatim query and the resulting ids.

    On a source-file change, Vellum diffs the file's old vs new symbols (source-position-insensitive content hash) and re-renders templates whose recorded reads intersect the diff. Added symbols are re-matched against each template's prior queries so a new symbols({ tag: 'foo' }) result triggers the right renders.

    Template edits re-render only that template. Config changes tear down the watcher and re-prime. Render and extract errors log and keep the previous output on disk - the watcher stays alive.

    API changes (minor - pre-1.0)

    TemplateEngine.render returns RenderResult, not string. Custom engines update the return shape:

    // before
    async render(source, ctx): Promise<string> { ... }
    
    // after
    async render(source, ctx): Promise<RenderResult> {
      return { output }
      // or { output, reads } when ctx.reads was supplied
    }

    SymbolIndex gains symbolsByFile and removeByFile. Custom index implementations need both. InMemorySymbolIndex now tracks per-file provenance via Symbol.source.file.

    matchesQuery(sym, query) is exported from @vellum-docs/core - the exact predicate InMemorySymbolIndex.symbols() uses, reusable by tooling that needs to test a single symbol against a query.

    New exports (@vellum-docs/core)

    • TemplateReads, createTemplateReads()
    • SymbolDiff, diffSymbols, hashSymbol, mergeDiffs, emptyDiff, isEmptyDiff
    • DependencyGraph
    • Vellum.extractLanguage, Vellum.renderTemplate, Vellum.listTemplates

    New exports (@vellum-docs/cli)

    • runWatch, WatchCommandOptions

    Watcher details

    File watching uses chokidar v4 with Nuxt-style granular filtering: node_modules, .git, .turbo, dist, build, coverage, and the resolved outDir are ignored anywhere in the tree, even if a user's sources[lang].include would otherwise cover them. Events are debounced 100ms; awaitWriteFinish handles editor save races.

    Known tradeoff

    Per-file TS extraction isn't meaningful in isolation (cross-file type graph), so extractLanguage re-runs the extractor over the whole program on each batch. Invalidation still narrows the render set - which is what dominates latency for a typical docs project - but we don't yet skip extraction for unchanged files beyond the existing content-hash cache.

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

@vellum-docs/extractor-typescript@0.3.0

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

@vellum-docs/language-server@0.3.0

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

@vellum-docs/profile-markdown@0.3.0

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

@vellum-docs/profile-mintlify@0.3.0

Patch Changes

  • ad1aa14: Add cell filter + TypeString.oneline for cell-safe rendering

    Every adopter was hand-rolling the same 5-filter escape chain when dropping types or summaries into markdown table cells:

    {{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}

    Two additions collapse that:

    TypeString.oneline?: string - populated at extraction time with the whitespace-collapsed form of text. Omitted when equal to text (single-line case). Fixes the \n + indentation problem at source, before any template filter runs.

    cell filter (profile-routed) - accepts a TypeString, plain string, or null. Collapses whitespace as defence-in-depth, routes through the profile's new cell(value, ctx) method, which wraps in a code span and escapes |. Works for anything cell-bound, not just types.

    Before:

    | `{{ m.name }}` | `{{ m.type.text | replace("\n"," ") | replace("    ","") | replace("|","\\|") | replace("<","&lt;") | replace(">","&gt;") }}` | {{ m.doc.summary }} |

    After:

    | `{{ m.name }}` | {{ m.type | cell | safe }} | {{ m.doc.summary | cell | safe }} |

    Schema additions (additive)

    • TypeString.oneline?: string
    • RendererProfile.cell(value: string, ctx: RenderContext): string

    Existing extractors keep working - when oneline is absent the filter falls back to .text. Existing profiles get the new method implemented in MarkdownProfile and MintlifyProfile; third-party profiles must add a cell implementation.

    Out of scope

    jsx-prop and fenced contexts from the original request. Neither has recurring template pain today; defer until they do.

  • 6503a35: Philosophy audit fixes - strict-by-default, dead schema cleanup

    An audit against the newly-written PHILOSOPHY.md surfaced four gaps. This changeset closes them.

    Strict template rendering is now on by default. Principle 11 ("fail loudly at build time") was being violated by throwOnUndefined: false - a template with a typo ({{ fn.doc.summaryy }} instead of fn.doc.summary) silently rendered as empty string, and the docs shipped with a blank section. The NunjucksEngine now defaults to strict rendering: any output of an undefined value throws, which bubbles to a non-zero build exit.

    Opt-out paths, for the rare cases where silent fallback is wanted during migration:

    • Config: new NunjucksEngine({ strict: false }).
    • CLI: vellum build --no-strict.

    This is a behavior change. Templates that relied on silent-empty for undefined values will now fail. Typical patterns that are still safe: {% if sym.members %}, {{ sym.doc.summary }} (empty string is defined), {% for m in sym.members or [] %}. The patterns that will now break are the ones you wanted to know about anyway.

    Schema cleanup. Three dead schema fields removed - they were defined but never populated by any extractor, violating principle 7 ("80% case defines the schema"):

    • Symbol.signatureResolved?: string - removed.
    • Member.kind values 'index' and 'call' - removed from the union. Can be added back with implementation when a TS call/index-signature extractor lands or a language that needs them ships.

    Docs drift fixes. ARCHITECTURE.md referenced a {{ str | tsdoc }} filter that never existed; replaced with {{ sym | summary }} (which does). Principle 2 in PHILOSOPHY.md now explicitly distinguishes "pattern-aware" (OK) from "language-idiosyncratic" (not OK), so Symbol.discriminator? is consistent with the stated rule.

  • Updated dependencies [2a9986c]

  • Updated dependencies [ad1aa14]

  • Updated dependencies [6503a35]

    • @vellum-docs/core@0.3.0

vellum-example-basic@0.0.8

Patch Changes

  • Updated dependencies [2a9986c]
  • Updated dependencies [ad1aa14]
  • Updated dependencies [3840674]
  • Updated dependencies [6503a35]
    • @vellum-docs/cli@0.3.0
    • @vellum-docs/core@0.3.0
    • @vellum-docs/engine-nunjucks@0.3.0
    • @vellum-docs/extractor-typescript@0.3.0
    • @vellum-docs/profile-markdown@0.3.0
    • @vellum-docs/profile-mintlify@0.3.0

vellum-example-nextjs@0.0.8

Patch Changes

  • Updated dependencies [2a9986c]
  • Updated dependencies [ad1aa14]
  • Updated dependencies [3840674]
  • Updated dependencies [6503a35]
    • @vellum-docs/cli@0.3.0
    • @vellum-docs/core@0.3.0
    • @vellum-docs/engine-nunjucks@0.3.0
    • @vellum-docs/extractor-typescript@0.3.0
    • @vellum-docs/profile-markdown@0.3.0

@photon-action-bot photon-action-bot Bot force-pushed the changeset-release/master branch 3 times, most recently from c1bbb79 to 9cf8a1b Compare April 14, 2026 22:29
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vellum-docs Ready Ready Preview, Comment Apr 15, 2026 3:32am

@photon-action-bot photon-action-bot Bot force-pushed the changeset-release/master branch from 170c2dc to dd796db Compare April 15, 2026 03:31
@qwerzl Tom Tang (qwerzl) merged commit 4f6014e into master Apr 15, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant