@iworkflow/core

中文文档
0.15.1发布于 2026/11/01MITTypeScriptES Module零依赖

引擎中立的持久工作流端口:流、步骤、子工作流、信号、查询、补丁、心跳、工作流契约

中文使用文档

@iworkflow /核心

Портдвижкадолгихпроцессов:кодпроцессапишетсяодинразиисполняется时间 (@iworkflow/temporal) или dbos (@iworkflow/dbos). Зависимостейнет;контракту процесса нужен @sinclair/typebox.

import type { Flow } from "@iworkflow/core";

export const outreach: Flow = async (ctx, input: { id: string }) => {
  const draft = await ctx.step<string>("compose", [input]);
  const profile = await ctx.child({ app: "iprofile", flow: "enrich" }, [input], { id: `${ctx.workflowId}-p` });
  const reply = await ctx.recv<string>("reply", 86_400_000);
  if (await ctx.patched("send-twice")) await ctx.step("remind", [input]);
  await ctx.setState("status", { reply });
  return { draft, profile, reply };
};

Шаг r прогрессом: heartbeat(details) и lastHeartbeat() из этого пакета внутри функции шага。 Рантайм шага ставит адаптер на время шага (stepScope):3 одном процессе могут жить оба движка。 @iworkflow/core иадаптеры——однойверсии:областьшагаобщаячерез Symbol.for.

Отказы одинаковы на обоих движках。Шагбросает nonRetryable(message, type, details) или new Failure(message, { type });портбросает FlowError с reason (failed, timeout, terminated, already-started, not-found), type, details и exhausted:

try {
  await ctx.step("send", [input], { attempts: 20, retryIntervalMs: 120_000, backoff: 1.5, pool: "paced" });
} catch (e) {
  if (e instanceof FlowError && e.type === "QuietHours") await ctx.sleep(e.details as number);
  else throw e;
}

Ещё: ctx.startChild (безожидания), ChildOptions.timeoutMs (id не переиспользуется: занятый - already-started), ctx.startedAt, ctx.parentId, AppDefinition.pools и maxConcurrentSteps, StartOptions.onExisting, handle.describe() и handle.terminate(), client.list({ idPrefix, flow, status, since, limit }) ——процессыдвижка,свежиепервыми(безсвоегореестразапусков)。

Приоритет: client.start(..., { priority }) (1 - первым, 5 - последним)→ ctx.priority;шагии дети его наследуют, StepOptions.priority ——свой。Срок повтора из отказа: new Failure(msg, { type, retryAfterMs }); benign: true - штатный отказ, Temporal не пишет его ошибкой。Номер попытки * шаге - stepAttempt(), процесс * запуск - stepInfo(), отмена попытки - stepSignal()。Журнал без повторов - ctx.log。Время * процессе - await ctx.now(). client.signalWithStart(target, args, opts, topic, value) - сигнал идущему или запуск, сигналом。 Контракт обоих движков - lab/test/contract.test.ts репозитория。

Контрактпроцесса

ВладелецпроцессаобъявляетконтрактсхемамиTypeBox (@sinclair/typebox 0.34, как (Elysia) своём пакете; соседиимпортируютегооттуда,анекопируюттипы。

import { Type as T } from "@sinclair/typebox";
import { childContract, defineWorkflowContract, startContract } from "@iworkflow/core/contract";

export const organizationOnboarding = defineWorkflowContract({
  name: "organizationOnboarding",
  taskQueue: "iprofile",
  workflowId: "iprofile-org-{code}",            // поля — обязательные поля входа
  input: T.Object({ code: T.String(), linkedBy: T.String() }),
  outcomes: { created: T.Object({ ok: T.Literal(true), organizationId: T.String() }),
              refused: T.Object({ ok: T.Literal(false), reason: T.String() }) },
  states: { checking: { next: ["provisioning", "refused"] }, provisioning: { next: ["created"] } },
  signals: {}, queries: {},                        // тема/ключ → { description, schema }
});

const h = await startContract(client, organizationOnboarding, input);   // id из шаблона, исход типизирован
const out = await childContract(ctx, organizationOnboarding, input);     // из кода процесса

Ошибкисогласованности(поленidевовходе,недостижимыйисход,состояниебезпутикисходу)——приимпорте。 workflowId(input), startContract и childContract на пустом или неверном поле отдают Failure стипом InvalidWorkflowId (nonRetryable):впроцессеэтоотказ,аневечныйповторзадачи。Отказ по делу (bad-code) родитель получает, если <e:2> проверит вход до запуска или поймает isFailure(e) && e.type === INVALID_WORKFLOW_ID. Манифест(JSON模式входа,исходов,сигналови查询),страницас减记диаграммой美人鱼ипроверкадрейфа:

bunx iworkflow-contracts src/temporal/contract.ts --out docs/contracts          # записать (нужен bun: грузит .ts)
bunx iworkflow-contracts src/temporal/contract.ts --out docs/contracts --check  # в CI: падает на расхождении

Одинвызовнакаталогсовсемимодулями:лишниефайлысметкойгенераторазаписьудаляет,файлыбезметкинетрогает。

版本与发布记录

@iworkflow/core 共发布 18 个版本,首次发布于 2026/10/21;过去 12 个月发布了 18 个版本。

版本发布日期距今
0.15.1latest2026/11/012 天前
0.15.02026/11/012 天前
0.14.02026/10/303 天前
0.13.02026/10/303 天前
0.12.02026/10/304 天前
0.11.02026/10/304 天前

关于 @iworkflow/core 的常见问题

@iworkflow/core 是什么?

引擎中立的持久工作流端口:流、步骤、子工作流、信号、查询、补丁、心跳、工作流契约

如何安装 @iworkflow/core?

在项目目录运行 npm install @iworkflow/core,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 iworkflow-contracts,可以用 npx iworkflow-contracts 直接运行。

@iworkflow/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@iworkflow/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@iworkflow/core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@iworkflow/core 可以商用吗?

@iworkflow/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@iworkflow/core 还在维护吗?

最新版本 0.15.1 发布于 2026/11/01(2 天前);至今共发布 18 个版本,其中过去 12 个月发布了 18 个;首次发布于 2026/10/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。