Message conditions (--)

Message conditions match **client text frames**: shorthands (`--login`), field tests (`--type=ping`), nesting, and combinations. This chapter also separates app-level ping from WebSocket protocol Ping.

前提(# format json):payload 可 parse 为 JSON 对象;否则本条件不匹配。 前提(# format text):payload 为原始字符串;主要用 --= 。

解析优先级(消息条件行;生命周期 --@ 见第七节,优先于本节)

解析步骤:

  1. 若行首 --@ → 走第七节(生命周期);行尾仍可附 ~name / !~name
  2. 否则 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)