npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kiosk-flow-processor

v0.1.1

Published

Runtime that executes Kiosk Flow Builder .flw flows

Downloads

10

Readme

Kiosk Flow Processor

框架无关的流程运行时:解释 .flw 流程图,驱动节点之间的跳转,并执行每个节点对应的脚本。纯 TypeScript、零 UI 依赖,可嵌入任意前端框架(Angular / React / Vue …)或 Node 环境。


命名关系:kiosk-flow-builder 与 kiosk-flow-processor

这两个名字经常被混淆,但它们是两个不同的东西,职责完全分开:

| | kiosk-flow-builder | kiosk-flow-processor(本仓库) | |---|---|---| | 是什么 | 可视化授权插件(VS Code 扩展 "Kiosk Flow Builder") | 运行时/引擎 | | 干什么 | 让你用图形化方式画流程,产出 .flw 文档 + 节点脚本骨架 | 执行 .flw:按边推进节点、调用节点脚本的 run(ctx) | | 何时用 | 开发期(设计流程) | 运行期(跑流程) | | 依赖关系 | 只是个编辑器 | 不依赖 kiosk-flow-builder,能跑任何合法 .flw,无论它怎么产生 |

一句话:kiosk-flow-builder 画流程(.flw)→ kiosk-flow-processor 执行流程。

历史:本包早期叫 @flow-builder/runtime,那个名字把「授权工具」和「运行时」混为一谈。现已改名为 kiosk-flow-processor,让运行时的命名与授权插件解耦。项目引用本包时,与 kiosk-flow-builder 没有任何运行期关系——.flw 只是一种输入数据格式。


核心概念

  • .flw 文档:纯 JSON(FlowDoc = nodes + edges)。节点类型有 start / end / operation / decision / subflow;边可带 event 标签。
  • 节点脚本:operation / decision 节点各对应一个 export default async function run(ctx: FlowContext)。脚本通过 ctx 与外界交互,永不直接 import 框架。
  • 跳转:脚本调 ctx.resolve(event),引擎在当前节点的出边里选 event 匹配的那条,进入目标节点。
  • 子流程(subflow):subflow 节点进入 targetFlow;子流程 end 节点的 name = 返回事件,父 subflow 节点用同名出边接住。
  • 宿主推事件:UI/宿主用 engine.emit(event, payload) 把用户事件推给正在监听的节点(ctx.on(event, …))。

快速上手

import { createFlowEngine } from 'kiosk-flow-processor';

const engine = createFlowEngine({
  services,   // 你的领域服务,节点脚本经 ctx.services 访问
  state,      // 唯一状态容器,节点脚本经 ctx.state 读写
  logger,     // 可选:ctx.logger
  i18n,       // 可选:ctx.i18n
});

// 注册流程图与节点脚本(key 为类 glob 路径)
engine.registerFlows({ 'flows/Session.flw': sessionFlowDoc /* 或 JSON 字符串 */ });
engine.registerScripts({ 'flow-scripts/Session/OpenSession.ts': { default: openSessionRun } });

// 启动,返回顶层流程的结束事件名
const exit = await engine.run('Session');

// 运行期把用户事件推给当前节点
engine.emit('submitted', { pin: '1234' });

一个节点脚本长这样:

import type { FlowContext } from 'kiosk-flow-processor';

export default async function run(ctx: FlowContext): Promise<void> {
  ctx.services.screen.show('common/PinPad');          // 调宿主服务
  ctx.on('submitted', ({ pin }) => {                  // 收宿主事件
    ctx.state.pin = pin;                              // 写状态
    ctx.resolve('continue');                          // 选出边推进
  });
  ctx.on('cancelled', () => ctx.resolve('cancel'));
}

强类型(声明合并)

FlowServices / FlowState / FlowEventMap 通过 declare module 'kiosk-flow-processor' 由使用方扩展,节点脚本因此获得精确类型,无需 any:

