# `GanttEx.Store`
[🔗](https://github.com/rocket4ce/gantt_ex/blob/v0.1.0/lib/gantt_ex/store.ex#L1)

An immutable, in-memory container of tasks and links.

The store keeps tasks as an ordered tree (siblings keep the order they were
given in), computes missing dates/durations, rolls up the dates of
`:project` tasks from their children and validates dependencies.

Every mutating function returns `{:ok, store, changes}`, where `changes` is
the list of everything that changed, including side effects such as the
dates of a parent project or the links removed with a task. `GanttEx.Live.Gantt`
turns each change into a `GanttEx.Event` for the parent LiveView.

## Examples

    iex> {:ok, store} =
    ...>   GanttEx.Store.new(
    ...>     [
    ...>       %{id: 1, text: "Website", type: :project},
    ...>       %{id: 2, text: "Design", parent: 1, start_date: ~D[2025-01-06], duration: 3},
    ...>       %{id: 3, text: "Build", parent: 1, start_date: ~D[2025-01-09], end_date: ~D[2025-01-16]}
    ...>     ],
    ...>     [%{id: 1, source: 2, target: 3}]
    ...>   )
    iex> project = GanttEx.Store.get_task(store, 1)
    iex> {project.start_date, project.end_date, project.duration}
    {~N[2025-01-06 00:00:00], ~N[2025-01-16 00:00:00], 10}
    iex> {:ok, store, changes} = GanttEx.Store.update_task(store, 3, %{duration: 9})
    iex> Enum.map(changes, fn {type, task, _details} -> {type, task.id} end)
    [task_updated: 3, task_updated: 1]
    iex> GanttEx.Store.get_task(store, 1).end_date
    ~N[2025-01-18 00:00:00]

# `change`

```elixir
@type change() ::
  {:task_created | :task_updated | :task_deleted | :task_moved,
   GanttEx.Task.t(), map()}
  | {:link_created | :link_updated | :link_deleted, GanttEx.Link.t(), map()}
```

A change produced by a mutation: `{type, entity, details}`.

  * `:task_created`, `:task_deleted`, `:link_created`, `:link_deleted` have empty details
  * `:task_updated` and `:link_updated` details contain `:changes`, a map of the
    changed fields and their new values
  * `:task_moved` details contain `:parent`, `:index` and `:siblings` (the
    ordered ids of the new siblings, including the moved task)

# `error`

```elixir
@type error() ::
  GanttEx.Task.error()
  | {:invalid_task, GanttEx.Task.id(), term()}
  | {:invalid_link, GanttEx.Task.id(), term()}
  | {:duplicate_id, :task | :link, GanttEx.Task.id()}
  | {:parent_cycle, GanttEx.Task.id()}
  | :not_found
  | :missing_parent
  | :invalid_parent
  | :self_link
  | :missing_task
  | :duplicate_link
  | :cycle
```

Why an operation was rejected.

# `key`

```elixir
@type key() :: String.t()
```

A string version of an id, as used in the DOM and in client events.

# `row`

```elixir
@type row() :: %{
  task: GanttEx.Task.t(),
  key: key(),
  index: non_neg_integer(),
  level: non_neg_integer(),
  parent_key: key() | nil,
  has_children: boolean(),
  open: boolean(),
  wbs: String.t(),
  uid: String.t(),
  group: boolean(),
  count: non_neg_integer() | nil
}
```

A visible row, as returned by `rows/2`.

# `t`

```elixir
@type t() :: %GanttEx.Store{
  auto_types: boolean(),
  calendar: GanttEx.Calendar.t() | nil,
  calendars: %{required(term()) =&gt; GanttEx.Calendar.t()},
  children: %{required(GanttEx.Task.id() | nil) =&gt; [GanttEx.Task.id()]},
  duration_unit: GanttEx.Dates.unit(),
  link_keys: %{required(key()) =&gt; GanttEx.Task.id()},
  link_order: [GanttEx.Task.id()],
  links: %{required(GanttEx.Task.id()) =&gt; GanttEx.Link.t()},
  task_keys: %{required(key()) =&gt; GanttEx.Task.id()},
  tasks: %{required(GanttEx.Task.id()) =&gt; GanttEx.Task.t()},
  work_time: boolean()
}
```

# `add_duration`

```elixir
@spec add_duration(t(), GanttEx.Task.t() | nil, NaiveDateTime.t(), number()) ::
  NaiveDateTime.t()
```

Adds `amount` duration units to `datetime` for `task`, honoring its calendar.

    iex> store = GanttEx.Store.new!([], [], work_time: true)
    iex> GanttEx.Store.add_duration(store, nil, ~N[2025-01-10 00:00:00], 1)
    ~N[2025-01-11 00:00:00]
    iex> GanttEx.Store.add_duration(store, nil, ~N[2025-01-10 00:00:00], 2)
    ~N[2025-01-14 00:00:00]

# `add_link`

```elixir
@spec add_link(t(), term()) :: {:ok, t(), [change()]} | {:error, error()}
```

Adds a link.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-01]}])
    iex> {:ok, store, [{:link_created, link, %{}}]} = GanttEx.Store.add_link(store, %{id: "l1", source: 1, target: 2})
    iex> link.type
    :finish_to_start
    iex> GanttEx.Store.add_link(store, %{id: "l2", source: 2, target: 1})
    {:error, :cycle}

