Behaviour defining the output-format interface for schema generators.
Implement this behaviour to add a new validation library target (e.g. Zod, Valibot).
AshTypescript.Codegen.SchemaCore handles all resource introspection, topological
sorting, and structural generation; implementations only provide the output syntax.
Implementing a new formatter
defmodule MyLib.SchemaFormatter do
@behaviour AshTypescript.Codegen.SchemaFormatter
def null_schema, do: "ml.null()"
def any_schema, do: "ml.any()"
# ...
end
Summary
Callbacks
Map of aggregate kind atoms to their schema strings (e.g. %{count: "z.number().int()"}).
Fallback schema for unknown/any type
Map of atom-symbol primitives (e.g. :map) to schema strings.
The import path for the validation library from application config.
Extra imports to inject into this formatter's generated schema file, so a
mapping_overrides/0 entry can reference a schema the user authored in
TypeScript instead of an inline library expression.
Build an array schema, applying outer :min_length and :max_length constraints.
Enum schema from a comma-joined string of quoted values.
Build a float schema, applying min/max/gt/lt constraints if present.
Build an integer schema, applying min/max constraints if present.
Build a string schema, applying constraints if present.
require_non_empty is true when the string's constraints carry
allow_empty?: false (the Ash default) — implementations should enforce a
minimum length of 1 when no explicit :min_length constraint exists.
Whether schema generation is enabled in the current project config.
The TypeScript import statement for the validation library (e.g. import { z } from "zod").
Human-readable library name for comments and error messages (e.g. "Zod" or "Valibot").
The library namespace prefix used when building schema declarations ("z" or "v").
Ltree type represented as an array of strings.
Ltree type represented as a string-or-array-of-strings union.
Per-library schema overrides from application config, for hand-rolled custom types whose generated schema needs to be controlled by hand.
Schema for nil / null type
The schema variable name suffix (e.g. "Schema" or "ValibotSchema").
Human-readable label for the resource schemas section comment header.
Map of simple Ash type modules to schema strings — no constraint handling needed.
Map of third-party Ash type modules (e.g. AshMoney.Types.Money) to schema strings.
Wrap a schema string as nullable — i.e. the field's value may be null.
In zod this is .nullable(); in valibot, v.nullable(...).
Wrap a comma-joined set of key: schema fields in an inline object schema.
Wrap a schema string as omittable — i.e. the field may be absent from the
input object. In zod this is .optional(); in valibot, v.optional(...).
Both libraries' optional accepts undefined only — not null. To accept
null, compose with wrap_nullable/1.
Record/map schema — string keys, any values.
Wrap a comma-joined set of schema strings in a union type.
Callbacks
Map of aggregate kind atoms to their schema strings (e.g. %{count: "z.number().int()"}).
@callback any_schema() :: String.t()
Fallback schema for unknown/any type
Map of atom-symbol primitives (e.g. :map) to schema strings.
@callback configured_import_path() :: String.t()
The import path for the validation library from application config.
@callback custom_imports() :: [map()]
Extra imports to inject into this formatter's generated schema file, so a
mapping_overrides/0 entry can reference a schema the user authored in
TypeScript instead of an inline library expression.
Returns a list of %{import_name: String.t(), file: String.t()} maps — the
same shape as import_into_generated, but scoped per library.
Build an array schema, applying outer :min_length and :max_length constraints.
Enum schema from a comma-joined string of quoted values.
Build a float schema, applying min/max/gt/lt constraints if present.
Build an integer schema, applying min/max constraints if present.
Build a string schema, applying constraints if present.
require_non_empty is true when the string's constraints carry
allow_empty?: false (the Ash default) — implementations should enforce a
minimum length of 1 when no explicit :min_length constraint exists.
@callback generate_schemas_enabled?() :: boolean()
Whether schema generation is enabled in the current project config.
The TypeScript import statement for the validation library (e.g. import { z } from "zod").
@callback library_name() :: String.t()
Human-readable library name for comments and error messages (e.g. "Zod" or "Valibot").
@callback library_prefix() :: String.t()
The library namespace prefix used when building schema declarations ("z" or "v").
@callback ltree_array() :: String.t()
Ltree type represented as an array of strings.
@callback ltree_union() :: String.t()
Ltree type represented as a string-or-array-of-strings union.
Per-library schema overrides from application config, for hand-rolled custom types whose generated schema needs to be controlled by hand.
Returns a keyword list of {type_module, schema_string} tuples. Consulted by
AshTypescript.Codegen.SchemaCore ahead of the in-tree accept-lists, mirroring
how type_mapping_overrides works on the TypeScript side.
@callback null_schema() :: String.t()
Schema for nil / null type
@callback schema_suffix() :: String.t()
The schema variable name suffix (e.g. "Schema" or "ValibotSchema").
@callback section_header() :: String.t()
Human-readable label for the resource schemas section comment header.
Map of simple Ash type modules to schema strings — no constraint handling needed.
Map of third-party Ash type modules (e.g. AshMoney.Types.Money) to schema strings.
Wrap a schema string as nullable — i.e. the field's value may be null.
In zod this is .nullable(); in valibot, v.nullable(...).
For fields that may be both omitted and null (the common case for nullable
Ash attributes — JSON.stringify drops undefined keys, so clearing a
nullable attribute requires sending "field": null), compose with
wrap_optional/1. Convention: apply wrap_nullable first (innermost), then
wrap_optional. The result is equivalent to zod's .nullish() shorthand.
Wrap a comma-joined set of key: schema fields in an inline object schema.
Wrap a schema string as omittable — i.e. the field may be absent from the
input object. In zod this is .optional(); in valibot, v.optional(...).
Both libraries' optional accepts undefined only — not null. To accept
null, compose with wrap_nullable/1.
@callback wrap_record() :: String.t()
Record/map schema — string keys, any values.
Wrap a comma-joined set of schema strings in a union type.