> ## Documentation Index
> Fetch the complete documentation index at: https://yumebox.gal.tf/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript API

扩展名：`.js`。必须定义 `main(profile)` 并返回配置对象。列表追加用 `rules+`，不是 YAML 的 `rules-end`。

## `main(profile)`

<ParamField body="profile" type="object" required>
  当前配置对象，可直接读写。
</ParamField>

<ParamField body="return" type="object" required>
  返回配置对象，或 resolve 为对象的 Promise。

  返回 `undefined` / 数组 / 标量，或抛错时，当前脚本失败并保留执行前配置。
</ParamField>

```js main-sync.js icon="braces" lines theme={null}
function main(profile) {
  profile["log-level"] = "info";
  return profile;
}
```

```js main-async.js icon="braces" lines theme={null}
async function main(profile) {
  const response = await fetch("http://127.0.0.1:8080/patch.yaml");
  if (!response.ok) return profile;
  return deepMerge(profile, await response.yaml(), true);
}
```

## `deepMerge(target, patch, isOverride)`

<ParamField body="target" type="object" required>
  目标对象，就地修改并返回。
</ParamField>

<ParamField body="patch" type="object" required>
  合并内容。
</ParamField>

<ParamField body="isOverride" type="boolean" default="true">
  `true` 时启用 `+key` / `key+` / `key!` 修饰符。
</ParamField>

### patch 键名

<ParamField body="key" type="any">
  对象递归合并；数组替换；标量覆盖。
</ParamField>

<ParamField body="+key" type="array">
  插入列表开头（`isOverride = true`）。
</ParamField>

<ParamField body="key+" type="array">
  追加到列表末尾（`isOverride = true`）。
</ParamField>

<ParamField body="key!" type="object">
  整字段替换。
</ParamField>

<ParamField body="angle-wrapped key" type="any">
  字面量字段名。
</ParamField>

```js deep-merge.js icon="braces" lines theme={null}
function main(profile) {
  return deepMerge(
    profile,
    {
      "+rules": ["DOMAIN-SUFFIX,router.local,DIRECT"],
      "rules+": ["DOMAIN-SUFFIX,example.com,DIRECT"],
      "dns!": {
        enable: true,
        nameserver: ["1.1.1.1"],
      },
    },
    true
  );
}
```

## 内置 API

### `yaml`

<ParamField body="yaml.parse(text)" type="(text: string) => any">
  YAML 文本 -> 值。
</ParamField>

<ParamField body="yaml.stringify(value)" type="(value: any) => string">
  值 -> YAML 文本。
</ParamField>

### `fetch(input, init?)`

仅支持 `http://`。

<ParamField body="input" type="string | object" required>
  URL，或含 `url` 的对象。
</ParamField>

<ParamField body="init.method" type="string" default="GET">
  HTTP 方法。
</ParamField>

<ParamField body="init.headers" type="object">
  请求头。
</ParamField>

<ParamField body="init.body" type="string | object">
  请求体；非字符串会 `JSON.stringify`。
</ParamField>

<ResponseField name="ok" type="boolean">
  是否 2xx。
</ResponseField>

<ResponseField name="status" type="number">
  状态码。
</ResponseField>

<ResponseField name="statusText" type="string">
  状态文本。
</ResponseField>

<ResponseField name="url" type="string">
  请求 URL。
</ResponseField>

<ResponseField name="headers" type="object">
  含 `get` / `has` / `toJSON`。
</ResponseField>

<ResponseField name="text()" type="Promise<string>">
  正文文本。
</ResponseField>

<ResponseField name="json()" type="Promise<any>">
  JSON 解析。
</ResponseField>

<ResponseField name="yaml()" type="Promise<any>">
  YAML 解析。
</ResponseField>

```js fetch-rules.js icon="braces" lines theme={null}
async function main(profile) {
  const response = await fetch("http://127.0.0.1:8080/rules.yaml");
  if (!response.ok) return profile;
  return deepMerge(profile, await response.yaml(), true);
}
```

### `console`

写入脚本同目录、同主文件名的 `.log`。每次执行前重置。

<ParamField body="console.log / info / warn / error / debug" type="function">
  写对应级别日志。age 加密配置下日志内容会脱敏。
</ParamField>

### Base64

<ParamField body="b64e(value)" type="(value: string) => string">
  UTF-8 -> Base64。
</ParamField>

<ParamField body="b64d(value)" type="(value: string) => string">
  Base64 -> UTF-8。
</ParamField>

<ParamField body="Buffer.from(value, encoding?)" type="function">
  `utf-8` 编码或 `base64` 解码的兼容接口。
</ParamField>

## 示例

```js filter-proxies.js icon="braces" lines theme={null}
function main(profile) {
  const proxies = Array.isArray(profile.proxies) ? profile.proxies : [];
  profile.proxies = proxies.filter((item) => {
    const name = String((item && item.name) || "");
    return !name.includes("过期");
  });
  return profile;
}
```