# `add_task`

```elixir
@spec add_task(t(), term(), keyword()) :: {:ok, t(), [change()]} | {:error, error()}
```

Adds a task. The parent is taken from the task's `:parent` field.

## Options

  * `:index` - position among its siblings. Defaults to appending.

## Examples

    iex> store = GanttEx.Store.new!([%{id: 1, type: :project, start_date: ~D[2025-01-01]}])
    iex> {:ok, store, changes} = GanttEx.Store.add_task(store, %{id: 2, parent: 1, start_date: ~D[2025-01-02], duration: 4})
    iex> Enum.map(changes, fn {type, task, _} -> {type, task.id} end)
    [task_created: 2, task_updated: 1]
    iex> GanttEx.Store.add_task(store, %{id: 3, parent: 99, start_date: ~D[2025-01-01]})
    {:error, :missing_parent}

# `ancestor_ids`

```elixir
@spec ancestor_ids(t(), GanttEx.Task.id()) :: [GanttEx.Task.id()]
```

Returns the ids of the ancestors of `id`, closest first.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, parent: 1, start_date: ~D[2025-01-01]}, %{id: 3, parent: 2, start_date: ~D[2025-01-01]}])
    iex> GanttEx.Store.ancestor_ids(store, 3)
    [2, 1]

# `calendar_for`

```elixir
@spec calendar_for(t(), GanttEx.Task.t() | nil) :: GanttEx.Calendar.t() | nil
```

Returns the calendar that applies to `task` (its `:calendar_id`, or the
global calendar), or `nil` when the store does not use working time.

# `child_ids`

```elixir
@spec child_ids(t(), GanttEx.Task.id() | nil) :: [GanttEx.Task.id()]
```

Returns the ids of the direct children of `id` (`nil` for the root).

# `children`

```elixir
@spec children(t(), GanttEx.Task.id() | nil) :: [GanttEx.Task.t()]
```

Returns the direct children of `id` (`nil` for the root).

# `compact`

```elixir
@spec compact([change()]) :: [change()]
```

Merges repeated `:task_updated` changes of the same task, keeping the
position of the first one and the last value.

    iex> a = GanttEx.Task.new!(id: 1, text: "A")
    iex> b = %{a | text: "B"}
    iex> GanttEx.Store.compact([{:task_updated, a, %{changes: %{start_date: 1}}}, {:task_updated, b, %{changes: %{end_date: 2}}}])
    [{:task_updated, b, %{changes: %{start_date: 1, end_date: 2}}}]

# `date_range`

```elixir
@spec date_range(t()) :: {NaiveDateTime.t(), NaiveDateTime.t()} | nil
```

