Skip to content

validate-expressions.ts 的 field-formula 校验读 f.formula —— spec 声明的是 expression,这段从未对任何 spec 合法 stack 跑过 #5026

Description

@xuyushun441-sys

#5017 清理这两个文件里的 ?? 别名链时顺带发现的,形状同族但不是 ??,所以按 Prime Directive #10 单独记账,不在 #5017 的 PR 范围内。

事实

packages/lint/src/validate-expressions.ts 的字段公式校验(#5017 分支上约 570-590 行,if (f.formula) { … } 整段)读的是 f.formula:

if (f.formula) {
  const res = validateExpression('value', f.formula as ..., { objectName, fields, fieldTypes, scope: 'record' });
  // → `object 'X' · field 'Y' formula` 的 error / warning
}

FieldSchema.shape 没有 formula。canonical 是 expression,而 formula 正是 field.zod.ts:333 里按名拒绝的别名:

formula: 'expression', calculation: 'expression', compute: 'expression',

实测(worktree 上 ObjectStackSchema.safeParse):

object w/ field.formula parses? false
  ["objects.0.fields.a: Unrecognized key(s) on this field: `formula`. …
    Did you mean `formula` → `expression`?"]
object w/ field.expression parses? true
  {"type":"formula","expression":{"dialect":"cel","source":"record.x + 1"},"returnType":"number", …}

该规则以 input: 'parsed' 注册(authoring-rules.ts),所以在 compile/build/validate 路径上看到的是解析产物 —— f.formula 恒为 undefined

#5017 那五条的差别:这条不是"收敛为 canonical",是"canonical 从来没被读过"

#5017 的每一条都有 canonical limb 在链里,删掉别名 limb 对任何能解析的 stack 零行为变化。这条不同:代码里根本没有 f.expression 的读法。所以

  • 现状:字段公式校验对任何 spec 合法 stack 都是完全不执行的;
  • 改成读 f.expression:等于启用一条从未跑过的检查,是覆盖面扩大,可能对现有真实元数据判红。

真实元数据确实全部用 canonical 拼法,即这段今天一条都没覆盖到:

位置 拼法
examples/app-showcase/src/data/objects/project.object.ts:67 expression: cel\(record.budget == null ? 0 : record.budget) - …``
examples/app-showcase/src/data/objects/invoice.object.ts:243 expression: cel\record.quantity * record.unit_price``
examples/app-showcase/src/data/objects/field-zoo.object.ts:132 expression: cel\…``
examples/app-crm/src/objects/{lead,opportunity,contact}.object.ts Field.formula({ expression: … })

validate-expressions.test.ts 里现存的 7 处 formula: fixture({ type: 'formula', formula: '…' })也都不是 spec 合法的 —— 这段分支今天全部的覆盖来自不能解析的 fixture。

需要决定的(所以没顺手改)

  1. 直接把 f.formula 换成 f.expression:恢复本意的覆盖,但要先验证真实元数据零新红(value 角色 + record scope,Formula guardrail: cel-js arithmetic silently returns null (double × int + bare identifiers) #1928 裸引用告警会一起生效),并把 7 处 fixture 改成 canonical 拼法。
  2. 删掉整段:与 validateOrgAxisRedLines 仍留着三条 spec 合法 stack 到不了的别名分支(objects[].rowLevelSecurity / .rls / permissionSets)—— #4984 修了 sharing 那一半 #5009 删 object-level RLS 同款处置。但这里和那里不同 —— 字段公式真实存在的授权/计算面,只是键名写错了,删掉会丢掉一条本应存在的 gate。
  3. 顺带要一并决定的:validate-null-guards.tsField.formula 被明确列为 excluded surface(理由是 value 角色天然可空、guard ? value : null 是祝福写法)。那条 ledger 说的是"不给 null-guard gate",不是"不给语法/字段存在性校验" —— 改动 1 不影响它,但注释里的 surface 名要跟着从 formula 改成 expression

倾向 1,理由两条轴:

但 1 是覆盖面扩大、可能对现有真实元数据判红,不该由一个"删死代码"的 PR 顺手带上,所以在这里等 maintainer 拍板。

参考

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions