消息条件(--)
消息条件用来匹配客户端发来的**文本帧**。可以写简写(如 `--login`)、字段匹配(如 `--type=ping`)、组合与嵌套。本节说明匹配规则,以及与应用层 ping、协议 Ping 的区别。
前提(# format json):payload 可 parse 为 JSON 对象;否则本条件不匹配。 前提(# format text):payload 为原始字符串;主要用 --= 。
解析优先级(消息条件行;生命周期 --@ 见第七节,优先于本节)
解析步骤:
- 若行首
--@→ 走第七节(生命周期);行尾仍可附 ~name / !~name - 否则 strip 行尾 scope 后缀(
~name或!~name)后,再判: a. 行首--=→ 全帧强匹配(见 6.5) b. 行首--field=→ 字段强存在(见 6.4) c. 行首--field=value→ 字段强匹配(见 6.3) d. 行首--field→ 字段弱匹配(见 6.2)
弱匹配:--field
JSON 顶层存在 key field 即可(值任意,含 null / "" / 0 / false)。 示例:--login 匹配 {"login":false}、{"login":""}
强匹配:--field=value
顶层 field 的值与 value 严格相等(类型归一:数字/布尔/string 比较规则与 HTTP Mock conditionValueMatch 对齐)。 支持通配符 (字段值匹配)。 示例:--password=123 --type=subscribe --channel=ticker
强存在:--field=
顶层必须有 key field;不要求 value 内容。
全帧强匹配:--=...
`--=` 之后直至行尾(trim)与整帧 payload 完全一致。
示例:
--={"type":"ping","seq":1}
--=ping
嵌套条件:AND
子条件在父条件匹配后才参与(选取 entry 与 runBranch 递归时;见 §5.2、§17.3)。
点路径(v1 必支持)
字段名含 . 表示 JSON 路径(非顶层 key 字面量):
- --payload.userId=1 强匹配嵌套字段
- --payload.user 弱匹配:路径存在即可
- --items[0].id=abc 数组下标(v1 支持 [0] 整数下标)
与顶层 --field 语义一致(弱/强/强存在/全帧 --= 仍仅针对整帧)。
解析:-- 后至 ~name/ !~name/行尾,最长匹配路径 token,再判 = 与 value。
与应用层 ping 的区分
--type=ping / --={"type":"ping"} 匹配 client 发来的 JSON 业务 ping 消息。
--@ping 匹配 WebSocket 协议级 Ping 控制帧(opcode=ping),二者完全不同。
命名 scope(条件行尾 ~name / !~name)
未命名 scope 仅在同一返回子树内匹配;跨分支退出(如 logout 停止 login 下的 loop)需命名 scope。
符号分工(避免与 @文件、--@ 生命周期、$变量 混淆)
@ 仅两处:返回行首读文件(@path);条件行首生命周期(--@open / --@loop 等,不改)。
命名 scope 一律 ~name / !~name,不与 @ 混用。
| 写法 | 位置 | 含义 |
|---|---|---|
--@open |
条件行首 | 生命周期事件 |
@fixtures/a.json |
返回行首 | 读文件 |
--login ~auth |
条件行尾 | 进入命名 scope |
--logout !~auth |
条件行尾 | 退出命名 scope |
$token |
JSON/管道内 | 变量引用 |
scopeName 格式:[A-Za-z_][A-Za-z0-9_]*(如 auth、room1)。
进入命名 scope:--login ~auth
- 写在消息条件或生命周期条件行尾(与条件主体空格分隔)
- 条件作为 entry 命中 → runBranch(entryNode, enter)(§17.4):顺序扫描平级子节点 → push scope 栈帧 { name: "auth", ... }
- runBranch 结束后移入匹配上下文;其下
--@loop等在新 scope 帧激活 - 示例:
--login ~auth
loginSuccess
--@loop 5s
heartbeat
退出命名 scope:--logout !~auth
- 写在条件行尾;匹配门控:除消息条件主体须命中外,scopeStack 上 必须已存在 名为 auth 的帧,该条件才参与匹配
- 栈上无 auth 时:
--logout !~auth整行不匹配(不是 pop no-op 仍发返回),以便同级/根级普通--logout等分支接管(见 13.6) - 匹配成功后:popScope(auth)(取消该帧及子孙帧上所有 --@loop timer)→ runBranch(popNode, popOnly)(§17.4:先 pop 再发返回;不 push 新帧、不移入子树)
- 扫描范围:带
!~name的消息条件在整棵 Flow 中参与候选(不受当前匹配子树限制),便于在 auth 子树深处时仍能用根级--logout !~auth退出 - 优先级:任一通过门控的
!~name命中,优先于所有不带!~的消息条件(源序仅在同为 !~ 或同为非 !~ 时决胜)
生命周期条件也可命名
--@open ~session
welcome +0ms
连接级子树 scope 名为 session;可用 --logout !~session 结束整段会话。
嵌套命名 scope
--login ~auth
loginSuccess
--join ~room
joined
--@loop 2s
roomTick
--@loop 5s
heartbeat
--leave !~room:仅 pop room,停 roomTick;heartbeat 继续--logout !~auth:pop auth 及全部子孙,heartbeat 与 roomTick 均停止
validate 规则
- 同一 Flow AST 中 两处条件行 均写
~sameName进入(重复定义):compile error E007 - 运行时 logout 后再 login
~auth允许(非 E007) - Flow 中存在
!~auth但 AST 中无任何~auth进入定义:compile warning W001(栈上本就不会命中,易写错) - 运行时栈上无 name 时
!~name不匹配(无 warning;见 6.9.3)