# `Selecto.Config.OverlayDSL`

A DSL (Domain-Specific Language) for defining overlay configurations.

This module provides a clean, declarative syntax for customizing Selecto domains
through overlay files. Instead of manually constructing maps, you can use
macros like `defcolumn`, `deffilter`, `deffunction`, `defdetail_action`, `defcte`,
`defvalues`, `defsubquery`, `defjoin`, `defschema`, `defschema_assoc`,
`defsource_assoc`, `defsource_relationship`, `defchoice_source`,
`defwrite_operation`, `defwrite_field`, `defwrite_relationship`,
`defwrite_transition`, `defwrite_validation`, `defwrite_constraint`,
`defwrite_tenant_scope`, `defwrite_hook`, `defaction`, and
`defcapability` along with module attributes.

## Usage

    defmodule MyApp.SelectoDomains.Overlays.ProductDomainOverlay do
      use Selecto.Config.OverlayDSL,
        # Selecto.Extensions.PostGIS is provided by the :selecto_postgis package
        extensions: [Selecto.Extensions.PostGIS]

      # Module attributes for common configurations
      @redactions [:internal_notes, :cost_price]

      # Column customizations
      defcolumn :price do
        label "Product Price"
        format :currency
        aggregate_functions [:sum, :avg, :min, :max]
      end

      defcolumn :description do
        label "Product Description"
        max_length 100
      end

      # Custom filters
      deffilter "price_range" do
        name "Price Range"
        type :string
        description "Filter products by price range (e.g., '10-100')"
      end

      deffilter "in_stock" do
        name "In Stock"
        type :boolean
        description "Show only items currently in stock"
      end

      # Named query members
      defcte :active_products do
        query &__MODULE__.active_products_cte/1
        columns ["id", "name"]
        join [owner_key: :id, related_key: :id, fields: :infer]
      end

      defvalues :status_lookup do
        rows [["active", "Active"], ["archived", "Archived"]]
        columns ["status", "label"]
        as "status_lookup"
        join [owner_key: :status, related_key: :status]
      end

      defsubquery :high_value_orders do
        query &__MODULE__.high_value_orders_subquery/1
        type :inner
        on [%{left: "id", right: "customer_id"}]
      end
    end

## Available Directives

### Module Attributes

- `@redactions` - List of field atoms to redact from queries

### JSONB Schema (with `defjsonb_schema`)

Define structured schemas for JSONB columns to enable typed access, filtering, and display:

    defjsonb_schema :attributes do
      field :color, :string, label: "Color"
      field :weight, :decimal, label: "Weight (kg)", precision: 2
      field :organic, :boolean, label: "Organic"
      field :certifications, {:array, :string}, label: "Certifications"
      field :dimensions, :object do
        field :width, :decimal, label: "Width"
        field :height, :decimal, label: "Height"
      end
    end

Supported types: `:string`, `:integer`, `:decimal`, `:boolean`, `:date`, `:datetime`,
`{:array, type}`, `:object` (with nested fields)

### Column Directives (within `defcolumn`)

- `label/1` - Human-readable column label
- `format/1` - Display format (`:currency`, `:percentage`, `:number`, `:date`, etc.)
- `aggregate_functions/1` - List of allowed aggregations (`:sum`, `:avg`, `:count`, `:min`, `:max`)
- `precision/1` - Numeric precision for decimal types
- `max_length/1` - Maximum string length for display
- `sortable/1` - Whether column can be sorted (boolean)
- `filterable/1` - Whether column can be filtered (boolean)

### Filter Directives (within `deffilter`)

- `name/1` - Human-readable filter name
- `type/1` - Filter type (`:string`, `:integer`, `:boolean`, `:date`, etc.)
- `description/1` - Help text for the filter
- `required/1` - Whether filter is required (boolean)
- `default/1` - Default value for the filter
- `options/1` - List of valid options for select-type filters

### Detail Action Macros

