Part of #4706。从 #5702 实施期实测拆出:#5702 在 SQL 族(driver-sql / driver-sqlite-wasm / driver-turso 的 local + remote 两面)实现了 $icontains,JS 求值面一个都没有。
现状(实测,#5702 落地后)
| 面 |
{ name: { $icontains: 'acme' } } |
| driver-sql / -sqlite-wasm / -turso(local+remote) |
✅ 求值,ASCII-only 折叠 |
| driver-memory 查询路径 + 分析面 |
❌ INVALID_FILTER / 400(SUPPORTED_FIELD_OPERATORS 由 spec FILTER_OPERATORS 派生,该表刻意未收 $icontains) |
| driver-memory 参考匹配器 |
❌ 同上(同一道 shape gate) |
| driver-mongodb |
❌ default: 拒收 |
objectql having |
❌ CONDITION_OPERATORS 不含它 |
@objectstack/formula matchesFilter |
❌(未在 #5702 范围内核过实现,但它不在任何 $icontains 分支里) |
拒收是 fail-closed,不是静默放宽 —— 这是刻意的方向,不是缺陷本身。缺陷是同一条 filter 在两类后端上给两个答案:一个返回行,一个抛 400。
用户可见后果
应用测试跑内存 double、生产跑 SQL 是本仓的常见形态(plugin-auth 的 auth-contains-filter.test.ts 就是这个形状)。一条用 $icontains 的 filter 会在测试里抛错、在生产里正常,或者反过来 —— 正是 #4706 对 $regex 的原始指控(「the divergence only shows up when the app's tests run on the memory double and production runs SQL」),换了个算子重演。
下游 #5814(better-auth Where.mode: 'insensitive')一旦落地就会踩到:认证查询在内存 double 上会 400。
建议范围
packages/spec 的 FILTER_OPERATORS 收入 $icontains,同 PR 更新 filter-operator-vocabulary.test.ts 的差集 pin(该 pin 现在钉死差集恰为 { $icontains });
- driver-memory 两面 + driver-mongodb 的实现(ASCII-only 折叠,不是
toLowerCase() —— 契约以 FILTER_TEXT_CASES 的非 ASCII 不折 pin 为准);
service-analytics 的 compileScopedFilterToSql 补一条臂(否则 echo 覆盖测试判红);
- objectql
having 与 formula 同批;
- 清
scripts/check-driver-conformance.mjs 里 driver-memory / driver-mongodb 两行 FILTER_TEXT_CASES DEBT 的 requirement-1 半边。
memory 侧实现本身是平凡的(真正则引擎已在,ASCII 折叠即可);贵的是词表纳入牵动的 allowlist 消费者。
Part of #4706。从 #5702 实施期实测拆出:#5702 在 SQL 族(driver-sql / driver-sqlite-wasm / driver-turso 的 local + remote 两面)实现了
$icontains,JS 求值面一个都没有。现状(实测,#5702 落地后)
{ name: { $icontains: 'acme' } }INVALID_FILTER/ 400(SUPPORTED_FIELD_OPERATORS由 specFILTER_OPERATORS派生,该表刻意未收$icontains)default:拒收havingCONDITION_OPERATORS不含它@objectstack/formulamatchesFilter$icontains分支里)拒收是 fail-closed,不是静默放宽 —— 这是刻意的方向,不是缺陷本身。缺陷是同一条 filter 在两类后端上给两个答案:一个返回行,一个抛 400。
用户可见后果
应用测试跑内存 double、生产跑 SQL 是本仓的常见形态(
plugin-auth的auth-contains-filter.test.ts就是这个形状)。一条用$icontains的 filter 会在测试里抛错、在生产里正常,或者反过来 —— 正是 #4706 对$regex的原始指控(「the divergence only shows up when the app's tests run on the memory double and production runs SQL」),换了个算子重演。下游 #5814(better-auth
Where.mode: 'insensitive')一旦落地就会踩到:认证查询在内存 double 上会 400。为什么 #5702 没做
$regex响亮拒收 +$icontains各后端实现(#4706 裁决 B 案 · 驱动半边) #5702 正文明确「$icontains实现半边属语义补齐投入,默认挂起」;having/ formula 不在domain:drivers车道;$icontains加进 spec 的FILTER_OPERATORS是这些实现的前提,也是它们的风险。该数组是运行时 allowlist(driver-memory的 shape gate 与service-analytics的objectql-echo-operator-coverage.test.ts都从它派生),spec:$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701 实测过:提前加入会让 driver-memory 从「响亮拒收」翻成「静默放宽」(谓词被丢弃 → 匹配每一行 → RLS 读作用域上是权限绕过,A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948)。所以词表纳入必须与 driver-memory + service-analytics 的实现同 PR,不能先加词表。建议范围
packages/spec的FILTER_OPERATORS收入$icontains,同 PR 更新filter-operator-vocabulary.test.ts的差集 pin(该 pin 现在钉死差集恰为{ $icontains });toLowerCase()—— 契约以FILTER_TEXT_CASES的非 ASCII 不折 pin 为准);service-analytics的compileScopedFilterToSql补一条臂(否则 echo 覆盖测试判红);having与 formula 同批;scripts/check-driver-conformance.mjs里 driver-memory / driver-mongodb 两行FILTER_TEXT_CASESDEBT 的 requirement-1 半边。memory 侧实现本身是平凡的(真正则引擎已在,ASCII 折叠即可);贵的是词表纳入牵动的 allowlist 消费者。