// r.step.aggregate.update — apply a delta to an existing aggregate stream. // // Wraps the existing createEventStoreExecutor.update(): writes the // `.updated` event + applies the inline projection in the // active TX. Optimistic-locking via the optional `version` field — // pipeline-author can supply the loaded version (often via a prior // r.step.read.findOne) or skip with skipOptimisticLock. // // Returns the SaveContext { id, data, changes, previous, isNew, event } // — landed under steps.. `changes` and `previous` are useful for // hooks/audit consumers; the `id` matches the input id. // // Failure-handling mirrors aggregate.create: WriteFailure → re-raised // as KumikoError → dispatcher catches and maps. import type { EventStoreExecutor } from "../../db/event-store-executor"; import { reraiseAsKumikoError } from "../../errors/write-error-info"; import { defineStep } from "../define-step"; import type { SaveContext } from "../types/hooks"; import type { EntityId } from "../types/identifiers"; import type { PipelineCtx, StepInstance, StepResolver } from "../types/step"; import { resolveOptional, resolveRequired } from "./_resolver-utils"; type AggregateUpdateArgs = { readonly name: string; readonly executor: EventStoreExecutor; readonly id: StepResolver; readonly changes: StepResolver>; readonly version?: StepResolver; readonly skipOptimisticLock?: boolean; }; defineStep({ kind: "aggregate.update", defaultFailureStrategy: "throw", resultKey: (args) => args.name, run: async (args, ctx: PipelineCtx) => { const id = resolveRequired(args.id, ctx); const changes = resolveRequired(args.changes, ctx); const version = resolveOptional(args.version, ctx); const result = await args.executor.update( { id, version, changes }, ctx.event.user, ctx.db, args.skipOptimisticLock ? { skipOptimisticLock: true } : undefined, ); if (!result.isSuccess) { throw reraiseAsKumikoError(result.error); } return result.data; }, }); export function buildAggregateUpdateStep( name: string, opts: { readonly executor: EventStoreExecutor; readonly id: StepResolver; readonly changes: StepResolver>; readonly version?: StepResolver; readonly skipOptimisticLock?: boolean; }, ): StepInstance { return { kind: "aggregate.update", args: { name, ...opts } satisfies AggregateUpdateArgs, }; }