documentation/features/developer-experience.md

<!--
SPDX-FileCopyrightText: 2025 Torkild G. Kjevik
SPDX-FileCopyrightText: 2025 ash_typescript contributors <https://github.com/ash-project/ash_typescript/graphs/contributors>

SPDX-License-Identifier: MIT
-->

# Developer Experience Features

AshTypescript provides features to improve developer experience: namespace organization, JSDoc generation, and API manifest documentation.

## Namespaces

Namespaces organize RPC actions into logical groups, improving discoverability in large codebases.

### Configuration Levels

Namespaces can be configured at three levels with cascading precedence:

**Domain Level** - Default namespace for all resources in a domain:

```elixir
defmodule MyApp.Domain do
  use Ash.Domain, extensions: [AshTypescript.Rpc]

  typescript_rpc do
    namespace :api  # All resources default to "api" namespace

    resource MyApp.Todo do
      rpc_action :list_todos, :read
    end
  end
end
```

**Resource Level** - Override for all actions on a specific resource:

```elixir
typescript_rpc do
  namespace :api

  resource MyApp.Todo do
    namespace :todos  # Overrides domain namespace

    rpc_action :list_todos, :read
    rpc_action :create_todo, :create
  end

  resource MyApp.User do
    # Uses domain namespace "api"
    rpc_action :list_users, :read
  end
end
```

**Action Level** - Override for a specific action:

```elixir
typescript_rpc do
  namespace :api

  resource MyApp.Todo do
    namespace :todos

    rpc_action :list_todos, :read  # Uses "todos"
    rpc_action :admin_list, :read, namespace: :admin  # Uses "admin"
  end
end
```

### Precedence Order

Action namespace > Resource namespace > Domain namespace > nil

### Generated Output

With namespaces enabled, the generated JSDoc includes the namespace:

```typescript
/**
 * List all todos
 *
 * @ashActionType :read
 * @namespace todos
 */
export async function listTodos(...) { ... }
```

## JSDoc Generation

Generated TypeScript functions include JSDoc comments that provide IDE discoverability through hover documentation and autocomplete hints.

### Default Output

Every generated RPC function includes basic JSDoc:

```typescript
/**
 * List all todos
 *
 * @ashActionType :read
 */
export async function listTodos(...) { ... }
```

### Exposing Ash Metadata

Enable detailed Ash metadata in JSDoc for development:

```elixir
config :ash_typescript,
  add_ash_internals_to_jsdoc: true
```

This adds internal references:

```typescript
/**
 * List all todos
 *
 * @ashActionType :read
 * @ashResource MyApp.Todo
 * @ashAction :list_todos
 * @ashActionDef lib/my_app/resources/todo.ex
 * @rpcActionDef lib/my_app/domain.ex
 * @namespace todos
 */
export async function listTodos(...) { ... }
```

### JSDoc Tags Reference

| Tag | Description | When Shown |
|-----|-------------|------------|
| `@ashActionType` | Ash action type (`:read`, `:create`, etc.) | Always |
| `@ashResource` | Full Elixir module name | When `add_ash_internals_to_jsdoc: true` |
| `@ashAction` | Internal Ash action name | When `add_ash_internals_to_jsdoc: true` |
| `@ashActionDef` | Source file of Ash action definition | When `add_ash_internals_to_jsdoc: true` |
| `@rpcActionDef` | Source file of RPC action configuration | When `add_ash_internals_to_jsdoc: true` |
| `@namespace` | Action namespace | When namespace is configured |
| `@see` | Related actions | When `see:` option is configured |
| `@deprecated` | Deprecation notice | When `deprecated:` option is configured |

### Source Path Prefix (Monorepos)

For monorepo setups where Elixir code is in a subdirectory:

```elixir
config :ash_typescript,
  source_path_prefix: "backend"
```

Output:

```typescript
/**
 * @ashActionDef backend/lib/my_app/resources/todo.ex
 * @rpcActionDef backend/lib/my_app/domain.ex
 */
```

### Custom Descriptions

Override default descriptions per action:

```elixir
typescript_rpc do
  resource MyApp.Todo do
    rpc_action :list_todos, :read,
      description: "Fetch all todos for the current user"
  end
end
```