Returns `{earliest_start, latest_end}` across all tasks, or `nil` when empty.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-03], duration: 2}, %{id: 2, start_date: ~D[2025-01-01], duration: 1}])
    iex> GanttEx.Store.date_range(store)
    {~N[2025-01-01 00:00:00], ~N[2025-01-05 00:00:00]}

# `delete_link`

```elixir
@spec delete_link(t(), GanttEx.Task.id()) ::
  {:ok, t(), [change()]} | {:error, error()}
```

Deletes a link.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-01]}], [%{id: 1, source: 1, target: 2}])
    iex> {:ok, store, [{:link_deleted, _link, %{}}]} = GanttEx.Store.delete_link(store, 1)
    iex> GanttEx.Store.links(store)
    []

# `delete_task`

```elixir
@spec delete_task(t(), GanttEx.Task.id()) ::
  {:ok, t(), [change()]} | {:error, error()}
```

Deletes a task with all its descendants and every link attached to them.

Changes are reported links first, then tasks from the deepest descendant up
to the deleted task, then the updated parent projects.

    iex> store = GanttEx.Store.new!(
    ...>   [%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, parent: 1, start_date: ~D[2025-01-01]}, %{id: 3, start_date: ~D[2025-01-01]}],
    ...>   [%{id: 10, source: 2, target: 3}]
    ...> )
    iex> {:ok, store, changes} = GanttEx.Store.delete_task(store, 1)
    iex> Enum.map(changes, fn {type, entity, _} -> {type, entity.id} end)
    [link_deleted: 10, task_deleted: 2, task_deleted: 1]
    iex> GanttEx.Store.task_count(store)
    1

# `descendant_ids`

```elixir
@spec descendant_ids(t(), GanttEx.Task.id() | nil) :: [GanttEx.Task.id()]
```

Returns the ids of all descendants of `id`, depth first.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, parent: 1, start_date: ~D[2025-01-01]}, %{id: 3, parent: 2, start_date: ~D[2025-01-01]}])
    iex> GanttEx.Store.descendant_ids(store, 1)
    [2, 3]

# `diff`

```elixir
@spec diff(t(), t()) :: [change()]
```

Returns the changes that turn `old` into `new`, in an order that can be
applied to a database: deleted links, deleted tasks (deepest first),
created tasks (parents first), updated tasks, moved tasks, created links
and updated links.

`GanttEx.History` uses it to report what undo and redo change.

    iex> old = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-02]}], [%{id: 1, source: 1, target: 2}])
    iex> {:ok, new, _} = GanttEx.Store.delete_task(old, 2)
    iex> new |> GanttEx.Store.diff(old) |> Enum.map(fn {type, entity, _} -> {type, entity.id} end)
    [task_created: 2, link_created: 1]
    iex> old |> GanttEx.Store.diff(new) |> Enum.map(fn {type, entity, _} -> {type, entity.id} end)
    [link_deleted: 1, task_deleted: 2]

# `duration_between`

```elixir
@spec duration_between(
  t(),
  GanttEx.Task.t() | nil,
  NaiveDateTime.t(),
  NaiveDateTime.t()
) :: integer()
```

Returns the duration between two datetimes for `task`, honoring its calendar.

    iex> store = GanttEx.Store.new!([], [], work_time: true)
    iex> GanttEx.Store.duration_between(store, nil, ~N[2025-01-10 00:00:00], ~N[2025-01-14 00:00:00])
    2

# `duration_unit`

```elixir
@spec duration_unit(t()) :: GanttEx.Dates.unit()
```

Returns the duration unit of the store.

# `fetch_link`

```elixir
@spec fetch_link(t(), GanttEx.Task.id()) :: {:ok, GanttEx.Link.t()} | :error
```

Fetches a link by id.

# `fetch_task`

```elixir
@spec fetch_task(t(), GanttEx.Task.id()) :: {:ok, GanttEx.Task.t()} | :error
```

Fetches a task by id.

# `get_link`

```elixir
@spec get_link(t(), GanttEx.Task.id()) :: GanttEx.Link.t() | nil
```

Gets a link by id, or `nil`.

# `get_task`

