Фазы и результаты handler
Phases
| Phase | Когда |
|---|---|
beforeCreate | до insert |
afterCreate | после insert |
beforeUpdate | до update |
afterUpdate | после update |
beforeDelete | до delete |
afterDelete | после delete |
beforeRead | до read (где wired) |
afterRead | после read |
beforeSaveVersion | alias для versioning entry |
afterSaveVersion | после commit version / WC |
Handler result
| Result | Эффект |
|---|---|
void / { type: 'next' } | следующий handler |
{ type: 'replaceInput', input } | подмена input для следующих handlers |
{ type: 'shortCircuit', result } | стоп цепочки, вернуть result (только фазы before*) |
{ type: 'abort', code, message, status? } | GraphQL/REST error |
Порядок выполнения
- Host core handlers (order ≈ 0) — ability checks, field validation
- Extension handlers по
orderиз registry (какstore.tsSDK)
skipHooks
Internal mutations могут передать skipHooks: true в runner, чтобы избежать рекурсии. Публичные API extensions не должны это использовать.
Пример abort
ts
ctx.registerEntityHook('artifact', 'beforeCreate', async (hookCtx) => {
if (hookCtx.fields['my-ext.strict'] === 'true' && !hookCtx.input?.name?.startsWith('REQ-')) {
return {
type: 'abort',
code: 'STRICT_NAME',
message: 'Name must start with REQ-',
};
}
});Пример replaceInput
ts
ctx.registerEntityHook('artifact', 'beforeCreate', async (hookCtx) => {
const input = hookCtx.input as { name?: string };
if (!input?.name) return;
return {
type: 'replaceInput',
input: { ...input, name: input.name.trim() },
};
});