# definePlugin

后台入口调用 `definePlugin()`，把清单中声明的功能对应到 JavaScript 函数。清单中的 ID 和处理函数的键保持一致，例如动作 `hello` 对应 `actions.hello`。

飞块启动后台时，会通过 `FLYONDECK_SDK_PATH` 提供 SDK 路径。加载方式如下：

```js
const { definePlugin } = require(process.env.FLYONDECK_SDK_PATH);

definePlugin({
  actions: {
    hello(args) {
      return { message: `你好，${args.name}` };
    }
  }
});
```

## definePlugin()

```ts
definePlugin(definition: PluginDefinition): void
```

`definition` 是下面定义的处理函数集合。注册后，SDK 向飞块报告后台已就绪。就绪消息必须包含 `actions`、`services`、`actionOptions`、`widgetOptions`、`dataProviders`、`widgetActions` 六个功能注册列表，以及 configurationVersion 与 configurationMigrations；没有迁移实现时 configurationMigrations 为显式空数组。宿主拒绝缺失字段或与清单不匹配的消息。

## PluginDefinition

所有属性均可选；根据插件实际声明的功能填写。

| 属性 | 类型 | 对应的清单 |
| --- | --- | --- |
| `configurationMigrations` | `Record<number, ConfigurationMigrationHandler>` | 目标配置版本 N，对应 N-1 到 N 的迁移 |
| `actions` | `Record<string, ActionHandler>` | `contributes.actions[].id` |
| `services` | `Record<string, Record<string, ServiceHandler>>` | 服务 ID，再按方法名分组 |
| `actionOptions` | `Record<string, Record<string, ActionOptionsHandler>>` | 普通动作 ID，再按顶层参数名分组 |
| `widgetOptions` | `Record<string, Record<string, WidgetOptionsHandler>>` | 超级块 ID，再按配置字段名分组 |
| `dataProviders` | `Record<string, DataProviderHandler>` | `contributes.dataProviders[].id` |
| `widgetActions` | `Record<string, Record<string, WidgetActionHandler>>` | 超级块 ID，再按实例动作 ID 分组 |
| `onStart` | `(context) => void \| Promise<void>` | 后台启动时调用 |
| `onStop` | `(context) => void \| Promise<void>` | 后台停止时调用 |

例如，计数器的操作 `increment` 填在 `widgetActions.counter.increment`。完整实现见[创建超级块](../guides/widgets.md)。

## ConfigurationMigrationHandler

```ts
(request: Readonly<ConfigurationMigrationRequest>)
  => ConfigurationMigrationPatch | Promise<ConfigurationMigrationPatch>
```

在独立迁移进程中处理宿主管理的已保存配置，不接收 PluginContext。该进程不调用普通 onStart/onStop，也不提供宿主能力调用。每次只处理一个配置记录及一个相邻版本步骤；回调应只计算转换，不执行网络请求或其他外部副作用。

请求字段：kind 为 settings、widget、action 或 widgetAction；contributionId 是旧动作或超级块 ID，widgetAction 还携带 widgetId；fromVersion 与 toVersion 为相邻整数。values 只包含已保存的普通值或流程字面量，boundFields 列出宿主管理的绑定字段名（也包括数值控件的数值参数），不公开流程表达式或状态引用内容。

返回 patch：rename 将旧字段名映射到新字段名，保留绑定；remove 删除旧字段；set 显式写入 JSON 字面量；contributionId 和 widgetId 可改为同一插件的新标识。rename 与 remove 针对原字段，最后应用 set。不得修改宿主文件路径、设备身份或其他插件。不能把宿主管理的数值参数改成控件固定参数。

请求和响应各最多 128 KiB；set 值必须为有限 JSON，最大嵌套深度 20。宿主处理完整版本链后验证目标契约，所有记录成功才提交。处理器失败、超时、目标字段无效或并发编辑冲突时保留旧数据。详见[更新与迁移](../migrations/compatibility.md)。

## ActionHandler

