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

TriggerSchema

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

TriggerSchema — единственный обобщённый параметр, который передают в createTrigger. Он описывает полную типизированную поверхность триггера: каждое событие, на которое тот реагирует, каждое условие, которое он умеет читать, и каждое действие, которое он может вызвать. Все три ключа необязательные; пустая схема ({}) корректна для триггеров без входов и выходов.

import type { TriggerSchema } from '@triggery/core';
type TriggerSchema = {
  events?:     Record<string, unknown>;  // payload type per event name
  conditions?: Record<string, unknown>;  // value type per condition name
  actions?:    Record<string, unknown>;  // payload type per action name
};
ПолеТипОбязательноеОписание
eventsRecord<string, unknown>нетМапа eventName → payloadType. Для событий без полезной нагрузки используй void.
conditionsRecord<string, unknown>нетМапа conditionName → valueType. Значения тянутся в pull-режиме (рантайм вызывает геттер в момент срабатывания).
actionsRecord<string, unknown>нетМапа actionName → payloadType. Для действий без параметров используй void.

Библиотека не навязывает конвенцию кейзинга — любая строка допустима как ключ. Распространённая практика в экосистеме Triggery:

  • События: глаголы в kebab-case с доменным префиксом — 'new-message', 'cta:click', 'session-expired'.
  • Условия: существительные в camelCase — user, activeChannelId, featureFlags.
  • Действия: глаголы в camelCase — showToast, playSound, setUser.

Это хорошо сочетается с хелпером createNamedHooks, который конвертирует kebab/camel-ключи в PascalCase-имена хуков ('new-message'useNewMessageEvent).

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';
createTriggercreateTrigger<{}>(config: CreateTriggerConfig<{}>, runtime?: Runtime): Trigger<{}>
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 }); }, }); ```
<{}>({
idid: string: 'demo:empty', eventsevents: readonly string[]: [], handlerhandler: TriggerHandler<{}, never>() { // No events, conditions or actions — useful for triggers driven by side // channels (timers, external callbacks) where the dispatch keeps a heartbeat. }, });
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: {
        tick: number;
        tock: void;
    };
}>(config: CreateTriggerConfig<{
    events: {
        tick: number;
        tock: void;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        tick: number;
        tock: void;
    };
}>
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: {
    tick: number;
    tock: void;
}
: { ticktick: number: number; tocktock: void: void };
}>({ idid: string: 'clock', eventsevents: readonly ("tick" | "tock")[]: ['tick', 'tock'], handler
handler: TriggerHandler<{
    events: {
        tick: number;
        tock: void;
    };
}, never>
({ event
event: EventOf<{
    events: {
        tick: number;
        tock: void;
    };
}>
}) {
if (event
event: EventOf<{
    events: {
        tick: number;
        tock: void;
    };
}>
.namename: "tick" | "tock" === 'tick') {
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)
('tick payload', event
event: {
    readonly name: "tick";
    readonly payload: number;
}
.payloadpayload: number); // typed number
} 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)
('tock'); // typed void
} }, });
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';
type User
type User = {
    id: string;
    name: string;
}
= { idid: string: string; namename: string: string };
createTrigger
createTrigger<{
    events: {
        "new-message": {
            text: string;
            channelId: string;
        };
    };
    conditions: {
        user: User | null;
        activeChannelId: string | null;
    };
    actions: {
        showToast: {
            title: string;
            body: string;
        };
        playSound: "beep" | "chime";
    };
}>(config: CreateTriggerConfig<{
    events: {
        "new-message": {
            text: string;
            channelId: string;
        };
    };
    conditions: {
        user: User | null;
        activeChannelId: string | null;
    };
    actions: {
        showToast: {
            title: string;
            body: string;
        };
        playSound: "beep" | "chime";
    };
}>, runtime?: Runtime): Trigger<...>
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: {
    'new-message': {
        text: string;
        channelId: string;
    };
}
: { 'new-message': { texttext: string: string; channelIdchannelId: string: string } };
conditions
conditions: {
    user: User | null;
    activeChannelId: string | null;
}
: { useruser: User | null: User
type User = {
    id: string;
    name: string;
}
| null; activeChannelIdactiveChannelId: string | null: string | null };
actions
actions: {
    showToast: {
        title: string;
        body: string;
    };
    playSound: "beep" | "chime";
}
: { showToast
showToast: {
    title: string;
    body: string;
}
: { titletitle: string: string; bodybody: string: string }; playSoundplaySound: "beep" | "chime": 'beep' | 'chime' };
}>({ idid: string: 'message-received', eventsevents: readonly "new-message"[]: ['new-message'], requiredrequired?: readonly ("user" | "activeChannelId")[] | undefined
Required condition keys. The trigger will not run unless all of them are registered.
: ['user'],
handler
handler: TriggerHandler<{
    events: {
        "new-message": {
            text: string;
            channelId: string;
        };
    };
    conditions: {
        user: User | null;
        activeChannelId: string | null;
    };
    actions: {
        showToast: {
            title: string;
            body: string;
        };
        playSound: "beep" | "chime";
    };
}, never>
({ event
event: {
    readonly name: "new-message";
    readonly payload: {
        text: string;
        channelId: string;
    };
}
, conditions
conditions: ConditionsCtx<{
    user: User | null;
    activeChannelId: string | null;
}, never>
, actions
actions: ActionsCtx<{
    showToast: {
        title: string;
        body: string;
    };
    playSound: "beep" | "chime";
}>
, check
check: CheckCtx<{
    user: User | null;
    activeChannelId: string | null;
}>
}) {
if (event
event: {
    readonly name: "new-message";
    readonly payload: {
        text: string;
        channelId: string;
    };
}
.payload
payload: {
    text: string;
    channelId: string;
}
.channelIdchannelId: string === conditions
conditions: ConditionsCtx<{
    user: User | null;
    activeChannelId: string | null;
}, never>
.activeChannelIdactiveChannelId?: string | null | undefined) return;
actions
actions: ActionsCtx<{
    showToast: {
        title: string;
        body: string;
    };
    playSound: "beep" | "chime";
}>
.showToast
showToast?: ((payload: {
    title: string;
    body: string;
}) => void) | undefined
?.({
titletitle: string: conditions
conditions: ConditionsCtx<{
    user: User | null;
    activeChannelId: string | null;
}, never>
.useruser?: User | null | undefined?.namename: string | undefined ?? '',
bodybody: string: event
event: {
    readonly name: "new-message";
    readonly payload: {
        text: string;
        channelId: string;
    };
}
.payload
payload: {
    text: string;
    channelId: string;
}
.texttext: string,
}); actions
actions: ActionsCtx<{
    showToast: {
        title: string;
        body: string;
    };
    playSound: "beep" | "chime";
}>
.playSoundplaySound?: ((payload: "beep" | "chime") => void) | undefined?.('beep');
}, });

Поле event в контексте обработчика — дискриминированный union по S['events']:

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" === 'close') {
// narrowed: event.payload is { id; reason } here 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)
('closed', event
event: {
    readonly name: "close";
    readonly payload: {
        id: string;
        reason: "ok" | "cancel";
    };
}
.payload
payload: {
    id: string;
    reason: "ok" | "cancel";
}
.reasonreason: "ok" | "cancel");
} }, });