Перейти к содержимому
GitHubXDiscord

TriggerCtx

Стабильный · с 0.1.0

Единственный аргумент, который получает каждый обработчик триггера. В нём упакованы матчившееся событие, снепшот условий, прокси действий, DSL check, метаданные рантайма и AbortSignal. Форма полностью выводится из TriggerSchema триггера — деструктурируй только нужные поля.

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 — схема триггера; R — union ключей обязательных условий (required: [...] в конфиге).

Дискриминированный union по S['events']. У каждого варианта { readonly name: K; readonly payload: EventMap<S>[K] }. Сужай через event.name.

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

Снепшот всех условий, снимается один раз в момент срабатывания. Ключи, перечисленные в required, видны как необязательные нет — без undefined; остальные — T | undefined.

Ограничение V1: required не сужает в системе типов — conditions.user остаётся User | undefined, даже если required: ['user']. Используй check.is/all/any или ранний guard if (!conditions.user) return;.

Мапа { [name]?: (payload) => void } плюс три цепочечных таймера:

МетодВозвращаетОписание
actions.<name>?.(payload)voidВызвать действие, если оно зарегистрировано. Optional chain, потому что реактор мог быть не смонтирован.
actions.debounce(ms)та же формаЖдать ms тишины перед срабатыванием.
actions.throttle(ms)та же формаНе более одного срабатывания за окно ms.
actions.defer(ms)та же формаЖдать ms перед срабатыванием — без coalescing.

DSL предикатов. См. createCheck. is, all и any сужают значение через NonNullable внутри предиката.

Метаданные рантайма о текущем запуске:

ПолеТипОписание
runIdstringУникальный id запуска.
triggerIdstringТриггер, который сейчас запущен.
scheduledAtnumberperformance.now() в момент постановки запуска в очередь.
cascadeDepthnumber0 для срабатываний верхнего уровня; n для n-го потомка каскада.
parentRunId?stringУстановлен, если запуск — часть каскада.
parentTriggerId?stringТо же — id upstream-триггера.

Прерывается, когда рантайм отменяет текущий запуск — например, стратегия concurrency take-latest его вытесняет, триггер отключён или утилизирован, либо разрушается сам рантайм. Передавай его в fetch(url, { signal }), addEventListener('…', fn, { signal }) или проверяй signal.aborted после любых async-ожиданий.

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}`);
} }, });