Skip to content
GitHubXDiscord

TriggerCtx

Stable · since 0.1.0

The single argument every trigger handler receives. It bundles the matching event, a conditions snapshot, the actions proxy, the check DSL, runtime metadata and an AbortSignal. The shape is fully derived from the trigger’s TriggerSchema — destructure the fields you need.

import type { TriggerCtx } from '@triggery/core';
type TriggerCtx<S extends TriggerSchema, R extends ConditionKey<S> = never> = {
  readonly event:      EventOf<S>;
  readonly conditions: ConditionsCtx<ConditionMap<S>, R>;
  readonly actions:    ActionsCtx<ActionMap<S>>;
  readonly check:      CheckCtx<ConditionMap<S>>;
  readonly meta:       MetaCtx;
  readonly signal:     AbortSignal;
};

S is the trigger’s schema; R is the union of required condition keys (required: [...] in the config).

Discriminated union over S['events']. Each variant has { readonly name: K; readonly payload: EventMap<S>[K] }. Narrow with event.name.

type EventOf<S> = { name: 'open'; payload: { id: string } }
               | { name: 'close'; payload: { id: string; reason: 'ok' | 'cancel' } };

conditionsConditionsCtx<ConditionMap<S>, R>

Section titled “conditions — ConditionsCtx<ConditionMap<S>, R>”

Snapshot of all conditions, captured once at fire time. Keys listed in required appear as non-optional; others are T | undefined.

V1 limitation: required does not narrow in the type system — conditions.user stays User | undefined even when required: ['user']. Use check.is/all/any or an early if (!conditions.user) return; guard.

Map of { [name]?: (payload) => void } plus three chainable timers:

MethodReturnsDescription
actions.<name>?.(payload)voidInvoke an action if it’s registered. Optional chain because no reactor may be mounted.
actions.debounce(ms)same shapeWait until ms of silence before firing.
actions.throttle(ms)same shapeAt most one fire per ms window.
actions.defer(ms)same shapeWait ms before firing — no coalescing.

The predicate DSL. See createCheck. is, all, and any all NonNullable-narrow the value inside the predicate.

Runtime metadata about the current run:

FieldTypeDescription
runIdstringUnique per-run id.
triggerIdstringThe trigger that’s running.
scheduledAtnumberperformance.now() when the run was enqueued.
cascadeDepthnumber0 for top-level fires; n for the n-th cascade child.
parentRunId?stringSet when the run is part of a cascade.
parentTriggerId?stringSame — id of the upstream trigger.

Aborts when the runtime cancels the current run — e.g. a take-latest concurrency strategy supersedes it, the trigger is disabled or disposed, or the runtime itself is torn down. Pass it into fetch(url, { signal }), addEventListener('…', fn, { signal }), or check signal.aborted after any async wait.

