38 lines
2.6 KiB
TypeScript
38 lines
2.6 KiB
TypeScript
import type {NodeHandler} from '../../../types/bot/workflow/runtime.ts';
|
|
import type {WorkflowNodeTemplate} from '../../../types/bot/workflow/metadata.ts';
|
|
import {describe, failure_port, output, port} from '../metadata.ts';
|
|
import {assert_json} from '../runtime/value.ts';
|
|
|
|
export const config_template: WorkflowNodeTemplate = {
|
|
name: 'goto', version: '1.0.0',
|
|
i18n: describe('重试跳转', '按当前节点的重试计数限制跳转次数,重试目标由 retry 出口连线指定。'),
|
|
config: [
|
|
{name: 'max_retries', type: 'number', required: true, default: 2, integer: true, min: 0, max: 10_000,
|
|
i18n: describe('最大重试次数', '允许经过 retry 出口的次数;0 表示直接进入次数耗尽分支,默认允许重试 2 次。')},
|
|
{name: 'delay_ms', type: 'number', required: true, default: 0, integer: true, min: 0, max: 2_147_483_647,
|
|
i18n: describe('重试间隔', '每次重试跳转前等待的毫秒数,0 表示不增加等待时长。')},
|
|
],
|
|
output: output('json', '上一节点输出', '重试或次数耗尽时均透传上一节点的 output,缺失时为 null。'),
|
|
ports: [port('retry', '重试', '未达到最大重试次数时增加计数、等待指定间隔,再以 success 状态跳转;必须连接重试目标。'),
|
|
port('exhausted', '次数耗尽', '已达到最大重试次数时以 failure 状态执行,错误代码为 RETRY_EXHAUSTED。'), failure_port],
|
|
};
|
|
|
|
export const execute: NodeHandler = async (node, state, signal, deps) => {
|
|
if (node.type !== 'goto') throw new Error('Expected a goto node');
|
|
signal.throwIfAborted();
|
|
const count = Object.hasOwn(state.retry_counts, node.id) ? state.retry_counts[node.id] : 0;
|
|
if (!Number.isSafeInteger(count) || count < 0 || !Number.isSafeInteger(node.config.max_retries)
|
|
|| node.config.max_retries < 0 || !Number.isFinite(node.config.delay_ms) || node.config.delay_ms < 0) {
|
|
throw Object.assign(new Error('Invalid retry configuration or count'), {code: 'INVALID_RETRY'});
|
|
}
|
|
if (count >= node.config.max_retries) {
|
|
return {port: 'exhausted', result: {status: 'failure', output: state.last_result?.output ?? null,
|
|
error: {code: 'RETRY_EXHAUSTED', message: 'Retry limit exhausted', node_id: node.id}}};
|
|
}
|
|
const retry_counts = {[node.id]: count + 1};
|
|
assert_json(retry_counts);
|
|
await deps.sleep('milliseconds', node.config.delay_ms, signal);
|
|
signal.throwIfAborted();
|
|
return {port: 'retry', result: {status: 'success', output: state.last_result?.output ?? null}, retry_counts};
|
|
};
|