// Create a fulfilled order, THEN emit the outgoing `order.completed` event. // // `ctx.workflows.start` / emit are post-commit safe inside a mutation: the row // commits first, then the event fans out to subscribed targets — so a rollback // never ships a webhook for an order that didn't persist. `emit` returns // immediately (the HTTP POSTs run in the background delivery workflow); the // `deliveries` array is one entry per matched target (empty until someone // subscribes via ctx.webhooks.subscribe). import type { AppContext } from '@voltro/runtime' import { useWebhooks } from '@voltro/plugin-webhooks' const execute = async (input: { sku: string; totalCents: number }, ctx: AppContext) => { // status set explicitly — a column DEFAULT only fills in on a SQL store. const row = await ctx.store.insert('orders', { sku: input.sku, totalCents: input.totalCents, status: 'fulfilled', }) const order = { id: row['id'] as string, sku: row['sku'] as string, totalCents: row['totalCents'] as number, status: row['status'] as string, tenantId: row['tenantId'] as string, } // Fan out to every subscribed target (durable, signed, retried in the bg). const webhooks = useWebhooks(ctx) const { eventId, deliveries } = await webhooks.emit('order.completed', { orderId: order.id, tenantId: order.tenantId, sku: order.sku, totalCents: order.totalCents, }) console.log(`[webhook:emit] order.completed event=${eventId} → ${deliveries.length} target(s)`) return order } export default execute