When `add_ash_internals_to_jsdoc: true`, the Ash action's description is used as fallback if no RPC description is set.

### Related Actions

Link related actions in JSDoc using the `see` option:

```elixir
rpc_action :list_todos, :read,
  see: [:create_todo, :update_todo]
```

Output:

```typescript
/**
 * @see createTodo
 * @see updateTodo
 */
```

### Deprecation Notices

Mark actions as deprecated:

```elixir
rpc_action :old_list, :read,
  deprecated: true

rpc_action :legacy_list, :read,
  deprecated: "Use listTodos instead"
```

Output:

```typescript
/**
 * @deprecated
 */

/**
 * @deprecated Use listTodos instead
 */
```

## Manifest Generation

Generate a Markdown manifest documenting all RPC actions for API documentation and developer onboarding.

### Configuration

```elixir
config :ash_typescript,
  manifest_file: "./docs/RPC_MANIFEST.md",
  add_ash_internals_to_manifest: true
```

### Sample Output

```markdown
# RPC Action Manifest

Generated: 2025-01-15

## Namespace: todos

### Todo

| Function | Action Type | Ash Action | Resource | Validation | Zod Schema | Channel |
|----------|-------------|------------|----------|------------|------------|---------|
| `listTodos` | read | `list` | `MyApp.Todo` | `validateListTodos` | `ListTodosInputSchema` | `listTodosChannel` |
| `createTodo` | create | `create` | `MyApp.Todo` | `validateCreateTodo` | `CreateTodoInputSchema` | `createTodoChannel` |

- **`listTodos`**: Fetch all todos for the current user
- **`createTodo`**: Create a new Todo | **See also:** `listTodos`

**Typed Queries:**
- `todoFields` -> `TodoFieldsResult`: Pre-defined field selection
```

### Grouping Behavior

- **With namespaces**: Actions grouped by namespace
- **Without namespaces**: Actions grouped by domain

### Controlling Manifest Content

The `add_ash_internals_to_manifest` config controls whether internal Ash details are shown:

| Setting | Columns Shown |
|---------|---------------|
| `false` | Function, Action Type |
| `true` | Function, Action Type, Ash Action, Resource |

## Configuration Reference

### All Developer Experience Options

```elixir
config :ash_typescript,
  # JSDoc configuration
  add_ash_internals_to_jsdoc: false,  # Show Ash module/action details
  source_path_prefix: nil,            # Prefix for source paths (monorepos)

  # Manifest configuration
  manifest_file: nil,                 # Path to generate manifest (nil = disabled)
  add_ash_internals_to_manifest: false  # Show Ash details in manifest
```

### Development vs Production

**Development Configuration:**

```elixir
# config/dev.exs
config :ash_typescript,
  add_ash_internals_to_jsdoc: true,
  add_ash_internals_to_manifest: true,
  manifest_file: "./docs/RPC_MANIFEST.md"
```

**Production Configuration:**

```elixir
# config/prod.exs
config :ash_typescript,
  add_ash_internals_to_jsdoc: false,
  add_ash_internals_to_manifest: false
```

## Common Patterns

### Namespace Organization by Feature

```elixir
typescript_rpc do
  resource MyApp.Todo do
    namespace :todos
    rpc_action :list_todos, :read
    rpc_action :create_todo, :create
  end

  resource MyApp.User do
    namespace :users
    rpc_action :list_users, :read
    rpc_action :get_current_user, :get_current, namespace: :auth
  end

  resource MyApp.Session do
    namespace :auth
    rpc_action :login, :create
    rpc_action :logout, :destroy
  end
end
```

### Development-Only Metadata

```elixir
# config/config.exs
config :ash_typescript,
  add_ash_internals_to_jsdoc: Mix.env() == :dev,
  add_ash_internals_to_manifest: Mix.env() == :dev
```

### Monorepo Setup

```elixir
# backend/config/config.exs
config :ash_typescript,
  source_path_prefix: "backend",
  output_file: "../frontend/src/generated/ash_rpc.ts"
```

## Next Steps

- [RPC Action Options](rpc-action-options.md) - All action configuration options
- [Configuration Reference](../reference/configuration.md) - Complete configuration options
- [Lifecycle Hooks](lifecycle-hooks.md) - HTTP and channel lifecycle hooks