- `defdetail_action id do ... end` - Define a detail-row action under `detail_actions`
- `defpopup id do ... end` - Define a modal detail-row action under `detail_actions`

### Query Member Macros

- `defcte id do ... end` - Define a named CTE preset under `query_members.ctes`
- `defvalues id do ... end` - Define a named VALUES preset under `query_members.values`
- `defsubquery id do ... end` - Define a named subquery-join preset under `query_members.subqueries`
- `deflateral id do ... end` - Define a named LATERAL preset under `query_members.laterals`
- `defunnest id do ... end` - Define a named UNNEST preset under `query_members.unnests`

### Function Macros

- `deffunction id do ... end` - Define a named UDF spec under `functions`

### Domain Registry Macros

- `defjoin id, config` - Define a top-level join entry under `joins`
- `defschema id, config` - Define a top-level schema entry under `schemas`
- `defschema_assoc schema_id, assoc_id, config` - Define a schema `associations` entry
- `defsource_assoc id, config` - Define a root `source.associations` entry
- `defsource_relationship id, config` - Define a top-level `source_relationships` entry
- `defchoice_source id, config` - Define a top-level `choice_sources` entry
- `defwrite_operation id, config_or_block` - Define `writes.operations[id]`
- `defwrite_field id, config_or_block` - Define `writes.fields[id]`
- `defwrite_relationship id, config_or_block` - Define `writes.relationships[id]`
- `defwrite_transition field, graph` - Define `writes.transitions[field]`
- `defwrite_validation rule` - Append a `writes.validations` rule
- `defwrite_constraint rule` - Append a `writes.constraints` rule
- `defwrite_tenant_scope config_or_block` - Define `writes.scope.tenant`
- `defwrite_hook id, refs` - Define `writes.hooks[id]`
- `defaction id, config_or_block` - Define a top-level domain action
- `defcapability id, config_or_block` - Define a top-level capability

### Query Member Directives

- `query/1` - Query builder (`fn -> selecto end`, `fn selecto -> selecto end`, or capture)
- `columns/1` - Declared columns for CTE/VALUES presets
- `join/1` - Auto-join options used by named member helpers
- `rows/1` - VALUES data rows (alias: `data/1`)
- `source/1` / `lateral_source/1` - LATERAL source tuple/query
- `array_field/1` - UNNEST source field/expression
- `join_type/1` - LATERAL join type (`:left`, `:inner`, etc.)
- `ordinality/1` - UNNEST ordinality alias
- `as/1` - VALUES alias name
- `join_id/1` - Subquery join id override
- `on/1` - Subquery join conditions
- `base_query/1`, `recursive_query/1` - Recursive CTE query functions
- `dependencies/1` - CTE dependencies
- `kind/1` - Subquery preset kind (currently `:join`)

## Examples

### Basic Column Customization

    defcolumn :price do
      label "Product Price"
      format :currency
      precision 2
      aggregate_functions [:sum, :avg]
    end

### Complex Filter

    deffilter "status" do
      name "Order Status"
      type :string
      description "Filter by order status"
      options ["pending", "shipped", "delivered", "cancelled"]
      default "pending"
    end

### Using Redactions

    @redactions [:password, :secret_key, :internal_notes]

### Computed Properties

    defcolumn :total_value do
      label "Total Value"
      format :currency
      aggregate_functions [:sum]
      computed true
    end

# `action`
*macro* 

Sets an action reference for a capability.

# `aggregate_functions`
*macro* 

Sets the allowed aggregate functions for a column.

Options: `:sum`, `:avg`, `:count`, `:min`, `:max`

# `allowed_in`
*macro* 

Sets the allowed call sites for a UDF registration.

# `allowed_ops`
*macro* 

Sets allowed write operations for a relationship.

# `arg`
*macro* 

Adds an argument definition to a UDF registration.

# `array_field`
*macro* 

Sets the UNNEST array field/expression for named UNNEST members.

# `as`
*macro* 

Sets the SQL alias/table name for named VALUES members.

