Skip to content

Configuration

Each generator function takes a config struct that controls its output paths and behavior. All config types are plain Rust structs with public fields — no builder pattern, no defaults you need to worry about.

Used by parse_schema().

FieldTypeDescription
schema_dirPathBufPath to the directory containing your schema .rs files. Typically "src/schema".
SchemaConfig {
schema_dir: "src/schema".into(),
}

All .rs files in this directory are scanned for structs with #[derive(OntologyEntity)]. Subdirectories are not traversed.


Used by gen_seaorm().

FieldTypeDescription
entity_outputPathBufOutput directory for generated SeaORM entity modules. Each entity gets its own file, plus a mod.rs.
conversion_outputPathBufOutput directory for generated from_model() / to_active_model() conversion code.
skip_conversionsVec<String>Entity names to skip when generating conversions. Use this if you write custom conversion logic for specific entities.
SeaOrmConfig {
entity_output: "src/persistence/db/entities/generated".into(),
conversion_output: "src/persistence/db/conversions/generated".into(),
skip_conversions: vec![],
}

Used by gen_markdown_io(), which takes the whole SchemaOutput and checks frontmatter keys against the keys OKF reserves; see reserved frontmatter keys.

The output_dir field controls where generated code lands. The rest describe the runtime shape of the vault (ADR 0001) and go into the generated open_vault. The returned MarkdownIoOutput carries the per-entity metadata, which gen_store consumes via Backend::Markdown.

FieldTypeDescription
output_dirPathBufOutput directory for the generated per-entity frontmatter modules.
vault_rootPathBufWhere the .md records live at runtime. Relative paths resolve against the program’s working directory (e.g., data/vault).
layoutMarkdownLayoutOn-disk arrangement of record files under the vault root.
list_capusizeHard cap on records parsed per list() before the runtime errors — ADR 0001’s explicit scale ceiling.
okfOkfOptionsOpt-in OKF artifacts; see below. OkfOptions::default() turns both off.

OkfOptions turns on the generated index.md files and the generated provenance stamp; the markdown backend guide is canonical for both, and the build configuration also produces a generated open_vault (details).

MarkdownIoConfig {
output_dir: "src/persistence/markdown/generated".into(),
vault_root: "data/vault".into(),
layout: ontogen::MarkdownLayout::PerEntityDir,
list_cap: 10_000,
okf: ontogen::OkfOptions::default(),
}
pub enum MarkdownLayout {
PerEntityDir, // vault_root/<dir_segment>/<id>.md -- the default
Flat, // vault_root/<id>.md, all entities together
}

Flat relies on the frontmatter type: discriminator and id prefixes to tell entities apart. PerEntityDir takes its directory segment from #[ontology(entity, directory = "...")].


Used by gen_dtos().

FieldTypeDescription
output_dirPathBufOutput directory for generated CreateEntityInput and UpdateEntityInput structs.
DtoConfig {
output_dir: "src/schema/dto".into(),
}

Generated DTOs include Deserialize, JsonSchema, and specta::Type derives for use across transport layers.


Used by gen_docs(), or Pipeline::docs(config). Writes a markdown data-model reference and one JSON Schema (draft 2020-12) per entity. The stage reads the parsed schema and feeds nothing downstream.

FieldTypeDescription
markdown_outputPathBufFile the markdown reference is written to (e.g., docs/data-model.md).
json_schema_dirPathBufDirectory that receives one <entity_snake>.schema.json per entity plus export.schema.json.
titleStringThe # heading of the markdown reference, and the stem of the export bundle’s title.
export_formatStringThe manifest.format constant in export.schema.json: your own name for the export bundle (e.g., determined-fitness-log).
DocsConfig {
markdown_output: "docs/data-model.md".into(),
json_schema_dir: "docs/schema".into(),
title: "Iron Log data model".into(),
export_format: "iron-log-export".into(),
}

Used by gen_store().