```elixir
@spec get_task(t(), GanttEx.Task.id()) :: GanttEx.Task.t() | nil
```

Gets a task by id, or `nil`.

# `has_children?`

```elixir
@spec has_children?(t(), GanttEx.Task.id()) :: boolean()
```

Returns `true` if the task has children.

# `index`

```elixir
@spec index(t(), GanttEx.Task.id()) :: non_neg_integer() | nil
```

Returns the position of a task among its siblings.

# `key`

```elixir
@spec key(GanttEx.Task.id()) :: key()
```

Converts an id into its string key (as used in DOM ids and client events).

    iex> GanttEx.Store.key(42)
    "42"

    iex> GanttEx.Store.key("a-1")
    "a-1"

# `level`

```elixir
@spec level(t(), GanttEx.Task.id()) :: non_neg_integer()
```

Returns the depth of a task (`0` for root tasks).

# `link_by_key`

```elixir
@spec link_by_key(t(), key()) :: GanttEx.Link.t() | nil
```

Gets a link by its string key, or `nil`.

# `link_error`

```elixir
@spec link_error(t(), GanttEx.Link.t()) ::
  nil | :self_link | :missing_task | :duplicate_link | :cycle
```

Returns why a link cannot be added, or `nil` when it is valid.

A link is rejected when it connects a task to itself, references a missing
task, duplicates an existing dependency between the same tasks, or would
create a circular dependency.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-01]}], [%{id: 1, source: 1, target: 2}])
    iex> GanttEx.Store.link_error(store, GanttEx.Link.new!(id: 2, source: 2, target: 1))
    :cycle
    iex> GanttEx.Store.link_error(store, GanttEx.Link.new!(id: 2, source: 1, target: 1))
    :self_link
    iex> GanttEx.Store.link_error(store, GanttEx.Link.new!(id: 2, source: 1, target: 2))
    :duplicate_link
    iex> GanttEx.Store.link_error(store, GanttEx.Link.new!(id: 2, source: 1, target: 2, type: :start_to_start))
    nil

# `links`

```elixir
@spec links(t()) :: [GanttEx.Link.t()]
```

Returns all links in insertion order.

# `links_for`

```elixir
@spec links_for(t(), GanttEx.Task.id()) :: [GanttEx.Link.t()]
```

Returns the links where the task is the source or the target.

# `move_task`

```elixir
@spec move_task(
  t(),
  GanttEx.Task.id(),
  GanttEx.Task.id() | nil,
  non_neg_integer() | nil
) ::
  {:ok, t(), [change()]} | {:error, error()}
```

Moves a task under `parent` (`nil` for the root) at `index` among its new
siblings (`nil` appends). A task cannot be moved inside itself or one of its
descendants.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-01]}, %{id: 3, start_date: ~D[2025-01-01]}])
    iex> {:ok, store, [{:task_moved, _task, details}]} = GanttEx.Store.move_task(store, 3, nil, 0)
    iex> details
    %{parent: nil, index: 0, siblings: [3, 1, 2]}
    iex> {:ok, store, _} = GanttEx.Store.move_task(store, 2, 1, nil)
    iex> GanttEx.Store.move_task(store, 1, 2, nil)
    {:error, :invalid_parent}

# `new`

```elixir
@spec new([term()], [term()], keyword()) :: {:ok, t()} | {:error, error()}
```

Builds a store from lists of tasks and links (anything accepted by
`GanttEx.Task.new/1` and `GanttEx.Link.new/1`).

## Options

  * `:duration_unit` - unit of `:duration` and `:lag` values. Defaults to `:day`.
  * `:work_time` - count only working time in durations (see `GanttEx.Calendar`).
    Defaults to `false`.
  * `:calendar` - the global calendar (a `GanttEx.Calendar` or its options).
    Defaults to Monday to Friday, 08:00-12:00 and 13:00-17:00.
  * `:calendars` - other calendars, referenced by the `:calendar_id` of tasks.
  * `:auto_types` - tasks with children become projects and projects without
    children become tasks, also after every mutation. Defaults to `false`.

