AshTypescript.Codegen.SchemaFormatter behaviour (ash_typescript v0.17.2)

Copy Markdown View Source

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.

Fallback for custom Ash types that carry a typescript_type_name

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 field is non-nullable — callers 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.

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 an inner schema string in an array type.

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

aggregate_types()

@callback aggregate_types() :: %{required(atom()) => String.t()}

Map of aggregate kind atoms to their schema strings (e.g. %{count: "z.number().int()"}).

any_schema()

@callback any_schema() :: String.t()

Fallback schema for unknown/any type

atom_primitives()

@callback atom_primitives() :: %{required(atom()) => String.t()}

Map of atom-symbol primitives (e.g. :map) to schema strings.

configured_import_path()

@callback configured_import_path() :: String.t()

The import path for the validation library from application config.

custom_type_fallback()

@callback custom_type_fallback() :: String.t()

Fallback for custom Ash types that carry a typescript_type_name

format_enum(values)

@callback format_enum(values :: String.t()) :: String.t()

Enum schema from a comma-joined string of quoted values.

format_float(constraints)

@callback format_float(constraints :: keyword()) :: String.t()

Build a float schema, applying min/max/gt/lt constraints if present.

format_integer(constraints)

@callback format_integer(constraints :: keyword()) :: String.t()

Build an integer schema, applying min/max constraints if present.

format_string(constraints, require_non_empty)

@callback format_string(constraints :: keyword(), require_non_empty :: boolean()) ::
  String.t()

Build a string schema, applying constraints if present. require_non_empty is true when the field is non-nullable — callers should enforce a minimum length of 1 when no explicit :min_length constraint exists.

generate_schemas_enabled?()

@callback generate_schemas_enabled?() :: boolean()

Whether schema generation is enabled in the current project config.

import_statement(import_path)

@callback import_statement(import_path :: String.t()) :: String.t()

The TypeScript import statement for the validation library (e.g. import { z } from "zod").

library_name()

@callback library_name() :: String.t()

Human-readable library name for comments and error messages (e.g. "Zod" or "Valibot").

library_prefix()

@callback library_prefix() :: String.t()

The library namespace prefix used when building schema declarations ("z" or "v").

ltree_array()

@callback ltree_array() :: String.t()

Ltree type represented as an array of strings.

ltree_union()

@callback ltree_union() :: String.t()

Ltree type represented as a string-or-array-of-strings union.

null_schema()

@callback null_schema() :: String.t()

Schema for nil / null type

schema_suffix()

@callback schema_suffix() :: String.t()

The schema variable name suffix (e.g. "Schema" or "ValibotSchema").

section_header()

@callback section_header() :: String.t()

Human-readable label for the resource schemas section comment header.

simple_primitives()

@callback simple_primitives() :: %{required(module()) => String.t()}

Map of simple Ash type modules to schema strings — no constraint handling needed.

third_party_types()

@callback third_party_types() :: %{required(module()) => String.t()}

Map of third-party Ash type modules (e.g. AshMoney.Types.Money) to schema strings.

wrap_array(inner)

@callback wrap_array(inner :: String.t()) :: String.t()

Wrap an inner schema string in an array type.

wrap_nullable(schema)

@callback wrap_nullable(schema :: String.t()) :: String.t()

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_object(fields)

@callback wrap_object(fields :: String.t()) :: String.t()

Wrap a comma-joined set of key: schema fields in an inline object schema.

wrap_optional(schema)

@callback wrap_optional(schema :: String.t()) :: String.t()

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.

wrap_record()

@callback wrap_record() :: String.t()

Record/map schema — string keys, any values.

wrap_union(schemas)

@callback wrap_union(schemas :: String.t()) :: String.t()

Wrap a comma-joined set of schema strings in a union type.