# @zenweb/result - AI API Reference

Zenweb 结果处理模块，统一控制器返回结果，默认 JSON 输出。

## Exports

### `fail()` - 抛出业务错误并终止执行

用于**预知的业务错误提示**（如参数校验失败、权限不足等），立即终止代码执行并返回错误响应。

> **注意:** `fail()` 仅用于可预见的业务错误。对于代码异常（如数据库连接失败、空指针等），应直接抛出 Error，由框架统一处理为意外错误。

```typescript
fail(): never                                    // 空错误
fail(message: string): never                     // 错误消息
fail(code: number, message?: string): never      // 错误代码 + 消息
fail(code: number, message?: string, data?: unknown): never  // 错误代码 + 消息 + 数据
fail(detail: ResultFailDetail): never            // 完整配置
```

**Example:**
```typescript
fail('错误消息');
fail(400, '参数错误');
fail(400, '参数错误', { field: 'email' });
fail({ code: 123, message: "自定义", status: 200, data: { id: 1 }, extra: { trace: 'abc' } });
```

### `ResultFail` extends `Error`

失败结果错误类，`ResultFailDetail` 的所有属性加上 `expose: boolean = true`（可暴露信息标识）。

**属性:** `code?`, `message?`, `status?`, `data?`, `extra?`

### `RenderManager` (Injectable singleton)

结果渲染管理器：
- `option: ResultOption` - 配置选项（只读）
- `renders: ResultRender[]` - 渲染器列表（后添加的优先匹配）
- `add(render: ResultRender)` - 添加渲染器
- `match(ctx: Context, data?: unknown)` - 匹配渲染器
- `output(ctx: Context, data?: unknown)` - 输出结果

> **注意:** `output()` 对 `Stream` 和 `Buffer` 类型直接设置到 `ctx.body`，不经过渲染器。

### `JSONRender` (Injectable singleton)

默认 JSON 渲染器，`enwrap=true`，`type='json'`，始终匹配。

### `ResultRender` 接口

```typescript
interface ResultRender {
  enwrap: boolean;                                    // 是否包装（调用 successWrap/failWrap）
  type?: string;                                       // 输出 Content-Type
  match(ctx: Context, data?: unknown): boolean | Promise<boolean>;
  render(ctx: Context, data?: unknown): unknown;      // 支持 async
}
```

### `ResultOption`

| 属性 | 默认值 | 说明 |
|-----|-------|------|
| `failCode` | - | 默认失败代码 |
| `failCodeHeader` | `true` (`'X-Fail-Code'`) | 失败代码输出到响应头 |
| `failMessage` | `'request fail'` | 默认失败消息 |
| `failStatus` | `422` | 默认失败 HTTP 状态码 |
| `renders` | - | 自定义渲染器类列表 |
| `successWrap(ctx, data?)` | `{ data }` | 成功结果包装 |
| `failWrap(ctx, err)` | `{ code, data, message, ...extra }` | 错误结果包装 |
| `exposeUnexpected` | 非生产环境或 `EXPOSE_UNEXPECTED=1` | 暴露意外错误详情 |
| `unexpectedStatus` | `500` | 意外错误 HTTP 状态码 |
| `unexpectedCode` | `500` | 意外错误代码 |

## Context Extensions

| 属性/方法 | 说明 |
|---------|------|
| `successData` | `ctx.success()` 的原始数据 |
| `successDataOutput` | 输出处理 Promise |
| `success(data?)` | 设置成功结果并输出 |
| `isFail` | 输出结果是否为 fail 类型 |

## Usage

```typescript
import { Core } from '@zenweb/core';
import modInject from '@zenweb/inject';
import modResult, { fail } from '@zenweb/result';

const app = new Core();
app.setup(modInject());
app.setup(modResult({
  failCode: 1,
  failStatus: 400,
  successWrap: (ctx, data) => ({ success: true, data }),
  failWrap: (ctx, err) => ({ success: false, error: { code: err.code, message: err.message } }),
}));

// 控制器中使用
@Controller()
class MyController {
  @Get('/user')
  getUser() {
    if (!valid) fail(400, '参数错误');
    return { name: 'test' };  // 自动包装为 { data: { name: 'test' } }
  }
}
```

## Custom Renderer

```typescript
import { Injectable } from '@zenweb/inject';
import { Context } from '@zenweb/core';  // Context 从 @zenweb/core 导入
import { ResultRender, RenderManager } from '@zenweb/result';

@Injectable('singleton')
class XMLRender implements ResultRender {
  enwrap = false;
  type = 'xml';
  match(ctx: Context) {
    return ctx.accepts('xml') === 'xml';
  }
  render(ctx: Context, data: unknown) {
    return toXML(data);
  }
}

// 注册
app.setup(modResult({ renders: [XMLRender] }));
```