# `base_query`
*macro* 

Sets recursive CTE base query function.

# `bulk`
*macro* 

Sets whether a write operation is bulk-capable.

# `capability`
*macro* 

Sets the capability required by a query-facing overlay entry.

# `cardinality`
*macro* 

Sets relationship cardinality.

# `cascade_delete`
*macro* 

Sets cascade delete behavior for a write relationship.

# `cascade_update`
*macro* 

Sets cascade update behavior for a write relationship.

# `choice_source`
*macro* 

Binds a field to a declared choice source.

# `columns`
*macro* 

Sets declared columns for CTE/VALUES query members.

# `computed`
*macro* 

Marks a column as computed (not from database).

# `conflict_targets`
*macro* 

Sets write operation conflict targets.

# `data`
*macro* 

Alias for `rows/1` in named VALUES members.

# `defaction`
*macro* 

Defines a domain action under the top-level `actions` registry.

# `default`
*macro* 

Sets the default value for the filter.

# `default_provider`
*macro* 

Sets a default provider for a write field.

# `defcapability`
*macro* 

Defines a capability under the top-level `capabilities` registry.

# `defchoice_source`
*macro* 

Defines a choice source under the top-level `choice_sources` registry.

## Example

    defchoice_source(:assignees, %{
      domain: :employee,
      value_field: :id,
      label_field: :full_name,
      source_relationship: :assignee
    })

# `defcolumn`
*macro* 

Defines a column customization.

## Example

    defcolumn :price do
      label "Product Price"
      format :currency
      aggregate_functions [:sum, :avg]
    end

# `defcte`
*macro* 

Defines a named CTE preset for `Selecto.with_cte/2`.

## Example

    defcte :active_customers do
      query &__MODULE__.active_customers_cte/1
      columns ["id", "name"]
      join [owner_key: :id, related_key: :id, fields: :infer]
    end

# `defdetail_action`
*macro* 

Defines a detail-row action.

## Example

    defdetail_action :customer_profile do
      name("Customer Profile")
      description("Open the customer profile in a new tab")
      type(:external_link)
      required_fields([:customer_id])
      payload(%{url_template: "https://app.example.test/customers/{{customer_id}}"})
    end

# `deffilter`
*macro* 

Defines a custom filter.

## Example

    deffilter "price_range" do
      name "Price Range"
      type :string
      description "Filter by price range"
    end

# `deffunction`
*macro* 

Defines a named UDF specification.

# `defjoin`
*macro* 

Defines a join configuration under the top-level `joins` registry.

## Example

    defjoin :initiative, %{
      type: :left,
      schema: MyApp.Initiative,
      owner_key: :initiative_id,
      related_key: :id
    }

# `defjsonb_schema`
*macro* 

Defines a JSONB schema for a JSONB column, enabling typed field access,
filtering, and display of structured JSON data.

## Example

    defjsonb_schema :attributes do
      field :color, :string, label: "Color"
      field :weight, :decimal, label: "Weight (kg)", precision: 2
      field :organic, :boolean, label: "Organic"
      field :origin_country, :string, label: "Country of Origin"
      field :certifications, {:array, :string}, label: "Certifications"
      field :dimensions, :object do
        field :width, :decimal, label: "Width"
        field :height, :decimal, label: "Height"
        field :depth, :decimal, label: "Depth"
      end
    end

## Supported Field Types

- `:string` - Text values
- `:integer` - Whole numbers
- `:decimal` - Decimal numbers (supports `precision` option)
- `:boolean` - True/false values
- `:date` - Date values (ISO 8601 format)
- `:datetime` - DateTime values (ISO 8601 format)
- `{:array, type}` - Arrays of the specified type
- `:object` - Nested object (use nested `field` calls)

## Field Options

- `label` - Human-readable label for display
- `precision` - Decimal precision (for `:decimal` type)
- `required` - Whether the field is required (default: false)
- `default` - Default value for the field
- `filterable` - Whether the field can be filtered (default: true)
- `sortable` - Whether the field can be sorted (default: true)
- `format` - Display format (`:currency`, `:percentage`, etc.)

