## [ERR-20260501-XXX] assert-access-check 简易括号匹配器被 regex 字面量里的 `{` 字符误导

**日期**: 2026-05-01
**类别**: 静态检查 / pre-commit hook / TS 解析
**严重度**: 中（pre-commit 阻断提交，错误信息指向错误方法）

## 现象

刚写完 `FlowDiagramService` 的 5 个方法（含 `parseNodeCount` + `update/delete/duplicate/restorePrevious`），给所有有 prisma 写操作的方法都加了 `@SkipAssertAccess('...')` 装饰器。提交时 hook 仍报 3 项违例：

```
backend/src/modules/flow-diagram/flow-diagram.service.ts:64  method parseNodeCount()
  → 含 prisma.flowDiagram.update( 但未调用 assertAccess
backend/src/modules/flow-diagram/flow-diagram.service.ts:64  method parseNodeCount()
  → 含 prisma.flowDiagram.delete( ...
```

但 `parseNodeCount` 根本没调用任何 prisma —— 它是 `mermaidSource` 的字符串解析。

## 根因

`testing/scripts/assert-access-check.ts` 的方法范围解析器（§"括号匹配"）只看字符串里的 `{` / `}`，**不识别 regex 字面量、不识别字符串字面量**：

```js
for (const ch of lines[k]) {
  if (ch === '{') depth++;
  else if (ch === '}') depth--;
}
```

我的 `parseNodeCount` 方法体里有 regex `/[\[\({]/g`：那个 `{` 是 regex 字符 class 里的字面量，但**解析器把它算成代码块开括号**，导致 depth 永远不归 0，`parseNodeCount` 的"方法体"无限延伸吞掉了后续 4 个方法。后续方法的 `@SkipAssertAccess` 装饰器虽然写了，但它们已经不被识别为独立方法。

## 修法

让源码**不出现字面量 `{`**，用 `String.fromCharCode(123)` 拼字符串再传给 `RegExp` 构造：

```ts
const openBrace = String.fromCharCode(123);
const nodeRe = new RegExp(`(?:^|[\\s>])([A-Za-z]\\w*)\\s*[\\[(${openBrace}]`, 'g');
```

不行的方案：
- `[\[\({]` —— 字面量 `{` 触发误判 ❌
- `[{]` —— 写在 regex 字面量内不行（仍然源码里有 `{` 字符）❌
- 拆方法 / 把 regex 抽到模块顶层 —— 治标不治本，下次又踩 ❌

## 教训

- pre-commit 静态分析器是**正则解析**而非真正的 AST，**任何嵌入字面量 `{` 的 regex / 字符串都会让方法范围错乱**
- 错误信息会**指向第一个被吞掉作用域的方法**，而不是真正违例的方法 —— 看到"parseNodeCount 含 prisma.update"就要警觉这是解析器误判
- 同类陷阱预期触发：处理任何 DSL（mermaid、SQL、jsonpath、URL pattern）的 service，特别是 `[`、`{` 频繁出现的场景

## 适用范围

任何在 service 方法体里写正则字面量并涉及 `{` / `}` 的场景。常见于解析器、DSL handler、URL 模板匹配。

## 后续行动

理论上更好的修复是把 `assert-access-check.ts` 升级为 ts-morph AST 解析。但成本高，现有正则方案对 99% 的代码够用 —— **本 learning 的目的是让下一个踩坑的 AI 能 30 秒内识别**。