Dates are completed as follows: when both `:start_date` and `:end_date` are
given the duration is computed (the end date wins over a given duration);
with a start date and a duration the end date is computed; a lone start date
gets a duration of 1. Milestones always end when they start.

Tasks pointing to an unknown parent become root tasks and links pointing to
unknown tasks are ignored.

    iex> {:ok, store} = GanttEx.Store.new([%{id: "a", start_date: "2025-02-01 09:00"}], [], duration_unit: :hour)
    iex> GanttEx.Store.get_task(store, "a").end_date
    ~N[2025-02-01 10:00:00]

    iex> GanttEx.Store.new([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 1, start_date: ~D[2025-01-01]}])
    {:error, {:duplicate_id, :task, 1}}

    iex> GanttEx.Store.new([%{id: 1, text: "Undated"}])
    {:error, {:invalid_task, 1, {:missing, :start_date}}}

With working time, a 2-day task starting on Friday ends on Tuesday, and a
task starting on a weekend starts on Monday:

    iex> {:ok, store} = GanttEx.Store.new([%{id: 1, start_date: ~D[2025-01-10], duration: 2}, %{id: 2, start_date: ~D[2025-01-11], duration: 1}], [], work_time: true)
    iex> {GanttEx.Store.get_task(store, 1).end_date, GanttEx.Store.get_task(store, 2).start_date}
    {~N[2025-01-14 00:00:00], ~N[2025-01-13 00:00:00]}

# `new!`

```elixir
@spec new!([term()], [term()], keyword()) :: t()
```

Like `new/3` but raises `ArgumentError` when the data is invalid.

# `rows`

```elixir
@spec rows(t(), keyword()) :: [row()]
```

Returns the visible rows, in display order.

## Options

  * `:collapsed` - a `MapSet` of task ids whose children are hidden.
  * `:filter` - a 1-arity function; only matching tasks (and their ancestors,
    to keep the tree readable) are returned. Collapsed branches are expanded
    while filtering.
  * `:sort` - `{field, :asc | :desc}` to sort siblings by a task field or a
    `:meta` key. The tree structure is preserved.
  * `:group_by` - `{group_fun, label_fun}` to group tasks (projects with
    children excluded) under virtual group rows. `group_fun` returns the
    group of a task, a list of groups, or `nil`; `label_fun` returns the
    label of a group. Group rows have `group: true`, a virtual project task
    spanning its members and `count`; collapsed groups are identified by
    `{:group, value}` in `:collapsed`. Rows have a unique `uid`, as a task
    can appear in several groups.

## Examples

    iex> store = GanttEx.Store.new!([
    ...>   %{id: 1, text: "B", start_date: ~D[2025-01-01]},
    ...>   %{id: 2, text: "child", parent: 1, start_date: ~D[2025-01-01]},
    ...>   %{id: 3, text: "A", start_date: ~D[2025-01-01]}
    ...> ])
    iex> store |> GanttEx.Store.rows() |> Enum.map(&{&1.key, &1.level})
    [{"1", 0}, {"2", 1}, {"3", 0}]
    iex> store |> GanttEx.Store.rows(collapsed: MapSet.new([1])) |> Enum.map(& &1.key)
    ["1", "3"]
    iex> store |> GanttEx.Store.rows(sort: {:text, :asc}) |> Enum.map(& &1.task.text)
    ["A", "B", "child"]
    iex> store |> GanttEx.Store.rows(filter: &(&1.text == "child")) |> Enum.map(& &1.key)
    ["1", "2"]

# `schedule`

```elixir
@spec schedule(
  GanttEx.Task.t(),
  GanttEx.Dates.unit(),
  [atom()],
  GanttEx.Calendar.t() | nil
) ::
  {:ok, GanttEx.Task.t()} | {:error, GanttEx.Task.error()}
```

Completes the dates of a single task for the given duration unit.

    iex> task = GanttEx.Task.new!(id: 1, start_date: ~D[2025-01-01], duration: 2)
    iex> {:ok, task} = GanttEx.Store.schedule(task, :day)
    iex> task.end_date
    ~N[2025-01-03 00:00:00]

    iex> task = GanttEx.Task.new!(id: 1, start_date: ~D[2025-01-05], end_date: ~D[2025-01-01])
    iex> GanttEx.Store.schedule(task, :day)
    {:error, {:invalid, :end_date}}