# `deflateral`
*macro* 

Defines a named LATERAL preset for `Selecto.with_lateral/2`.

## Example

    deflateral :tag_expansion do
      source {:unnest, ""selecto_root"."tags""}
      as "tag_expansion"
      join_type :inner
    end

# `defpopup`
*macro* 

Defines a modal detail-row action.

This is a convenience wrapper around `defdetail_action` that defaults `type`
to `:modal`.

# `defschema`
*macro* 

Defines a schema configuration under the top-level `schemas` registry.

## Example

    defschema :initiative, %{
      source_table: "initiatives",
      columns: %{id: %{type: :integer}, name: %{type: :string}}
    }

# `defschema_assoc`
*macro* 

Defines an association under a schema entry in `schemas`.

## Example

    defschema_assoc(:bundle_parent_load, :split_parent_load, %{
      queryable: :split_parent_load,
      field: :split_parent_load,
      owner_key: :split_parent_id,
      related_key: :id
    })

# `defsource_assoc`
*macro* 

Defines a root source association under `source.associations`.

## Example

    defsource_assoc(:bundle_parent_load, %{
      queryable: :bundle_parent_load,
      field: :bundle_parent_load,
      owner_key: :bundle_parent_id,
      related_key: :id
    })

# `defsource_relationship`
*macro* 

Defines a source relationship under the top-level `source_relationships` registry.

## Example

    defsource_relationship(:assignee, %{
      target_domain: :employee,
      source_field: :assignee_id,
      target_field: :id,
      source_path: "assignee"
    })

# `defsubquery`
*macro* 

Defines a named subquery preset for `Selecto.with_subquery/2`.

## Example

    defsubquery :high_value_orders do
      query &__MODULE__.high_value_orders_subquery/1
      type :inner
      on [%{left: "id", right: "customer_id"}]
    end

# `defunnest`
*macro* 

Defines a named UNNEST preset for `Selecto.with_unnest/2`.

## Example

    defunnest :product_tags do
      array_field "tags"
      as "tag"
      ordinality "tag_position"
    end

# `defvalues`
*macro* 

Defines a named VALUES preset for `Selecto.with_values/2`.

## Example

    defvalues :status_lookup do
      rows [["active", "Active"], ["inactive", "Inactive"]]
      columns ["status", "label"]
      as "status_lookup"
      join [owner_key: :status, related_key: :status]
    end

# `defwrite_constraint`
*macro* 

Appends a write constraint rule under `writes.constraints`.

# `defwrite_field`
*macro* 

Defines a write field under `writes.fields`.

# `defwrite_hook`
*macro* 

Defines declared host-runtime hook references under `writes.hooks`.

Hook references remain host-owned code. Domain inspection and exported
contracts expose only safe metadata for these references.

# `defwrite_operation`
*macro* 

Defines a write operation under `writes.operations`.

## Examples

    defwrite_operation :insert do
      enabled true
      returning :record
    end

    defwrite_operation :delete, %{enabled: true, require_filter: true}

# `defwrite_relationship`
*macro* 

Defines a write relationship under `writes.relationships`.

# `defwrite_tenant_scope`
*macro* 

Defines canonical tenant scope metadata under `writes.scope.tenant`.

## Examples

    defwrite_tenant_scope do
      required(true)
      field(:tenant_id)
      satisfied_by([:trusted_context, :prefix])
    end

    defwrite_tenant_scope %{required: true, field: :tenant_id}

# `defwrite_transition`
*macro* 

Defines a transition graph under `writes.transitions`.

# `defwrite_validation`
*macro* 

Appends a write validation rule under `writes.validations`.

# `dependencies`
*macro* 

Sets CTE dependency names.

# `description`
*macro* 

Sets the filter description/help text.

# `enabled`
*macro* 

Sets whether a write operation, field, or relationship is enabled.

