/** * Async-task example — Phase 06 #23. * * Long-running side effects should not run inside the plugin invocation * (the sandbox has a fuel ceiling and the hook fan-out is blocked * while a handler runs). Instead, the plugin records an audit event * and returns immediately; a host worker picks up the event from the * outbox and does the heavy lifting outside the sandbox. * * The plugin never blocks waiting on the worker — that is the whole * point of the pattern. If the host wants a callback, it emits a new * hook event when the worker is done; the plugin can subscribe to * that too. */ import { defineHook, type PluginHookEntityPayload } from "../../src/index.js"; export const handleEntityUpdated = defineHook( async (input, ctx) => { ctx.log.info( { tenantId: input.tenantId, entity: input.entityType, changedFieldCount: input.changedFields?.length ?? 0, }, "async-task.handleEntityUpdated received update" ); // Record one structured audit event — the host worker subscribes // to plugin-emitted audit events and runs the actual work. Body // is bounded so we don't accidentally turn the audit log into a // queue (the audit sink rejects payloads above its cap). await ctx.audit.record({ action: "async-task.work-requested", target: `${input.entityType}:${input.entityId}`, meta: { tenantId: input.tenantId, changedFieldCount: input.changedFields?.length ?? 0, }, }); ctx.log.info({}, "async-task enqueued work-request, returning fast"); return { enqueued: true }; } );