# `task_by_key`

```elixir
@spec task_by_key(t(), key()) :: GanttEx.Task.t() | nil
```

Gets a task by its string key (see `key/1`), or `nil`.

    iex> store = GanttEx.Store.new!([%{id: 7, start_date: ~D[2025-01-01]}])
    iex> GanttEx.Store.task_by_key(store, "7").id
    7

# `task_count`

```elixir
@spec task_count(t()) :: non_neg_integer()
```

Returns the number of tasks.

# `tasks`

```elixir
@spec tasks(t()) :: [GanttEx.Task.t()]
```

Returns all tasks in tree order (depth first).

    iex> store = GanttEx.Store.new!([%{id: 2, parent: 1, start_date: ~D[2025-01-01]}, %{id: 1, start_date: ~D[2025-01-01]}, %{id: 3, start_date: ~D[2025-01-01]}])
    iex> store |> GanttEx.Store.tasks() |> Enum.map(& &1.id)
    [1, 2, 3]

# `to_data`

```elixir
@spec to_data(t()) :: %{data: [map()], links: [map()]}
```

Returns the data in the DHTMLX JSON shape: `%{data: [task_map], links: [link_map]}`.

    iex> store = GanttEx.Store.new!([%{id: 1, text: "A", start_date: ~D[2025-01-01]}])
    iex> %{data: [task], links: []} = GanttEx.Store.to_data(store)
    iex> Map.take(task, [:id, :text, :duration])
    %{id: 1, text: "A", duration: 1}

# `update_link`

```elixir
@spec update_link(t(), GanttEx.Task.id(), map() | keyword()) ::
  {:ok, t(), [change()]} | {:error, error()}
```

Updates the `:type`, `:lag`, `:readonly`, `:color` or meta fields of a link.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, start_date: ~D[2025-01-01]}], [%{id: 1, source: 1, target: 2}])
    iex> {:ok, _store, [{:link_updated, link, %{changes: changes}}]} = GanttEx.Store.update_link(store, 1, %{lag: 2})
    iex> {link.lag, changes}
    {2, %{lag: 2}}

# `update_task`

```elixir
@spec update_task(t(), GanttEx.Task.id(), map() | keyword()) ::
  {:ok, t(), [change()]} | {:error, error()}
```

Updates a task with the given attributes.

Dates are kept consistent: changing only the start moves the task (the
duration is kept), changing the end or the duration resizes it. Changing
`:parent` moves the task to the end of its new parent.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01], duration: 2}])
    iex> {:ok, store, [{:task_updated, task, %{changes: changes}}]} = GanttEx.Store.update_task(store, 1, %{start_date: ~D[2025-01-10]})
    iex> {task.end_date, changes}
    {~N[2025-01-12 00:00:00], %{start_date: ~N[2025-01-10 00:00:00], end_date: ~N[2025-01-12 00:00:00]}}
    iex> GanttEx.Store.update_task(store, 1, %{end_date: ~D[2024-01-01]})
    {:error, {:invalid, :end_date}}

# `wbs_codes`

```elixir
@spec wbs_codes(t()) :: %{required(GanttEx.Task.id()) =&gt; String.t()}
```

Returns the WBS code (outline number) of every task, such as `"1.2.3"`.

Codes follow the order of the tree, whatever the sorting or filtering of
the rows.

    iex> store = GanttEx.Store.new!([%{id: 1, start_date: ~D[2025-01-01]}, %{id: 2, parent: 1, start_date: ~D[2025-01-01]}, %{id: 3, parent: 1, start_date: ~D[2025-01-01]}, %{id: 4, start_date: ~D[2025-01-01]}])
    iex> GanttEx.Store.wbs_codes(store)
    %{1 => "1", 2 => "1.1", 3 => "1.2", 4 => "2"}

---

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