FieldTypeDescription
output_dirPathBufOutput directory for generated CRUD method modules. Each entity gets its own file.
hooks_dirOption<PathBuf>Directory for scaffolded hook files. When Some, hook files are created once per entity and never overwritten. When None, hook scaffolding is skipped entirely.
schema_module_pathStringRust import path for the schema module in generated code. Use ontogen::DEFAULT_SCHEMA_MODULE_PATH ("crate::schema") for the canonical default.
backendBackendWhich persistence backend the generated CRUD bodies are wired to. See Backend below and the markdown backend guide.
wikilink_policyOption<WikilinkPolicy>Whether the DTO From impls strip wikilink-shaped relation ids ([[id]] to id). None uses the backend’s default. See WikilinkPolicy below.
id_strategyIdStrategyHow create_* fills the id of a record created without one, on either backend: the default for every entity without its own #[ontology(entity, id = "...")]. See IdStrategy. Required; Pipeline sets it with store_id_strategy(..).
StoreConfig {
output_dir: "src/store/generated".into(),
hooks_dir: Some("src/store/hooks".into()),
schema_module_path: ontogen::DEFAULT_SCHEMA_MODULE_PATH.into(),
backend: ontogen::Backend::Seaorm(Some(seaorm)),
wikilink_policy: None,
id_strategy: ontogen::IdStrategy::Provided,
}

When hooks_dir is None, the generated CRUD code still compiles — but it expects your consuming crate to provide hook modules at the expected import paths. This is useful when you want full control over hook file organization.

pub enum IdStrategy {
Provided, // the caller must supply the id
SlugFromField(String), // slugify the named field, probing -2, -3, ...
Uuid, // fresh UUID v4
}

One build-time setting for both backends, with no default. The store-wide strategy is StoreConfig::id_strategy; with Pipeline, call .store_id_strategy(..) whenever the store stage is enabled, or build() fails with:

store codegen error: the store stage needs a default id strategy: call Pipeline::store_id_strategy(IdStrategy::Provided | IdStrategy::SlugFromField("<field>".into()) | IdStrategy::Uuid). It is required even when every entity sets #[ontology(entity, id = "...")], which overrides it per entity

An entity overrides the default in the schema with #[ontology(entity, id = "provided" | "uuid" | "slug(<field>)")] (see Annotations). Each entity’s create uses its override, else the default.

SlugFromField(f) requires f to be a plain String field on every entity it applies to: every entity without an override for the default, the one entity for a slug(f) override. The generator fails otherwise, naming the entity. Uuid needs the uuid feature of markdown-store in a markdown consumer. Creating records describes how a create uses the strategy, and the SeaORM dependency on ontogen-core.

Over HTTP, a create whose document has no data.id gets its id from the strategy, and a client-supplied data.id is used under every strategy. A create left with no id (Provided with no id from the client or a before_create hook, or a slug source that slugifies to nothing) is 400 {entity}_id_required; a duplicate id is 409 {entity}_already_exists. See Server Transports.

pub enum Backend {
Seaorm(Option<SeaOrmOutput>),
Markdown(MarkdownIoOutput),
}

The persistence backend is a generation-time choice (ADR 0001), not a runtime one. gen_store emits one CRUD module per entity wired to that backend’s primitives, and everything above the store — gen_api, gen_servers, gen_clients — is byte-identical between the two.

Seaorm’s payload is optional enrichment: pass the SeaOrmOutput you got from gen_seaorm for exact table and column references, or None to fall back to naming conventions. Markdown always carries metadata — the emitter genuinely needs the per-entity directory mapping (the vault layout goes into the generated open_vault instead), so you must pass the MarkdownIoOutput that gen_markdown_io returned.

pub enum WikilinkPolicy {
Strip, // [[id]] -> id on every relation field
Passthrough, // relation ids pass through untouched
}

Each backend has a default — the markdown backend strips at its typed boundary, SQL backends pass through — so wikilink_policy: None is right for almost everyone.

Set it explicitly for the hybrid case: a SQL-backed store whose wire contract still accepts wikilinked ids, for instance an API fed by markdown-authoring agents. Some(WikilinkPolicy::Strip) on a SeaORM store makes the generated DTO From impls accept "[[task-42]]" and store "task-42".


