• Pauses the workflow until a signal with the given signalId is received. The workflow suspends durably — it survives process restarts and will resume exactly once when the matching signal() call delivers data.

    condition is the receive side of the signal coordination pair. The send side is signal(), which can be called from another workflow, a hook function, or externally via client.workflow.signal().

    On replay, condition returns the previously stored signal payload immediately — no actual suspension occurs.

    import { Durable } from '@hotmeshio/hotmesh';

    export async function approvalWorkflow(orderId: string): Promise<boolean> {
    const { submitForReview } = Durable.workflow.proxyActivities<typeof activities>();
    await submitForReview(orderId);

    // Pause until a human approves or rejects
    const decision = await Durable.workflow.condition<{ approved: boolean }>('approval');
    return decision.approved;
    }

    // From an API handler or another workflow:
    await client.workflow.signal('approval', { approved: true });

    Pass a duration string as the second argument to set a deadline. condition returns false if the timeout fires before a signal arrives.

    const decision = await Durable.workflow.condition<{ approved: boolean }>(
    'approval',
    '72h', // give reviewers 72 hours; returns false on timeout
    );
    if (decision === false) return 'auto-rejected-timeout';
    return decision.approved ? 'approved' : 'rejected';

    Pass a ConditionQueueConfig as the second argument to surface the pause as a claimable row in public.hmsh_escalations. The INSERT — every field of the config, including metadata facets — is committed atomically with the workflow checkpoint: one write, one commit, crash-safe. From the row's first visible moment it carries its complete routing context and metadata, so claim-by-metadata routing and version-pinned facets (e.g. a schema_version the resolver UI renders) can trust every row they read.

    const decision = await Durable.workflow.condition<{ approved: boolean }>(
    'manager-approval',
    {
    role: 'manager',
    type: 'order-approval',
    subtype: 'regional',
    priority: 2,
    description: 'Approve or reject the regional order',
    metadata: { orderId, region },
    envelope: { instructions: 'Review the attached order' },
    timeout: '72h', // SLA: resume with false + expire the row if unresolved
    },
    );
    if (decision === false) return 'auto-rejected-sla'; // row is now status='expired'

    // Elsewhere: list, claim, then resolve (resumes the workflow)
    const [item] = await client.escalations.list({ role: 'manager', status: 'pending' });
    await client.escalations.claim({ id: item.id, assignee: 'alice@company.com' });
    await client.escalations.resolve({ id: item.id, resolverPayload: { approved: true } });

    The resolve/signal delivery pipeline routes to the main flow's waiter. Inside a hook function (execHook), an escalation-bearing wait writes its row and honors timeout (the row expires and the hook resumes with false), while resolution delivery targets the main flow — so structure SLA-gated human waits in the workflow body and let hook functions report back via signal().

    A signal delivered before its condition() registers — a fast signaler, or a payload deposited before the workflow starts — is buffered as a pending signal and delivered when the wait registers. The buffer holds a signal for 10 minutes by default; pass expire to signal() (e.g. '1h', '30d') to hold it longer when signaling early on purpose.

    const [name, score] = await Promise.all([
    Durable.workflow.condition<string>('name-signal'),
    Durable.workflow.condition<number>('score-signal'),
    ]);

    Harvest fan-out scales the same way: open N waits with Promise.all and signal all of them at once. Buffering covers every signal that races ahead of registration, so size fan-out by the pending-signal TTL — how long a racing signal may wait for its condition to register — with no separate bound on the number of concurrent waits.

    const signalId = `result-${Durable.workflow.random()}`;
    await Durable.workflow.hook({
    taskQueue: 'processors',
    workflowName: 'processItem',
    args: [input, signalId],
    });
    return await Durable.workflow.condition<string>(signalId);

    Type Parameters

    • T

      The type of data expected in the signal payload.

    Parameters

    • signalId: string

      A unique signal identifier shared by the sender and receiver.

    • OptionaltimeoutOrConfig: string | ConditionQueueConfig

      Optional timeout string (e.g. '30s', '24h') OR a ConditionQueueConfig that writes one row to public.hmsh_escalations atomically at suspension time. For an escalation-bearing wait with an SLA, set the config's timeout field — the wait arms the same resume timer as the string form, and when the timer wins the escalation row transitions pending → expired so a late resolve fails as already-expired. (expiresAt is display metadata on the row only; it arms nothing.)

    Returns Promise<T | false | null>

    The signal payload, false when a timeout (string form or config.timeout) expired first, or null if the escalation was cancelled via client.escalations.cancel().