import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
createTrigger
createTrigger<{
    events: {
        ping: void;
    };
    conditions: {
        user: {
            id: string;
        } | null;
    };
    actions: {
        log: string;
    };
}>(config: CreateTriggerConfig<{
    events: {
        ping: void;
    };
    conditions: {
        user: {
            id: string;
        } | null;
    };
    actions: {
        log: string;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        ping: void;
    };
    conditions: {
        user: {
            id: string;
        } | null;
    };
    actions: {
        log: string;
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    ping: void;
}
: { pingping: void: void };
conditions
conditions: {
    user: {
        id: string;
    } | null;
}
: { user
user: {
    id: string;
} | null
: { idid: string: string } | null };
actions
actions: {
    log: string;
}
: { loglog: string: string };
}>({ idid: string: 'demo', eventsevents: readonly "ping"[]: ['ping'], requiredrequired?: readonly "user"[] | undefined
Required condition keys. The trigger will not run unless all of them are registered.
: ['user'],
handler
handler: TriggerHandler<{
    events: {
        ping: void;
    };
    conditions: {
        user: {
            id: string;
        } | null;
    };
    actions: {
        log: string;
    };
}, never>
({ event
event: {
    readonly name: "ping";
    readonly payload: void;
}
, conditions
conditions: ConditionsCtx<{
    user: {
        id: string;
    } | null;
}, never>
, actions
actions: ActionsCtx<{
    log: string;
}>
, check
check: CheckCtx<{
    user: {
        id: string;
    } | null;
}>
, metameta: MetaCtx, signalsignal: AbortSignal }) {
if (signalsignal: AbortSignal.abortedAbortSignal.aborted: boolean
The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (`true`) or not (`false`). [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)
) return;
if (!check
check: CheckCtx<{
    user: {
        id: string;
    } | null;
}>
.is
is<"user">(key: "user", predicate: (value: {
    id: string;
}) => boolean): boolean
('user', u
u: {
    id: string;
}
=> u
u: {
    id: string;
}
.idid: string.lengthString.length: number
Returns the length of a String object.
> 0)) return;
actions
actions: ActionsCtx<{
    log: string;
}>
.loglog?: ((payload: string) => void) | undefined?.(`[run ${metameta: MetaCtx.runIdrunId: string}] ${event
event: {
    readonly name: "ping";
    readonly payload: void;
}
.namename: "ping"}`);
}, });
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
createTrigger
createTrigger<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}>(config: CreateTriggerConfig<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    open: {
        id: string;
    };
    close: {
        id: string;
        reason: "ok" | "cancel";
    };
}
: { open
open: {
    id: string;
}
: { idid: string: string }; close
close: {
    id: string;
    reason: "ok" | "cancel";
}
: { idid: string: string; reasonreason: "ok" | "cancel": 'ok' | 'cancel' } };
}>({ idid: string: 'modal', eventsevents: readonly ("open" | "close")[]: ['open', 'close'], handler
handler: TriggerHandler<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}, never>
({ event
event: EventOf<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}>
}) {
if (event
event: EventOf<{
    events: {
        open: {
            id: string;
        };
        close: {
            id: string;
            reason: "ok" | "cancel";
        };
    };
}>
.namename: "open" | "close" === 'open') {
consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
('opening', event
event: {
    readonly name: "open";
    readonly payload: {
        id: string;
    };
}
.payload
payload: {
    id: string;
}
.idid: string);
} else { consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
('closing', event
event: {
    readonly name: "close";
    readonly payload: {
        id: string;
        reason: "ok" | "cancel";
    };
}
.payload
payload: {
    id: string;
    reason: "ok" | "cancel";
}
.reasonreason: "ok" | "cancel");
} }, });
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
createTrigger
createTrigger<{
    events: {
        fetch: string;
    };
    actions: {
        setData: unknown;
    };
}>(config: CreateTriggerConfig<{
    events: {
        fetch: string;
    };
    actions: {
        setData: unknown;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        fetch: string;
    };
    actions: {
        setData: unknown;
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    fetch: string;
}
: { fetchfetch: string: string };
actions
actions: {
    setData: unknown;
}
: { setDatasetData: unknown: unknown };
}>({ idid: string: 'fetcher', eventsevents: readonly "fetch"[]: ['fetch'], concurrencyconcurrency?: ConcurrencyStrategy | undefined
Concurrency strategy applied across handler runs (default: `'take-latest'`). - `take-latest` — new run aborts the previous (`signal.aborted` becomes true). - `take-every` — every run proceeds independently, no aborts. - `take-first` / `exhaust` — new runs are skipped while one is still in flight. - `queue` — new runs wait for the previous to finish (serialized). - `sync` — like `take-every`; provided as a documentation marker.
: 'take-latest',
async handler
handler: TriggerHandler<{
    events: {
        fetch: string;
    };
    actions: {
        setData: unknown;
    };
}, never>
({ event
event: {
    readonly name: "fetch";
    readonly payload: string;
}
, actions
actions: ActionsCtx<{
    setData: unknown;
}>
, signalsignal: AbortSignal }) {
const resconst res: Response = await fetchfunction fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
(`/api/${event
event: {
    readonly name: "fetch";
    readonly payload: string;
}
.payloadpayload: string}`, { signalRequestInit.signal?: AbortSignal | null | undefined
An AbortSignal to set request's signal.
});
if (signalsignal: AbortSignal.abortedAbortSignal.aborted: boolean
The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (`true`) or not (`false`). [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)
) return;
const dataconst data: any = await resconst res: Response.jsonBody.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
();
actions
actions: ActionsCtx<{
    setData: unknown;
}>
.setDatasetData?: ((payload: unknown) => void) | undefined?.(dataconst data: any);
}, });
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
createTrigger
createTrigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>(config: CreateTriggerConfig<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    keystroke: string;
}
: { keystrokekeystroke: string: string };
actions
actions: {
    search: string;
}
: { searchsearch: string: string };
}>({ idid: string: 'autocomplete', eventsevents: readonly "keystroke"[]: ['keystroke'], handler
handler: TriggerHandler<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}, never>
({ event
event: {
    readonly name: "keystroke";
    readonly payload: string;
}
, actions
actions: ActionsCtx<{
    search: string;
}>
}) {
actions
actions: ActionsCtx<{
    search: string;
}>
.debounce
function debounce(ms: number): ActionsCtx<{
    search: string;
}>
(300).searchsearch?: ((payload: string) => void) | undefined?.(event
event: {
    readonly name: "keystroke";
    readonly payload: string;
}
.payloadpayload: string);
}, });
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
createTrigger
createTrigger<{
    events: {
        "order-placed": {
            id: string;
        };
    };
}>(config: CreateTriggerConfig<{
    events: {
        "order-placed": {
            id: string;
        };
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        "order-placed": {
            id: string;
        };
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{ events
events: {
    'order-placed': {
        id: string;
    };
}
: { 'order-placed': { idid: string: string } } }>({
idid: string: 'order', eventsevents: readonly "order-placed"[]: ['order-placed'], handler
handler: TriggerHandler<{
    events: {
        "order-placed": {
            id: string;
        };
    };
}, never>
({ event
event: {
    readonly name: "order-placed";
    readonly payload: {
        id: string;
    };
}
, metameta: MetaCtx }) {
if (metameta: MetaCtx.cascadeDepthcascadeDepth: number > 0) { consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
(`order ${event
event: {
    readonly name: "order-placed";
    readonly payload: {
        id: string;
    };
}
.payload
payload: {
    id: string;
}
.idid: string} via cascade from ${metameta: MetaCtx.parentTriggerIdparentTriggerId?: string | undefined}`);
} }, });