declare module 'kiosk-flow-processor' {
  interface FlowServices { screen: ScreenService; /* … */ }
  interface FlowState { pin: string | null; /* … */ }
  interface FlowEventMap { submitted: { pin: string }; cancelled: void; }
}

可观测性与健壮性

节点生命周期与当前位置

const engine = createFlowEngine({
  onNodeEnter: ({ flowName, node, seq }) => log(`→ ${flowName}/${node.name} #${seq}`),
  onNodeExit:  ({ node, event })         => log(`← ${node.name} =${event}`),
});

engine.current; // { flowName, node } | null —— 运行中的当前节点;未运行/已结束为 null
  • 所有节点类型触发,seq 在单次 run() 内单调递增。
  • exit 的 event:start=出边事件、operation/decision=resolve 的事件、end=该 end 节点名;subflow 的 exit 在子流程返回时才触发(event=子流程 end 名),形成正确的嵌套 trace。
  • 回调抛错非致命(记 logger.error,不打断流程)。适合日志、埋点、屏幕切换钩子、测试断言。

节点清理钩子 ctx.onDestroy

export default async function run(ctx: FlowContext) {
  const sub = external.subscribe(/* … */);
  ctx.onDestroy(() => sub.unsubscribe()); // 节点离开(resolve 或错误)时触发一次
}

多个句柄按注册顺序执行,先于 timer/订阅的自动清理,单个句柄抛错互不影响。

Runaway 守卫

maxSyncTransitions(默认 10000):两次 await 之间的连续同步转移(start/subflow/end)超过阈值即抛 FlowRunawayError,拦住"无 await 的结构环"(如子流程立即 end 的自返回环)造成的事件循环卡死。含 operation/decision 的合法长流程或无限主循环不受影响(每个 await 都会清零计数)。与 maxCallDepth(嵌套深度)正交。

节点错误:onNodeError + error 出边路由

createFlowEngine({ onNodeError: ({ node, error }) => report(node.name, error) });

节点脚本抛异常时:先触发 onNodeError(非致命);若该节点有一条显式 error 出边则路由过去、流程继续,否则抛出原始错误onErrorrun() reject。resolve 到未知事件仍是致命错误,且触发 onNodeError

注册期出边校验

convertFlowDoc(经 registerFlows 调用)对以下歧义直接抛 FlowConvertError:同节点多条默认(空 event)出边、同节点重复 event、start 节点非单出边。Kiosk Flow Builder 画布也镜像了这三条(标为 error),授权期即可发现,不必等到运行期。


公开 API(节选)

  • createFlowEngine(options?)FlowEngine(registerFlows / registerScripts / run / emit / current)
  • FlowEngineOptions:services / state / logger / i18n / maxCallDepth / maxSyncTransitions / onError / onNodeEnter / onNodeExit / onNodeError
  • FlowContext:resolve / on / off / setTimeout / setInterval / onDestroy / services / state / logger / flowName / node
  • parseFlow(text)convertFlowDoc(doc)normalizeFlowModule / normalizeScriptModuleflowKeyFromPath / scriptKeyFromPath
  • 错误类型:FlowParseErrorFlowConvertErrorFlowRunawayError
  • 类型:FlowContextFlowDocFlowNodeFlowEdgeFlowEngineFlowEngineOptionsFlowServicesFlowStateFlowEventMapFlowLoggerFlowI18nNodeLifecycleInfoNodeExitInfoNodeErrorInfo
  • VERSION:构建版本串,semver + git 短哈希(如 0.1.0+6f573fb)。运行期查当前引擎版本:import { VERSION } from 'kiosk-flow-processor'

构建与版本

npm run build      # tsup 打包到 dist/(esm + cjs + d.ts)
npm test           # vitest

版本以 package.jsonversion 为单一真相源,tsup 构建时注入 package.json version + git 短哈希 成为导出的 VERSION,便于把任意一次运行精确定位到源码 commit。