Architecture-deepening candidate #2 (Worth exploring) from the improve-codebase-architecture review. Related to #434.
Problem
packages/core/src/hooks/index.ts exposes eight separate execute* functions — list + field variants of resolveInput, validate, beforeOperation, afterOperation. Each write call site in context/index.ts must invoke them in the correct order and thread resolvedData through by hand:
let resolvedData = await executeResolveInput(...)
resolvedData = await executeFieldResolveInputHooks(inputData, resolvedData, ...)
await executeValidate(... resolvedData ...)
await executeFieldValidateHooks(... resolvedData ...)
The "transform → validate" order and the data threading are knowledge held by the caller, not the module — orchestration leakage. Adding a phase touches every call site.
Deletion test → concentrates: replacing the loose functions with one pipeline object pulls the ordering/threading into one place; call sites shrink to "run, then check errors."
Proposed deepening
A HookPipeline with a small interface that owns the transform/validate span:
hookPipeline.run({ operation, inputData, item, listConfig, context })
// → { resolvedData, validationErrors }
It runs, in order: list resolveInput → field resolveInput → list validate → field validate → built-in field rules. Call sites become const { resolvedData, validationErrors } = await hookPipeline.run(...).
Relationship to #434 (Write Pipeline)
These are complementary: the Write Pipeline (#434) calls the Hook Pipeline for the transform/validate span, then handles access, persistence, and after-hooks. This may be extracted as a sub-step of #434 or landed separately afterward — coordinate to avoid overlap. If #434 lands first and already isolates this span, downgrade/close this.
Acceptance criteria
Out of scope / notes
Architecture-deepening candidate #2 (Worth exploring) from the
improve-codebase-architecturereview. Related to #434.Problem
packages/core/src/hooks/index.tsexposes eight separateexecute*functions — list + field variants ofresolveInput,validate,beforeOperation,afterOperation. Each write call site incontext/index.tsmust invoke them in the correct order and threadresolvedDatathrough by hand:The "transform → validate" order and the data threading are knowledge held by the caller, not the module — orchestration leakage. Adding a phase touches every call site.
Deletion test → concentrates: replacing the loose functions with one pipeline object pulls the ordering/threading into one place; call sites shrink to "run, then check errors."
Proposed deepening
A
HookPipelinewith a small interface that owns the transform/validate span:It runs, in order: list
resolveInput→ fieldresolveInput→ listvalidate→ fieldvalidate→ built-in field rules. Call sites becomeconst { resolvedData, validationErrors } = await hookPipeline.run(...).Relationship to #434 (Write Pipeline)
These are complementary: the Write Pipeline (#434) calls the Hook Pipeline for the transform/validate span, then handles access, persistence, and after-hooks. This may be extracted as a sub-step of #434 or landed separately afterward — coordinate to avoid overlap. If #434 lands first and already isolates this span, downgrade/close this.
Acceptance criteria
HookPipelineowns the transform+validate order; call sites no longer threadresolvedDataor sequence hooks by hand.ValidationError; hook arguments unchanged (Keystone-compliant).packages/coresuite stays green.@opensaas/stack-corechangeset (patch — internal refactor). Lint/format clean. Noany/casts introduced.Out of scope / notes
checkFieldAccess) and the two-phase read are untouched (ADR-0001).