45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
/**
|
|
* 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<PluginHookEntityPayload, { enqueued: boolean }>(
|
|
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 };
|
|
}
|
|
);
|