import { createCommandEnvelope } from "./command-envelope.js"; import { waitForCommandSettlement } from "./command-settlement.js"; import type { CommandSettledDetail } from "./host-event-contract.js"; import type { CommandEnvelope, PublicCommandPayload } from "./sdk-types.js"; /** * A command input: the pure payload with `kind` and command-specific fields. * Distributes over the PublicCommandPayload union so discriminant * narrowing is preserved (e.g. `{ kind: "open", flowId }` type-checks). */ export type CommandInput = PublicCommandPayload extends infer C ? C extends PublicCommandPayload ? C : never : never; type DispatchOptions = { idempotencyKey?: string; }; /** * Creates a dispatch function that wraps a command payload in an envelope, * pushes it into the queue, and returns a promise that settles when the * runtime acknowledges it. * * Request IDs are generated automatically. * * @example * ```ts * const dispatch = createCommandDispatch((env) => queue.push(env)); * await dispatch({ kind: "open", flowId: "f1", flowHandleId: "h1" }); * ``` */ export const createCommandDispatch = ( push: (envelope: CommandEnvelope) => void, ): ((input: CommandInput, options?: DispatchOptions) => Promise) => { const dispatchAndWait = createCommandSettlementDispatch(push); return async ( input: CommandInput, options?: DispatchOptions, ): Promise => { await dispatchAndWait(input, options); }; }; export const createCommandSettlementDispatch = ( push: (envelope: CommandEnvelope) => void, ): (( input: CommandInput, options?: DispatchOptions, ) => Promise) => { return async ( input: CommandInput, options?: DispatchOptions, ): Promise => { const envelope: CommandEnvelope = createCommandEnvelope({ command: input, idempotencyKey: resolveIdempotencyKey(options?.idempotencyKey) ?? undefined, }); push(envelope); return waitForCommandSettlement({ requestId: envelope.requestId }); }; }; const resolveIdempotencyKey = (value?: string): string | null => { if (value === undefined) { return null; } const idempotencyKey = value.trim(); if (!idempotencyKey) { throw new Error("idempotencyKey must be a non-empty string"); } return idempotencyKey; };