Skip to content

Commit a054761

Browse files
committed
docs(skills,docs): 三处 hook 文档不再教「批量写的行级谓词在 ctx.input.ast」(#5670)
引擎从未在写路径的 `HookContext` 上放过 AST。`packages/objectql/src/engine.ts` 的 5 个 `HookContext` 生产点里,`input: { ast }` 只出现在两条读路径(喂 `driver.find` / `findOne`);写路径构造的是 `{ id, data, options }` 与 `{ id, options }`,批量写的行级谓词走引擎内部的 `OperationContext.ast`(#2982), 从不进 `hookContext.input`。#5273 已把 spec 侧那份契约表改成引擎真正构造的形状 (PR #5668),真值由新增的 `packages/objectql/src/hook-input-shape-contract.test.ts` 钉死 —— 其中 `beforeUpdate` / `beforeDelete` 在 `multi: true` 上都断言 `'ast' in input === false`,并以 `beforeFind` 作阳性对照证明引擎并非到处不发 `ast`。 同一句从未兑现的陈述还活在三个面上,本单按已落地措辞逐处对齐: - `skills/objectstack-data/rules/hooks.md` - `skills/objectstack-data/references/data-hooks.md` - `content/docs/api/data-flow.mdx` 三处改法一致:删掉「the row-scoping predicate is in `ctx.input.ast`」半句,替换为 两句实情 —— 批量写不向 hook 暴露谓词(谓词在引擎内部 `OperationContext.ast`, composed 的 RLS / sharing 过滤器由此绑定 driver 调用本身,handler 无法放宽;要 限定批量就在调用方用 `options.where`),以及 #5038 之后 `after*` 按匹配行派发、 每行单记录形状、`input.id` 在那里绑定。主句(批量写触发同名事件、没有 `*Many` 事件)本身成立,原样保留。 前两个文件属已发布 skill 目录,沿其既有惯例带 issue 编号;`data-flow.mdx` 全文 零 issue 引用,故那处不引编号。三处都不写仓内 `packages/**` 测试路径 —— 这三份 文件现有引用里没有一条指向仓内源码路径,而 skill 经 `npx skills add` 装到第三方 项目后那种路径也不存在。 ⛔ `content/docs/releases/v16.mdx:185` 带同句但未动:发布态记录按 CLAUDE.md 不在 代码 PR 里改。`packages/spec/**` 未动(#5668 已修)。 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
1 parent 5c94f83 commit a054761

3 files changed

Lines changed: 19 additions & 9 deletions

File tree

content/docs/api/data-flow.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ flowchart TD
310310
end
311311
```
312312

313-
The lifecycle events are defined by the `HookEvent` enum (8 events). Reads fire `beforeFind`/`afterFind` — for **both** `find` and `findOne`, so one subscription covers every read shape. Bulk writes (`multi: true`) fire the **same** `beforeUpdate`/`beforeDelete`/`afterUpdate`/`afterDelete` events as single-id writes, with the row-scoping predicate in `ctx.input.ast`. There are deliberately no per-method (`findOne`/`count`/`aggregate`) or `*Many` events: read authorization and row filtering are RLS/permission-rule concerns, and field masking is field-level metadata.
313+
The lifecycle events are defined by the `HookEvent` enum (8 events). Reads fire `beforeFind`/`afterFind` — for **both** `find` and `findOne`, so one subscription covers every read shape. Bulk writes (`multi: true`) fire the **same** `beforeUpdate`/`beforeDelete`/`afterUpdate`/`afterDelete` events as single-id writes. A bulk write hands hooks **no** row-scoping predicate: it lives on the engine-internal `OperationContext.ast`, so the RLS / sharing filters composed onto it bind the driver call itself, where no handler can widen them — scope a batch through `options.where` at the caller. The `after*` events instead dispatch **once per matched row**, each on a single-record-shaped context whose `input.id` names that row. There are deliberately no per-method (`findOne`/`count`/`aggregate`) or `*Many` events: read authorization and row filtering are RLS/permission-rule concerns, and field masking is field-level metadata.
314314

315315
| Hook | Phase | Can Modify? | Can Abort? |
316316
|:---|:---|:---|:---|

skills/objectstack-data/references/data-hooks.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,16 @@ ObjectStack provides **8 lifecycle events** organized by operation type:
5858
> **Why only 8?** The read events fire for `findOne` as well as `find` (the event
5959
> attaches to record materialization, not the engine method), so one subscription
6060
> covers every read shape — there is no `beforeFindOne`/`afterFindOne`. Likewise the
61-
> write events fire on bulk `multi:true` operations (the row-scoping predicate is in
62-
> `ctx.input.ast`), so there is no `*Many` event. And there is no `beforeCount`/
63-
> `beforeAggregate`: read authorization and row filtering belong to **RLS / permission
64-
> rules**, and field masking to **field-level metadata** — declarative mechanisms that
65-
> apply everywhere, rather than a hook every author must remember to re-attach.
61+
> write events fire on bulk `multi:true` operations, so there is no `*Many` event. A
62+
> bulk write hands hooks **no** row-scoping predicate: it lives on the engine-internal
63+
> `OperationContext.ast` (#2982), so the RLS / sharing filters composed onto it bind
64+
> the driver call itself, where no handler can widen them — scope a batch through
65+
> `options.where` at the caller. The `after*` events instead dispatch **once per
66+
> matched row**, each on a single-record-shaped context whose `input.id` names that
67+
> row (#5038). And there is no `beforeCount`/`beforeAggregate`: read authorization and
68+
> row filtering belong to **RLS / permission rules**, and field masking to
69+
> **field-level metadata** — declarative mechanisms that apply everywhere, rather than
70+
> a hook every author must remember to re-attach.
6671
6772
### Before vs After Hooks
6873

skills/objectstack-data/rules/hooks.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,14 @@ Sandbox essentials (full contract in
112112

113113
> **One read event, one write event per kind.** `beforeFind`/`afterFind` fire for
114114
> `findOne` too (the event attaches to record materialization, not the method), and
115-
> the write events fire on bulk `multi:true` operations as well — the row-scoping
116-
> predicate is in `ctx.input.ast`. There is no `beforeFindOne`, `beforeCount`,
117-
> `beforeAggregate`, or `*Many` event.
115+
> the write events fire on bulk `multi:true` operations as well. A bulk write hands
116+
> hooks **no** row-scoping predicate: it lives on the engine-internal
117+
> `OperationContext.ast` (#2982), so the RLS / sharing filters composed onto it bind
118+
> the driver call itself, where no handler can widen them — scope a batch through
119+
> `options.where` at the caller. The `after*` events instead dispatch **once per
120+
> matched row**, each on a single-record-shaped context whose `input.id` names that
121+
> row (#5038). There is no `beforeFindOne`, `beforeCount`, `beforeAggregate`, or
122+
> `*Many` event.
118123
>
119124
> **Don't reach for a hook when a declarative mechanism already fits:**
120125
> - Read authorization / row filtering → **RLS / permission rules**, not a `beforeFind` hook.

0 commit comments

Comments
 (0)