```ts
(args: Record<string, unknown>, context: PluginContext)
  => unknown | Promise<unknown>
```

`args` 是已通过动作 Schema 校验的参数对象。返回值成为流程中的步骤结果；声明了 `outputSchema` 时，返回值也会经过该 Schema 校验。

## ServiceHandler

```ts
(args: Record<string, unknown>, context: PluginContext,
 request: { callerPluginId: string }) => unknown | Promise<unknown>
```

`args` 来自服务消费方。`request.callerPluginId` 是消费方的插件 ID，可用于区分调用方。参数和返回值都使用 JSON 数据，见[插件服务](./services.md)。

## ActionOptionsHandler

```ts
(request: Readonly<{ query: string; selectedValue?: string }>, context: PluginContext)
  => Array<{ label: string; value: string }>
   | Promise<Array<{ label: string; value: string }>>
```

在 `actionOptions[actionId][fieldKey]` 注册处理器，对应普通动作顶层字符串参数的 `dynamicOptions: true` 声明。声明与注册必须完全匹配，包括没有实现时的空列表；宿主不接受旧就绪消息格式。

`query` 最长 80 字符；`selectedValue` 为已保存的稳定 ID，最长 256 字符。搜索返回最多 50 项，每项 `{label,value}`：label 非空且最长 120 字符，value 非空、不重复且最长 256 字符，还须符合参数 Schema。插件应根据 selectedValue 回查名称，便于重新打开配置时展示已保存项。

查询仅用于配置，不执行动作、不自动启用插件。失败时抛出可供用户理解的错误；空数组表示没有匹配项。执行动作时仍接收保存的字符串，插件负责检查业务目标是否有效，宿主不会重新查询候选项或自动替换失效值。各候选项接口共用每插件最多 8 个并发请求的预算。

## WidgetOptionsHandler

```ts
(request: { query: string; selectedValue?: string },
 context: PluginContext)
  => Array<{ label: string; value: string }>
   | Promise<Array<{ label: string; value: string }>>
```

用户在配置表单中搜索时，飞块传入 `query`；已有选中项时，同时传入 `selectedValue`。返回候选项中的 `label` 用于显示，`value` 用于保存。

查询最长 80 个字符，每次返回最多 50 项。动态项的标签最长 120 个字符，值最长 256 个字符。配置声明见 [WidgetSetting.dynamicOptions](./settings.md#widgetsetting-dynamicoptions)。

## DataProviderHandler

```ts
(request: {
  input: Readonly<Record<string, unknown>>;
  settings: Readonly<Record<string, string | number | boolean>>;
  instance: { deviceId: string; screenId: string; blockId: string };
  widgetId: string;
}, context: PluginContext) => unknown | Promise<unknown>
```

`input` 是页面提交的输入，`settings` 是这个实例的配置。`instance` 标识发起请求的块，`widgetId` 标识超级块类型。返回值交给页面的 `requestData()`，见[请求数据与调用服务](../guides/data-services.md)。

## WidgetActionHandler

```ts
(request: {
  deviceId: string;
  screenId: string;
  blockId: string;
  args: Readonly<Record<string, unknown>>;
}, context: PluginContext) => void | Promise<void>
```

前三个属性定位用户操作的实例，`args` 是页面提交的操作参数。操作完成后，可以调用 `context.states.publish()` 更新这个实例的状态。页面的 `invokeAction()` 不接收返回值。

## PluginContext

每个处理函数都能使用 `context`。它按用途分成命名空间，方法说明和参数集中在对应参考页。

| 命名空间 | 常用方法 |
| --- | --- |
| [settings](./plugin-settings.md) | `get()`、`onChange()` |
| [system](./system.md) | `openExternal()`、`sendText()` |
| [native](./native.md) | `execute()` |
| [network](./network.md) | `getJson()` |
| [deck](./deck.md) | `navigate()` |
| [states](./states-api.md) | `states.publish()` |
| [widgets](./widgets-api.md) | `instances()` |
| [events](./events.md) | `emit()` |
| [plugins](./plugins.md) | `call()` |