# `execution`
*macro* 

Sets execution metadata for a domain action.

# `field`
*macro* 

Defines a field within a JSONB schema.

This macro is only valid inside a `defjsonb_schema` block.

## Examples

    # Simple field with type and label
    field :color, :string, label: "Color"

    # Field with multiple options
    field :weight, :decimal, label: "Weight", precision: 2, required: true

    # Array field
    field :tags, {:array, :string}, label: "Tags"

    # Nested object field
    field :dimensions, :object do
      field :width, :decimal
      field :height, :decimal
    end

# `filterable`
*macro* 

Sets whether the column is filterable.

# `forbidden_on`
*macro* 

Sets write operations that forbid the field.

# `foreign_key`
*macro* 

Sets the foreign key for a write relationship.

# `format`
*macro* 

Sets the display format for a column.

Common formats: `:currency`, `:percentage`, `:number`, `:date`, `:datetime`, `:yes_no`

# `immutable`
*macro* 

Marks a write field immutable.

# `insertable`
*macro* 

Sets whether a write field is insertable.

# `join`
*macro* 

Sets join options for query member auto-join behavior.

# `join_id`
*macro* 

Sets explicit join id for named subquery members.

# `join_type`
*macro* 

Sets the join type for named LATERAL members.

# `kind`
*macro* 

Sets named subquery kind (`:join` currently supported).

# `label`
*macro* 

Sets the human-readable label for a column or filter.

# `lateral_source`
*macro* 

Alias for `source/1` in named LATERAL members.

# `max_items`
*macro* 

Sets maximum item count for a write relationship.

# `max_length`
*macro* 

Sets the maximum display length for string columns.

# `min_items`
*macro* 

Sets minimum item count for a write relationship.

# `name`
*macro* 

Sets the human-readable name for a filter.

# `on`
*macro* 

Sets ON conditions for named subquery members.

# `operations`
*macro* 

Sets operations for a capability.

# `options`
*macro* 

Sets the valid options for a select-type filter.

# `ordinality`
*macro* 

Sets ordinality alias for named UNNEST members.

# `orphan_strategy`
*macro* 

Sets orphan strategy metadata for a write relationship.

# `ownership`
*macro* 

Sets ownership metadata for a relationship.

# `payload`
*macro* 

Sets a payload map for a detail-row action.

# `precision`
*macro* 

Sets the numeric precision for decimal columns.

# `query`
*macro* 

Sets a query builder function for named query members.

# `recursive_query`
*macro* 

Sets recursive CTE recursive query function.

# `reference`
*macro* 

Sets rich reference metadata for a field, including choice-source caption/value paths.

# `require_filter`
*macro* 

Sets whether a write operation requires a filter.

# `required`
*macro* 

Sets whether the filter is required.

# `required_fields`
*macro* 

Sets required fields for a detail-row action.

# `required_on`
*macro* 

Sets write operations that require the field.

# `returning`
*macro* 

Sets write operation returning behavior.

# `returns`
*macro* 

Sets the declared return type or return metadata for a UDF registration.

# `rows`
*macro* 

Sets VALUES rows for a named `defvalues` member.

# `server_managed`
*macro* 

Marks a write field as server-managed.

# `sortable`
*macro* 

Sets whether the column is sortable.

# `source`
*macro* 

Sets a source expression for named LATERAL members.

# `sql_name`
*macro* 

Sets the SQL function name for a UDF registration.

# `transition`
*macro* 

Sets transition metadata for a domain action.

# `type`
*macro* 

Sets the filter type.

Common types: `:string`, `:integer`, `:boolean`, `:date`, `:datetime`, `:decimal`

# `unique_by`
*macro* 

Sets uniqueness fields for a write relationship.

# `updatable`
*macro* 

Sets whether a write field is updatable.

# `validators`
*macro* 

Sets validators for a write field or relationship.

# `writable`
*macro* 

Sets relationship writability.

# `write_once`
*macro* 

Marks a write field as write-once.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