Used by gen_api().

FieldTypeDescription
output_dirPathBufOutput directory for generated CRUD API modules.
excludeVec<String>Entity names to skip. These entities won’t get generated API modules.
scan_dirsVec<PathBuf>Directories to scan for hand-written API modules. Scanned functions are merged with generated CRUD into a unified ApiOutput. When empty, only generated modules are included.
state_typeStringThe AppState type name. The scanner accepts a function when its rendered first-parameter type contains this string as a substring (&AppState, &Arc<AppState>, State<'_, Arc<AppState>> all match). See accepted signatures.
store_typeOption<String>The Store type name. Functions whose first parameter contains this substring are classified as entity-scoped (they operate on a specific store instance rather than the global state). Pick a distinctive name — "Store" will also match "StoreContext", "StoreManager", etc.
schema_module_pathStringRust import path for the schema module in generated code. Use ontogen::DEFAULT_SCHEMA_MODULE_PATH ("crate::schema") for the canonical default.
paginatedVec<String>Modules (snake_case entity names, e.g. "workout") whose generated list takes limit: Option<u64>, offset: Option<u64> and gets a generated count beside it, so a paginated handler asks the store for one page and a total. A surface whose pagination is set needs every module it pages listed here; see PaginationConfig. Pipeline sets it with .api_paginated(..).
ApiConfig {
output_dir: "src/api/v1/generated".into(),
exclude: vec![],
scan_dirs: vec!["src/api/v1".into()],
state_type: "AppState".to_string(),
store_type: Some("Store".to_string()),
schema_module_path: ontogen::DEFAULT_SCHEMA_MODULE_PATH.into(),
paginated: vec![],
}

A hand-written list or count in a scanned directory replaces the generated one of the same module: the store takes no filter, so a filtered list, and the count that matches it, are written by hand. See Filtered lists. Files under output_dir are never scanned, so output_dir may sit inside a scan directory. Every directory in scan_dirs must exist.

When scan_dirs is non-empty, gen_api parses every .rs file in those directories (excluding generated/ subdirectories) with syn. It extracts:

  • Function name and doc comment
  • Parameter names and types
  • Return type
  • The first parameter type to determine if it’s AppState-scoped or Store-scoped

Scanned functions are classified by their name pattern and parameter shape into OpKind values (List, GetById, Create, Update, Delete, CustomGet, CustomPost, EventStream). This classification drives HTTP verb and route selection in the server transport generators.

Any pub fn whose first parameter doesn’t match the substring rule, takes &self / self, or has no parameters at all is dropped from the generated output. gen_api prints one cargo:warning= per drop so they don’t go silent. See build-time skip warnings for the exact wording and how to react.

If a function genuinely doesn’t need state — a pure data transformation, a clock read, a sync OS-level side effect — annotate it with #[ontogen::stateless] to opt it into the API layer without a placeholder state parameter. The generators emit handlers without the State<...> extractor and forward no positional state argument. See Stateless utility functions.


Used by gen_servers(). Controls Rust server transports only — Axum, Tauri IPC, MCP. The parsed schema is the first argument of gen_servers, not a config field.

The Axum server speaks JSON:API 1.1: entities are resource objects, lists take filter[…], sort, include and (when paginated) page[offset]/page[limit], relationships have their own endpoints, custom ops send and answer meta-only documents, and every error is an errors[] document. Tauri IPC and MCP keep flat payloads. Besides which modules and functions are served, the fields below that shape the HTTP wire are naming (paths and resource types), route_prefix, pagination, error_source_dir (error statuses and codes) and sse_route_overrides. See Server Transports and the normative wire contract.

FieldTypeDescription
api_dirPathBufDirectory to scan for API source files when not using ApiOutput.
state_typeStringThe AppState type name for route handlers (e.g., "AppState").
service_import_pathStringImport path for service modules from the consuming crate (e.g., "crate::api::v1").
types_import_pathStringImport path for schema types (e.g., "crate::schema").
state_importStringImport path for the state type (e.g., "crate::AppState").
namingNamingConfigNaming overrides for pluralization, singularization, and labels.
generatorsVec<ServerGeneratorConfig>Which server generators to run and their output paths.
sse_route_overridesHashMap<String, String>Map from event function name to custom SSE route path. Values use colon-style params (:id); see route parameter syntax.
route_prefixOption<RoutePrefix>Optional route prefix for project-scoped routes.
store_typeOption<String>Store type for entity-scoped functions.
store_importOption<String>Import path for the Store type.
paginationOption<PaginationConfig>Pagination for the primary surface’s list operations. It covers every module on that surface, so each module’s list must take limit and offset and have a count (see ApiConfig::paginated). Over HTTP a paginated resource list takes page[offset] and page[limit] and answers with meta (total, limit, offset) and first/prev/next/last links; a list served as a custom op takes opArg[offset]/opArg[limit] and answers meta.result as {items, total, limit, offset}. See PaginationConfig.
error_source_dirOption<PathBuf>Directory scanned for the consumer’s enum AppError. Each variant’s name decides its HTTP status and code (*NotFound 404, *IdRequired 400, *AlreadyExists 409, *ParentRequired 403, otherwise 500). None means no scan, and every AppError-typed site answers 500 internal_error. The Pipeline builder sets it to its schema directory when it is None.
extra_surfacesVec<ApiSurface>API surfaces scanned in addition to the primary one the fields above describe. Their modules merge into the same router, IPC handler, MCP registry and TypeScript transport. Empty for a single-surface crate. See ApiSurface.

Controls how entity names are transformed for URLs, labels, and module names. Lives at ontogen::servers::NamingConfig.

FieldTypeDescription
plural_overridesHashMap<String, String>Module name to plural form (e.g., "evidence" to "evidence"). The kebab-cased plural is both the HTTP path segment and the JSON:API resource type (workout_set is workout-sets).
singular_overridesHashMap<String, String>Module name to singular form.
label_overridesHashMap<String, String>Module name to human label (e.g., "work_session" to "Work Session").
plural_label_overridesHashMap<String, String>Module name to human plural label.
singleton_modulesHashSet<String>Module names declared as singletons (single-entity modules like database, autostart, vault). The HTTP generator emits singular kebab URLs (/api/database/path) instead of the pluralized form. A source-side // ontogen:singleton marker reaches the same effect via ApiModule::is_singleton; both inputs are OR’d together before generators run. See the singleton modules guide.
command_overridesHashMap<String, String>Per-function override for the emitted IPC command / TS method name. Keys are "module::fn_name" (e.g., "journal::get_tag_history"); values replace the default {entity}_{fn_name} scheme. The source-side #[ontogen(rename = "...")] attribute always wins — the config entry is silently ignored when the attribute is set. HTTP route paths and the underlying Rust function name are unaffected. See Renaming a command.

Uses cruet for Rails-style inflection by default. Override maps take precedence.

let mut naming = NamingConfig::default();
naming.singleton_modules.insert("database".to_string());
naming.singleton_modules.insert("autostart".to_string());
pub enum ServerGenerator {
HttpAxum { output: PathBuf },
TauriIpc { output: PathBuf },
Mcp { output: PathBuf },
}
VariantOutputDescription
HttpAxumRust fileAxum handlers for the JSON:API wire, with an entity_routes() router constructor.
TauriIpcRust file#[tauri::command] handlers with State extraction, and an ipc_handler() to pass to tauri::Builder::invoke_handler. Arguments and results are flat.
McpRust fileMCP tool definitions with JSON Schema parameters. Arguments and results are flat.

Optional prefix for project-scoped routes (e.g., /api/projects/{project_id}/nodes). A scoped route has the shape of its unscoped one under the prefix, relationship routes and junction routes included, and every links member and Location header it answers carries the prefix; the resource type does not. A prefix parameter that does not parse as its rust_type is 400 invalid_path_parameter, and a failing state_accessor is 500 internal_error. See Route prefix.

FieldTypeDescription
segmentsStringPath segments to insert (e.g., "projects/:project_id").
state_accessorStringState method for validation (e.g., "store_for" produces state.store_for(&ontogen_scope)?, where ontogen_scope is the prefix param’s value).
paramsVec<PrefixParam>Parameters extracted from segments.

RoutePrefix::segments and sse_route_overrides take colon-style parameters (projects/:project_id), while the generated axum router emits axum 0.8’s brace style (/api/projects/{project_id}/nodes).

That asymmetry is deliberate: axum_path() normalizes every route string at emission, so build scripts written before the axum 0.8 cutover keep working unchanged. Write colons in config; expect braces in generated code and in ServersOutput.http_routes.

FieldTypeDescription
nameStringParameter name (e.g., "project_id"). In HTTP it names the route segment only: generated handlers bind the value as ontogen_scope, so any name works, including one a handler uses itself (query, id). IPC and MCP commands take it under this name.
rust_typeStringRust type (e.g., "uuid::Uuid").
ts_typeStringTypeScript type (e.g., "string").
FieldTypeDescription
default_limitu32Page size when the request gives no page[limit] (HTTP resource list), no opArg[limit] (HTTP custom-op list) or no limit (IPC, MCP, TypeScript).
max_limitu32Largest page size. A larger requested limit is clamped to it.

Over HTTP, page[limit]=0, a value that is not plain ASCII digits, or another page[…] member (page[number], page[size]) is 400 invalid_query_parameter. On a list served as a custom op, opArg[limit]=0 is allowed and answers an empty page. The generated TypeScript list method keeps its limit/offset arguments and returns PaginatedResult<T> (items, total, limit, offset) on every transport.

One directory of API modules with the import paths and store accessor its generated handlers use. Lives at ontogen::ApiSurface (also ontogen::servers::ApiSurface). The top-level api_dir, service_import_path, types_import_path, store_type and pagination fields of ServersConfig and ClientsConfig describe the primary surface; extra_surfaces adds more, and the same list must be given to both configs so the Rust handlers and the TypeScript transport agree. Pipeline::api_surface(surface) registers one surface with both stages (servers_surface / clients_surface target one stage).

FieldTypeDescription
api_dirPathBufDirectory containing this surface’s API source files.
service_import_pathStringImport path for this surface’s service modules (e.g., "determined_fitness::api"). A surface scanned from a sibling crate must name its service path by that crate’s own name, not through a crate:: re-export: crate:: paths in its files resolve against the crate this path names.
types_import_pathStringImport path for the types this surface’s handlers reference (e.g., "determined_fitness::schema").
store_accessorOption<String>The state method store-scoped handlers call to obtain the store: state.{store_accessor}().await. None means "store", which is what the primary surface always uses.
store_typeOption<String>Store type name for this surface’s entity-scoped functions. Each surface is scanned with its own store type.
paginationOption<PaginationConfig>Pagination for this surface’s list operations.
paginated_modulesVec<String>When non-empty, restricts pagination to these module names. Empty means every module of the surface paginates.
schema_dirOption<PathBuf>Directory of this surface’s #[ontology(entity)] structs. The admin registry parses it for the surface’s field definitions; without it each of the surface’s entities ships with fields: []. The surface’s TypeScript types come from the type pool either way.

Modules are merged by name across surfaces. A module present in several surfaces becomes one module under one route prefix and one command prefix, with two rules: a function name may come from one surface only (a duplicate is a build error naming the module, the function and both surfaces), and the five CRUD functions (list, get_by_id, create, update, delete) must all come from the same surface. The surface that first defines a module imports it under its own name; every other surface’s same-named module is imported as {module}_{surface_index} (use determined_fitness::api::{workout as workout_1};) and its handlers call through that alias. A type name that two surfaces import from different paths keeps the bare import for the lower-indexed surface and is written fully qualified in the other surface’s handlers. The client generators key types by bare name, so a type name should denote one type across every surface: two distinct types that share a name would collapse into one TypeScript type, and the admin registry rejects an entity name that more than one surface’s schema defines. route_prefix cannot be combined with an extra surface that has a store_type: scoped handlers open the store through the prefix’s state_accessor, which yields the primary surface’s store.

ApiSurface {
api_dir: "../determined-fitness/src/api".into(),
service_import_path: "determined_fitness::api".into(),
types_import_path: "determined_fitness::schema".into(),
store_accessor: Some("fitness_store".into()),
store_type: Some("Store".into()),
pagination: Some(PaginationConfig { default_limit: 50, max_limit: 500 }),
paginated_modules: vec!["workout_set".into()],
schema_dir: Some("../determined-fitness/src/schema".into()),
}

Used by gen_clients(). The sibling of ServersConfig for the client-side surface: TypeScript bindings, the HTTP and HTTP+IPC transport clients, and the admin registry.

The two configs overlap on naming, state, and routing because those decisions drive both sides of the wire — URL pluralization, pagination, route prefixes. Both stages also take the parsed schema as their first argument, which is where the admin-registry generator gets its per-field metadata and enum values and where the sort keys come from; an empty schema (&SchemaOutput::default()) ships admin-registry.ts with empty fields: [] per entity. The shared types are re-exported from ontogen::servers. naming, route_prefix, pagination, sse_route_overrides and extra_surfaces must match the ServersConfig values, or the TypeScript transport calls paths and parameters the server does not serve.

ClientsConfig has no Default. ClientsConfig::new(api_dir, state_type, service_import_path, types_import_path, state_import) fills every other field with its inert value (no generators, no formatting, no route prefix, no store, no pagination, no label overrides), so a build script can write ClientsConfig { generators: vec![...], ..ClientsConfig::new(...) } and keep compiling when a field is added.

FieldTypeDescription
api_dirPathBufDirectory to scan for API source files when not using ApiOutput.
state_typeStringThe AppState type name (e.g., "AppState").
service_import_pathStringImport path for service modules (e.g., "crate::api::v1").
types_import_pathStringImport path for schema types (e.g., "crate::schema").
state_importStringImport path for the state type (e.g., "crate::AppState").
namingNamingConfigNaming overrides. Same type and semantics as on ServersConfig.
generatorsVec<ClientGenerator>Which client generators to run. See ClientGenerator below.
ts_formatterTsFormatterHow to format the generated TypeScript. See TsFormatter below. Defaults to TsFormatter::None.
sse_route_overridesHashMap<String, String>Map from event function name to custom SSE route path.
ts_skip_commandsVec<String>IPC command names to omit from the generated TypeScript. Skipped commands still get server-side handlers.
route_prefixOption<RoutePrefix>Optional route prefix. Same type as on ServersConfig.
store_typeOption<String>Store type for entity-scoped functions.
store_importOption<String>Import path for the Store type.
paginationOption<PaginationConfig>Pagination for list operations. Same type as on ServersConfig.
label_overridesHashMap<String, String>Admin-registry field labels that replace the title-cased field name, keyed module.field for one entity’s field (the snake_case module name: workout_set.rpe) or field for that field on every entity: ("avg_hr_bpm", "Average HR (bpm)"). Not part of the wire.
pool_extra_rootsVec<PathBuf>Extra source roots for the ontogen-ts type pool, beyond CARGO_MANIFEST_DIR/src. See pool_extra_roots below.
pool_exclude_pathsVec<PathBuf>Source paths to drop from the pool after scanning. See pool_exclude_paths below.
extra_surfacesVec<ApiSurface>API surfaces scanned in addition to the primary one. Same type and rules as on ServersConfig; register a surface on both stages with Pipeline::api_surface.
ClientsConfig {
api_dir: "src/api/v1".into(),
state_type: "AppState".into(),
service_import_path: "crate::api::v1".into(),
types_import_path: "crate::schema".into(),
state_import: "crate::AppState".into(),
naming: NamingConfig::default(),
generators: vec![
ClientGenerator::HttpTauriIpcSplit {
output: "../src-nuxt/app/generated/transport.ts".into(),
bindings_path: "../src-nuxt/app/generated/types.ts".into(),
},
ClientGenerator::AdminRegistry {
output: "../src-nuxt/app/admin/generated/admin-registry.ts".into(),
},
],
ts_formatter: ontogen::TsFormatter::None,
sse_route_overrides: Default::default(),
ts_skip_commands: vec![],
route_prefix: None,
store_type: Some("Store".into()),
store_import: Some("crate::store::Store".into()),
pagination: None,
label_overrides: Default::default(),
pool_extra_roots: Vec::new(),
pool_exclude_paths: Vec::new(),
extra_surfaces: vec![],
}

Lives at ontogen::clients::ClientGenerator.

pub enum ClientGenerator {
HttpTauriIpcSplit { output: PathBuf, bindings_path: PathBuf },
HttpTs { output: PathBuf, bindings_path: PathBuf },
AdminRegistry { output: PathBuf },
}
VariantOutputDescription
HttpTauriIpcSplitTypeScript fileUnified client that uses HTTP in browsers and Tauri IPC in desktop apps.
HttpTsTypeScript fileHTTP-only TypeScript client.
AdminRegistryTypeScript fileEntity metadata for admin UI components.

bindings_path is an output, not an input — Ontogen rewrites that file on every build. See the TypeScript bindings guide.

Ontogen does not format generated TypeScript for you. The default emits exactly what the generators produced:

pub enum TsFormatter {
None, // emit as generated -- the default
Custom { /* ... */ }, // format in-process via a caller-supplied hook
Command(Vec<String>), // shell out to an external formatter
}

Shell out to an external formatter. The output file’s resolved path is appended as the final argument, so tools like prettier can resolve their own config. The command runs from the nearest ancestor node_modules directory when one exists:

ts_formatter: TsFormatter::Command(vec![
"prettier".into(),
"--stdin-filepath".into(),
]),

Format in-process with a library of your choice. The hook receives the source and the resolved output path:

ts_formatter: TsFormatter::custom(|src, _path| {
my_formatter::format(src).map_err(|e| e.to_string())
}),

TsFormatter::custom fails the build if the hook returns Err. Use TsFormatter::custom_with(f, OnFormatError::…) to choose a different policy — useful when a formatter is nice-to-have but shouldn’t be able to break codegen.

By default the ontogen-ts type pool scans only CARGO_MANIFEST_DIR/src. Add sibling crates’ source roots here when long-tail types live outside the consuming crate. Paths resolve relative to CARGO_MANIFEST_DIR.

pool_extra_roots: vec!["../my-schema/src".into()],

Each root’s types are keyed under that crate’s name, read from its Cargo.toml and normalized the way Cargo does (- to _); the consuming crate’s own types are keyed under crate. So a sibling and the consumer can both define lint::Severity without colliding, and references resolve the way rustc would — my_schema::lint::Severity names the sibling’s, a bare Severity names the consumer’s.

Pool entries whose module path lies under any of these paths are dropped before the long-tail resolver runs. Rooted at CARGO_MANIFEST_DIR, mirroring pool_extra_roots.

The canonical use is unavoidable for SeaORM consumers: gen_seaorm emits a Relation enum per entity by convention, so any consumer with its own domain type named Relation would see ambiguous matches and abort. Exclude the directory you pointed gen_seaorm at:

pool_exclude_paths: vec!["src/persistence/db/entities/generated".into()],

Pipeline users get this populated automatically from their seaorm() stage; direct callers set it explicitly.


Used by install_admin_layer().

FieldTypeDescription
nuxt_configPathBufPath to the Nuxt app’s nuxt.config.ts.
layer_pathStringRelative path from the nuxt config to the admin layer package.
AdminLayerConfig {
nuxt_config: "../src-nuxt/nuxt.config.ts".into(),
layer_path: "../crates/ontogen/packages/nuxt_admin_layer".to_string(),
}