@triggery/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @triggery/react
2
+
3
+ ## 0.1.0
4
+
5
+ First public preview release.
6
+
7
+ React bindings for Triggery — hook-first orchestration layer
8
+
9
+ See the [repository-level CHANGELOG](../../CHANGELOG.md#010--2026-05-16) for the full set of packages and the umbrella feature list. Future entries on this file are appended automatically by changesets.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aleksey Skhomenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # @triggery/react
2
+
3
+ React bindings for [Triggery](https://github.com/triggeryjs/triggery).
4
+
5
+ Provides:
6
+
7
+ - `useEvent(trigger, 'eventName')` — get a typed event emitter.
8
+ - `useCondition(trigger, 'name', () => value, [deps])` — register pull-based condition data.
9
+ - `useAction(trigger, 'name', handler)` — register an action executor.
10
+ - `useInlineTrigger({ on, do, if?, required? })` — define an inline trigger inside a component.
11
+ - `<TriggerScope id="...">` — isolate registrations to a scope.
12
+ - `<TriggerRuntimeProvider runtime={...}>` — attach a custom runtime.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pnpm add @triggery/react @triggery/core
18
+ ```
19
+
20
+ ## License
21
+
22
+ MIT &copy; Aleksey Skhomenko
@@ -0,0 +1,192 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { Runtime, TriggerSchema, ActionKey, Trigger, ActionMap, ConditionKey, ConditionMap, EventKey, EventMap, TriggerInspectSnapshot, NamedHooks, createTrigger } from '@triggery/core';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+
6
+ /**
7
+ * React context used to inject a custom runtime. When no provider is present,
8
+ * hooks fall back to the global default runtime.
9
+ */
10
+ declare const TriggerRuntimeContext: react.Context<Runtime | undefined>;
11
+ /** Read the active runtime from context, or return the default runtime. */
12
+ declare function useRuntime(): Runtime;
13
+ /**
14
+ * Scope context — string id provided by `<TriggerScope id="…">`. Default is
15
+ * `''` (global). Hooks pass it through to `registerCondition`/`registerAction`
16
+ * so the runtime can match the registration against a trigger's `scope`.
17
+ */
18
+ declare const TriggerScopeContext: react.Context<string>;
19
+ /** Read the active scope from context. `''` means "global / no scope". */
20
+ declare function useScope(): string;
21
+
22
+ /**
23
+ * Return an event emitter. Its identity is stable across renders (`useCallback`
24
+ * under the hood). The trigger must list this event in its `events: [...]` schema
25
+ * passed to `createTrigger`.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * const fireNewMessage = useEvent(messageTrigger, 'new-message');
30
+ * useEffect(() => socket.on('msg', fireNewMessage), [fireNewMessage]);
31
+ * ```
32
+ */
33
+ declare function useEvent<S extends TriggerSchema, K extends EventKey<S>>(_trigger: Trigger<S>, eventName: K): EventMap<S>[K] extends void ? () => void : (payload: EventMap<S>[K]) => void;
34
+ /**
35
+ * Register a condition getter for a trigger. The runtime calls `getter()` at
36
+ * fire-time only — there are no subscriptions and no re-renders.
37
+ *
38
+ * `deps` only invalidate the closure cache (same semantics as `useCallback`).
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * useCondition(messageTrigger, 'user', () => currentUser, [currentUser]);
43
+ * ```
44
+ */
45
+ declare function useCondition<S extends TriggerSchema, K extends ConditionKey<S>>(trigger: Trigger<S>, name: K, getter: () => ConditionMap<S>[K], deps?: readonly unknown[]): void;
46
+ /**
47
+ * Register an action handler for a trigger. The runtime invokes the handler whenever
48
+ * the trigger body calls `actions.<name>(...)`.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * useAction(messageTrigger, 'showToast', (payload) => toast.success(payload.title));
53
+ * ```
54
+ */
55
+ declare function useAction<S extends TriggerSchema, K extends ActionKey<S>>(trigger: Trigger<S>, name: K, handler: ActionMap<S>[K] extends void ? () => void | Promise<void> : (payload: ActionMap<S>[K]) => void | Promise<void>): void;
56
+ /**
57
+ * Return the latest snapshot of a trigger (for in-app debug panels).
58
+ */
59
+ declare function useInspect<S extends TriggerSchema>(trigger: Trigger<S>): TriggerInspectSnapshot | undefined;
60
+ /**
61
+ * Subscribe to the active runtime's inspector buffer and return the most
62
+ * recent `limit` snapshots (newest first). Re-renders whenever a new run is
63
+ * recorded.
64
+ *
65
+ * Use this for in-app devtools panels, custom run lists, or any "show me the
66
+ * last N runs" UI. Combine with `<TriggerSnapshotView>` from
67
+ * `@triggery/devtools-panel` for a turnkey UI.
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * function DebugPanel() {
72
+ * const history = useInspectHistory(50);
73
+ * return <ul>{history.map((s) => <li key={s.runId}>{s.triggerId}: {s.status}</li>)}</ul>;
74
+ * }
75
+ * ```
76
+ */
77
+ declare function useInspectHistory(limit?: number): readonly TriggerInspectSnapshot[];
78
+
79
+ /**
80
+ * Build the named-hooks proxy for a trigger.
81
+ *
82
+ * For a schema with `events: { 'new-message' }`, `conditions: { user }` and
83
+ * `actions: { showToast }` this returns:
84
+ *
85
+ * useNewMessageEvent -> () => fire
86
+ * useUserCondition -> (getter, deps?) => void
87
+ * useShowToastAction -> (handler) => void
88
+ *
89
+ * Names are derived from string keys via `kebab-case -> PascalCase`:
90
+ * - `'new-message'` -> `useNewMessageEvent`
91
+ * - `'user'` -> `useUserCondition`
92
+ * - `'showToast'` -> `useShowToastAction`
93
+ *
94
+ * Use it like:
95
+ *
96
+ * export const { useNewMessageEvent, useUserCondition, useShowToastAction } =
97
+ * createNamedHooks(messageTrigger);
98
+ *
99
+ * Implementation detail: the underlying object is a `Proxy` keyed by the hook
100
+ * name. Each lookup synthesizes a hook that delegates to `useEvent` /
101
+ * `useCondition` / `useAction` with the right port name.
102
+ */
103
+ declare function createNamedHooks<S extends TriggerSchema>(trigger: Trigger<S>): NamedHooks<S>;
104
+
105
+ type TriggerRuntimeProviderProps = {
106
+ readonly runtime: Runtime;
107
+ readonly children: ReactNode;
108
+ };
109
+ /**
110
+ * Provide a custom runtime to all descendants. Without a provider, hooks use the
111
+ * global `defaultRuntime`.
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * const isolated = createRuntime();
116
+ * <TriggerRuntimeProvider runtime={isolated}>
117
+ * <MyApp />
118
+ * </TriggerRuntimeProvider>
119
+ * ```
120
+ */
121
+ declare function TriggerRuntimeProvider({ runtime, children }: TriggerRuntimeProviderProps): react_jsx_runtime.JSX.Element;
122
+
123
+ type TriggerScopeProps = {
124
+ /**
125
+ * Scope id. Triggers created with `scope: <this id>` in their config will see
126
+ * the conditions/actions registered inside this subtree; triggers without a
127
+ * matching scope will not.
128
+ *
129
+ * Nested scopes replace the outer one (last writer wins) — there is no
130
+ * implicit composition.
131
+ */
132
+ readonly id: string;
133
+ readonly children: ReactNode;
134
+ };
135
+ /**
136
+ * Provide a scope id to descendants. Registrations made by `useCondition` /
137
+ * `useAction` inside this subtree are tagged with the scope, and only triggers
138
+ * declared with the same `scope` will receive them.
139
+ *
140
+ * For full isolation (separate inspector / scheduler / middleware), prefer
141
+ * `<TriggerRuntimeProvider>` — `TriggerScope` is a lighter, in-runtime
142
+ * partitioning.
143
+ *
144
+ * @example
145
+ * ```tsx
146
+ * const chatTrigger = createTrigger<...>({
147
+ * id: 'message',
148
+ * scope: 'chat',
149
+ * events: ['new-message'],
150
+ * handler: ({ event, actions }) => actions.showToast?.({ title: event.payload.author }),
151
+ * });
152
+ *
153
+ * // useCondition / useAction inside the scope register with scope='chat'.
154
+ * <TriggerScope id="chat">
155
+ * <UserProvider />
156
+ * <ChatToaster />
157
+ * </TriggerScope>
158
+ * ```
159
+ */
160
+ declare function TriggerScope({ id, children }: TriggerScopeProps): react_jsx_runtime.JSX.Element;
161
+
162
+ type UseInlineTriggerConfig<S extends TriggerSchema> = {
163
+ /** Event name to react to. Required. */
164
+ readonly on: keyof NonNullable<S['events']> & string;
165
+ /** Handler body. Receives the same context as a full `createTrigger` handler. */
166
+ readonly do: Parameters<typeof createTrigger<S>>[0]['handler'];
167
+ /** Optional debug id (defaults to a stable auto-generated one). */
168
+ readonly id?: string;
169
+ };
170
+ /**
171
+ * Define a trigger inline inside a component — an escape hatch for one-off
172
+ * reactions where pulling them into a separate `*.trigger.ts` file would be
173
+ * overkill. The trigger lives for the lifetime of the component (created on
174
+ * mount, disposed on unmount).
175
+ *
176
+ * Use cases: tiny analytics taps, modal-stack coordination scoped to one screen,
177
+ * keyboard shortcuts that only matter while a panel is open.
178
+ *
179
+ * For anything reusable across the app — prefer `createTrigger` in a dedicated
180
+ * `*.trigger.ts` file.
181
+ *
182
+ * @example
183
+ * ```tsx
184
+ * useInlineTrigger<{ events: { 'cta:click': { id: string } } }>({
185
+ * on: 'cta:click',
186
+ * do: ({ event }) => track('cta', event.payload),
187
+ * });
188
+ * ```
189
+ */
190
+ declare function useInlineTrigger<S extends TriggerSchema>(config: UseInlineTriggerConfig<S>): void;
191
+
192
+ export { TriggerRuntimeContext, TriggerRuntimeProvider, type TriggerRuntimeProviderProps, TriggerScope, TriggerScopeContext, type TriggerScopeProps, type UseInlineTriggerConfig, createNamedHooks, useAction, useCondition, useEvent, useInlineTrigger, useInspect, useInspectHistory, useRuntime, useScope };
package/dist/index.js ADDED
@@ -0,0 +1,169 @@
1
+ import { getDefaultRuntime, createTrigger } from '@triggery/core';
2
+ import { createContext, useContext, useCallback, useRef, useEffect, useState, useMemo } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/context.ts
6
+ var TriggerRuntimeContext = createContext(void 0);
7
+ function useRuntime() {
8
+ const ctxRuntime = useContext(TriggerRuntimeContext);
9
+ return ctxRuntime ?? getDefaultRuntime();
10
+ }
11
+ var TriggerScopeContext = createContext("");
12
+ function useScope() {
13
+ return useContext(TriggerScopeContext);
14
+ }
15
+ function useEvent(_trigger, eventName) {
16
+ const runtime = useRuntime();
17
+ return useCallback(
18
+ ((payload) => {
19
+ runtime.fire(eventName, payload);
20
+ }),
21
+ [runtime, eventName]
22
+ );
23
+ }
24
+ function useCondition(trigger, name, getter, deps = []) {
25
+ const runtime = useRuntime();
26
+ const scope = useScope();
27
+ const getterRef = useRef(getter);
28
+ getterRef.current = getter;
29
+ const stableGetter = useCallback(() => getterRef.current(), deps);
30
+ useEffect(() => {
31
+ const token = runtime.registerCondition(trigger.id, name, stableGetter, { scope });
32
+ return () => token.unregister();
33
+ }, [runtime, trigger.id, name, stableGetter, scope]);
34
+ }
35
+ function useAction(trigger, name, handler) {
36
+ const runtime = useRuntime();
37
+ const scope = useScope();
38
+ const handlerRef = useRef(handler);
39
+ handlerRef.current = handler;
40
+ useEffect(() => {
41
+ const token = runtime.registerAction(
42
+ trigger.id,
43
+ name,
44
+ (payload) => handlerRef.current(payload),
45
+ { scope }
46
+ );
47
+ return () => token.unregister();
48
+ }, [runtime, trigger.id, name, scope]);
49
+ }
50
+ function useInspect(trigger) {
51
+ return trigger.inspect();
52
+ }
53
+ function useInspectHistory(limit = 20) {
54
+ const runtime = useRuntime();
55
+ const [history, setHistory] = useState(
56
+ () => runtime.getInspectorBuffer().slice(0, limit)
57
+ );
58
+ useEffect(() => {
59
+ setHistory(runtime.getInspectorBuffer().slice(0, limit));
60
+ const token = runtime.subscribe(() => {
61
+ setHistory(runtime.getInspectorBuffer().slice(0, limit));
62
+ });
63
+ return () => token.unregister();
64
+ }, [runtime, limit]);
65
+ return history;
66
+ }
67
+
68
+ // src/namedHooks.ts
69
+ function createNamedHooks(trigger) {
70
+ const cache = /* @__PURE__ */ new Map();
71
+ return new Proxy({}, {
72
+ get(_target, prop) {
73
+ if (typeof prop !== "string") return void 0;
74
+ const cached = cache.get(prop);
75
+ if (cached) return cached;
76
+ const parsed = parseHookName(prop);
77
+ if (!parsed) return void 0;
78
+ const { kind, portName } = parsed;
79
+ let hook;
80
+ if (kind === "event") {
81
+ hook = () => (
82
+ // biome-ignore lint/suspicious/noExplicitAny: passthrough
83
+ useEvent(trigger, portName)
84
+ );
85
+ } else if (kind === "condition") {
86
+ hook = (getter, deps) => (
87
+ // biome-ignore lint/suspicious/noExplicitAny: passthrough
88
+ useCondition(trigger, portName, getter, deps)
89
+ );
90
+ } else {
91
+ hook = (handler) => (
92
+ // biome-ignore lint/suspicious/noExplicitAny: passthrough
93
+ useAction(trigger, portName, handler)
94
+ );
95
+ }
96
+ cache.set(prop, hook);
97
+ return hook;
98
+ },
99
+ has(_target, prop) {
100
+ return typeof prop === "string" && parseHookName(prop) !== null;
101
+ }
102
+ });
103
+ }
104
+ var SUFFIXES = {
105
+ event: "Event",
106
+ condition: "Condition",
107
+ action: "Action"
108
+ };
109
+ function parseHookName(hookName) {
110
+ if (!hookName.startsWith("use")) return null;
111
+ for (const [kind, suffix] of Object.entries(SUFFIXES)) {
112
+ if (!hookName.endsWith(suffix)) continue;
113
+ const middle = hookName.slice(3, hookName.length - suffix.length);
114
+ if (middle.length === 0) continue;
115
+ return { kind, portName: pascalToCamel(middle) };
116
+ }
117
+ return null;
118
+ }
119
+ function pascalToCamel(s) {
120
+ if (s.length === 0) return s;
121
+ return s.charAt(0).toLowerCase() + s.slice(1);
122
+ }
123
+ function TriggerRuntimeProvider({ runtime, children }) {
124
+ return /* @__PURE__ */ jsx(TriggerRuntimeContext.Provider, { value: runtime, children });
125
+ }
126
+ function TriggerScope({ id, children }) {
127
+ return /* @__PURE__ */ jsx(TriggerScopeContext.Provider, { value: id, children });
128
+ }
129
+ var inlineCounter = 0;
130
+ function useInlineTrigger(config) {
131
+ const runtime = useRuntime();
132
+ const scope = useScope();
133
+ const idRef = useRef(void 0);
134
+ if (idRef.current === void 0) {
135
+ idRef.current = config.id ?? `inline:${(++inlineCounter).toString(36)}`;
136
+ }
137
+ const handlerRef = useRef(config.do);
138
+ handlerRef.current = config.do;
139
+ const stableHandler = useMemo(
140
+ () => ((ctx) => handlerRef.current(ctx)),
141
+ []
142
+ );
143
+ const eventNameRef = useRef(config.on);
144
+ if (eventNameRef.current !== config.on) {
145
+ const env = globalThis.process?.env?.NODE_ENV;
146
+ if (env !== "production") {
147
+ console.warn(
148
+ `[triggery] useInlineTrigger(${idRef.current}): "on" must be stable between renders (got "${String(config.on)}" but was "${String(eventNameRef.current)}")`
149
+ );
150
+ }
151
+ }
152
+ useEffect(() => {
153
+ const trigger = createTrigger(
154
+ {
155
+ id: idRef.current,
156
+ // biome-ignore lint/suspicious/noExplicitAny: inline trigger has runtime-only schema
157
+ events: [eventNameRef.current],
158
+ handler: stableHandler,
159
+ ...scope !== "" && { scope }
160
+ },
161
+ runtime
162
+ );
163
+ return () => trigger.dispose();
164
+ }, [runtime, stableHandler, scope]);
165
+ }
166
+
167
+ export { TriggerRuntimeContext, TriggerRuntimeProvider, TriggerScope, TriggerScopeContext, createNamedHooks, useAction, useCondition, useEvent, useInlineTrigger, useInspect, useInspectHistory, useRuntime, useScope };
168
+ //# sourceMappingURL=index.js.map
169
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context.ts","../src/hooks.ts","../src/namedHooks.ts","../src/TriggerRuntimeProvider.tsx","../src/TriggerScope.tsx","../src/useInlineTrigger.ts"],"names":["jsx","useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { getDefaultRuntime, type Runtime } from '@triggery/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * React context used to inject a custom runtime. When no provider is present,\n * hooks fall back to the global default runtime.\n */\nexport const TriggerRuntimeContext = createContext<Runtime | undefined>(undefined);\n\n/** Read the active runtime from context, or return the default runtime. */\nexport function useRuntime(): Runtime {\n const ctxRuntime = useContext(TriggerRuntimeContext);\n return ctxRuntime ?? getDefaultRuntime();\n}\n\n/**\n * Scope context — string id provided by `<TriggerScope id=\"…\">`. Default is\n * `''` (global). Hooks pass it through to `registerCondition`/`registerAction`\n * so the runtime can match the registration against a trigger's `scope`.\n */\nexport const TriggerScopeContext = createContext<string>('');\n\n/** Read the active scope from context. `''` means \"global / no scope\". */\nexport function useScope(): string {\n return useContext(TriggerScopeContext);\n}\n","import type {\n ActionKey,\n ActionMap,\n ConditionKey,\n ConditionMap,\n EventKey,\n EventMap,\n Trigger,\n TriggerInspectSnapshot,\n TriggerSchema,\n} from '@triggery/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useRuntime, useScope } from './context.ts';\n\n/**\n * Return an event emitter. Its identity is stable across renders (`useCallback`\n * under the hood). The trigger must list this event in its `events: [...]` schema\n * passed to `createTrigger`.\n *\n * @example\n * ```tsx\n * const fireNewMessage = useEvent(messageTrigger, 'new-message');\n * useEffect(() => socket.on('msg', fireNewMessage), [fireNewMessage]);\n * ```\n */\nexport function useEvent<S extends TriggerSchema, K extends EventKey<S>>(\n _trigger: Trigger<S>,\n eventName: K,\n): EventMap<S>[K] extends void ? () => void : (payload: EventMap<S>[K]) => void {\n const runtime = useRuntime();\n return useCallback(\n ((payload?: unknown) => {\n runtime.fire(eventName, payload);\n }) as EventMap<S>[K] extends void ? () => void : (payload: EventMap<S>[K]) => void,\n [runtime, eventName],\n );\n}\n\n/**\n * Register a condition getter for a trigger. The runtime calls `getter()` at\n * fire-time only — there are no subscriptions and no re-renders.\n *\n * `deps` only invalidate the closure cache (same semantics as `useCallback`).\n *\n * @example\n * ```tsx\n * useCondition(messageTrigger, 'user', () => currentUser, [currentUser]);\n * ```\n */\nexport function useCondition<S extends TriggerSchema, K extends ConditionKey<S>>(\n trigger: Trigger<S>,\n name: K,\n getter: () => ConditionMap<S>[K],\n deps: readonly unknown[] = [],\n): void {\n const runtime = useRuntime();\n const scope = useScope();\n // Keep the latest getter in a ref — the runtime reads through a stable wrapper.\n const getterRef = useRef(getter);\n getterRef.current = getter;\n // biome-ignore lint/correctness/useExhaustiveDependencies: deps managed by caller\n const stableGetter = useCallback(() => getterRef.current(), deps);\n\n useEffect(() => {\n const token = runtime.registerCondition(trigger.id, name, stableGetter, { scope });\n return () => token.unregister();\n }, [runtime, trigger.id, name, stableGetter, scope]);\n}\n\n/**\n * Register an action handler for a trigger. The runtime invokes the handler whenever\n * the trigger body calls `actions.<name>(...)`.\n *\n * @example\n * ```tsx\n * useAction(messageTrigger, 'showToast', (payload) => toast.success(payload.title));\n * ```\n */\nexport function useAction<S extends TriggerSchema, K extends ActionKey<S>>(\n trigger: Trigger<S>,\n name: K,\n handler: ActionMap<S>[K] extends void\n ? () => void | Promise<void>\n : (payload: ActionMap<S>[K]) => void | Promise<void>,\n): void {\n const runtime = useRuntime();\n const scope = useScope();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const token = runtime.registerAction(\n trigger.id,\n name,\n (payload) => (handlerRef.current as (payload: unknown) => void | Promise<void>)(payload),\n { scope },\n );\n return () => token.unregister();\n }, [runtime, trigger.id, name, scope]);\n}\n\n/**\n * Return the latest snapshot of a trigger (for in-app debug panels).\n */\nexport function useInspect<S extends TriggerSchema>(trigger: Trigger<S>) {\n return trigger.inspect();\n}\n\n/**\n * Subscribe to the active runtime's inspector buffer and return the most\n * recent `limit` snapshots (newest first). Re-renders whenever a new run is\n * recorded.\n *\n * Use this for in-app devtools panels, custom run lists, or any \"show me the\n * last N runs\" UI. Combine with `<TriggerSnapshotView>` from\n * `@triggery/devtools-panel` for a turnkey UI.\n *\n * @example\n * ```tsx\n * function DebugPanel() {\n * const history = useInspectHistory(50);\n * return <ul>{history.map((s) => <li key={s.runId}>{s.triggerId}: {s.status}</li>)}</ul>;\n * }\n * ```\n */\nexport function useInspectHistory(limit = 20): readonly TriggerInspectSnapshot[] {\n const runtime = useRuntime();\n const [history, setHistory] = useState<readonly TriggerInspectSnapshot[]>(() =>\n runtime.getInspectorBuffer().slice(0, limit),\n );\n\n useEffect(() => {\n // Re-seed on mount in case fires happened between the initial render and\n // the effect (the runtime may have new entries already).\n setHistory(runtime.getInspectorBuffer().slice(0, limit));\n const token = runtime.subscribe(() => {\n setHistory(runtime.getInspectorBuffer().slice(0, limit));\n });\n return () => token.unregister();\n }, [runtime, limit]);\n\n return history;\n}\n","import type { NamedHooks, Trigger, TriggerSchema } from '@triggery/core';\nimport { useAction, useCondition, useEvent } from './hooks.ts';\n\n/**\n * Build the named-hooks proxy for a trigger.\n *\n * For a schema with `events: { 'new-message' }`, `conditions: { user }` and\n * `actions: { showToast }` this returns:\n *\n * useNewMessageEvent -> () => fire\n * useUserCondition -> (getter, deps?) => void\n * useShowToastAction -> (handler) => void\n *\n * Names are derived from string keys via `kebab-case -> PascalCase`:\n * - `'new-message'` -> `useNewMessageEvent`\n * - `'user'` -> `useUserCondition`\n * - `'showToast'` -> `useShowToastAction`\n *\n * Use it like:\n *\n * export const { useNewMessageEvent, useUserCondition, useShowToastAction } =\n * createNamedHooks(messageTrigger);\n *\n * Implementation detail: the underlying object is a `Proxy` keyed by the hook\n * name. Each lookup synthesizes a hook that delegates to `useEvent` /\n * `useCondition` / `useAction` with the right port name.\n */\nexport function createNamedHooks<S extends TriggerSchema>(trigger: Trigger<S>): NamedHooks<S> {\n // Cache hooks per identifier so React doesn't see a new function reference\n // every render (which would break hook-rules + cause unnecessary churn).\n const cache = new Map<string, unknown>();\n\n return new Proxy({} as NamedHooks<S>, {\n get(_target, prop) {\n if (typeof prop !== 'string') return undefined;\n const cached = cache.get(prop);\n if (cached) return cached;\n\n const parsed = parseHookName(prop);\n if (!parsed) return undefined;\n\n const { kind, portName } = parsed;\n // biome-ignore lint/suspicious/noExplicitAny: bridge layer\n let hook: any;\n if (kind === 'event') {\n hook = () =>\n // biome-ignore lint/suspicious/noExplicitAny: passthrough\n useEvent(trigger as Trigger<S>, portName as any);\n } else if (kind === 'condition') {\n hook = (\n // biome-ignore lint/suspicious/noExplicitAny: passthrough\n getter: any,\n deps?: readonly unknown[],\n ) =>\n // biome-ignore lint/suspicious/noExplicitAny: passthrough\n useCondition(trigger as Trigger<S>, portName as any, getter, deps);\n } else {\n // biome-ignore lint/suspicious/noExplicitAny: passthrough\n hook = (handler: any) =>\n // biome-ignore lint/suspicious/noExplicitAny: passthrough\n useAction(trigger as Trigger<S>, portName as any, handler);\n }\n cache.set(prop, hook);\n return hook;\n },\n has(_target, prop) {\n return typeof prop === 'string' && parseHookName(prop) !== null;\n },\n });\n}\n\ntype ParsedHook = {\n kind: 'event' | 'condition' | 'action';\n /** Port name back in original camelCase/kebab-case form (best-effort). */\n portName: string;\n};\n\nconst SUFFIXES = {\n event: 'Event',\n condition: 'Condition',\n action: 'Action',\n} as const;\n\nfunction parseHookName(hookName: string): ParsedHook | null {\n if (!hookName.startsWith('use')) return null;\n for (const [kind, suffix] of Object.entries(SUFFIXES) as [\n keyof typeof SUFFIXES,\n (typeof SUFFIXES)[keyof typeof SUFFIXES],\n ][]) {\n if (!hookName.endsWith(suffix)) continue;\n const middle = hookName.slice(3, hookName.length - suffix.length);\n if (middle.length === 0) continue;\n return { kind, portName: pascalToCamel(middle) };\n }\n return null;\n}\n\n/**\n * Convert PascalCase back to camelCase. We don't try to round-trip kebab-case\n * (`'new-message'` -> `'newMessage'`) because keys in `createTrigger` schemas\n * can be either form, and the runtime stores them as the original string.\n *\n * For kebab-case keys, the user can fall back to the universal `useEvent(t, 'kebab-key')`\n * hook — named-hooks support is best-effort for camelCase keys.\n */\nfunction pascalToCamel(s: string): string {\n if (s.length === 0) return s;\n return s.charAt(0).toLowerCase() + s.slice(1);\n}\n","import type { Runtime } from '@triggery/core';\nimport type { ReactNode } from 'react';\nimport { TriggerRuntimeContext } from './context.ts';\n\nexport type TriggerRuntimeProviderProps = {\n readonly runtime: Runtime;\n readonly children: ReactNode;\n};\n\n/**\n * Provide a custom runtime to all descendants. Without a provider, hooks use the\n * global `defaultRuntime`.\n *\n * @example\n * ```tsx\n * const isolated = createRuntime();\n * <TriggerRuntimeProvider runtime={isolated}>\n * <MyApp />\n * </TriggerRuntimeProvider>\n * ```\n */\nexport function TriggerRuntimeProvider({ runtime, children }: TriggerRuntimeProviderProps) {\n return (\n <TriggerRuntimeContext.Provider value={runtime}>{children}</TriggerRuntimeContext.Provider>\n );\n}\n","import type { ReactNode } from 'react';\nimport { TriggerScopeContext } from './context.ts';\n\nexport type TriggerScopeProps = {\n /**\n * Scope id. Triggers created with `scope: <this id>` in their config will see\n * the conditions/actions registered inside this subtree; triggers without a\n * matching scope will not.\n *\n * Nested scopes replace the outer one (last writer wins) — there is no\n * implicit composition.\n */\n readonly id: string;\n readonly children: ReactNode;\n};\n\n/**\n * Provide a scope id to descendants. Registrations made by `useCondition` /\n * `useAction` inside this subtree are tagged with the scope, and only triggers\n * declared with the same `scope` will receive them.\n *\n * For full isolation (separate inspector / scheduler / middleware), prefer\n * `<TriggerRuntimeProvider>` — `TriggerScope` is a lighter, in-runtime\n * partitioning.\n *\n * @example\n * ```tsx\n * const chatTrigger = createTrigger<...>({\n * id: 'message',\n * scope: 'chat',\n * events: ['new-message'],\n * handler: ({ event, actions }) => actions.showToast?.({ title: event.payload.author }),\n * });\n *\n * // useCondition / useAction inside the scope register with scope='chat'.\n * <TriggerScope id=\"chat\">\n * <UserProvider />\n * <ChatToaster />\n * </TriggerScope>\n * ```\n */\nexport function TriggerScope({ id, children }: TriggerScopeProps) {\n return <TriggerScopeContext.Provider value={id}>{children}</TriggerScopeContext.Provider>;\n}\n","import { createTrigger, type Trigger, type TriggerSchema } from '@triggery/core';\nimport { useEffect, useMemo, useRef } from 'react';\nimport { useRuntime, useScope } from './context.ts';\n\nlet inlineCounter = 0;\n\nexport type UseInlineTriggerConfig<S extends TriggerSchema> = {\n /** Event name to react to. Required. */\n readonly on: keyof NonNullable<S['events']> & string;\n /** Handler body. Receives the same context as a full `createTrigger` handler. */\n readonly do: Parameters<typeof createTrigger<S>>[0]['handler'];\n /** Optional debug id (defaults to a stable auto-generated one). */\n readonly id?: string;\n};\n\n/**\n * Define a trigger inline inside a component — an escape hatch for one-off\n * reactions where pulling them into a separate `*.trigger.ts` file would be\n * overkill. The trigger lives for the lifetime of the component (created on\n * mount, disposed on unmount).\n *\n * Use cases: tiny analytics taps, modal-stack coordination scoped to one screen,\n * keyboard shortcuts that only matter while a panel is open.\n *\n * For anything reusable across the app — prefer `createTrigger` in a dedicated\n * `*.trigger.ts` file.\n *\n * @example\n * ```tsx\n * useInlineTrigger<{ events: { 'cta:click': { id: string } } }>({\n * on: 'cta:click',\n * do: ({ event }) => track('cta', event.payload),\n * });\n * ```\n */\nexport function useInlineTrigger<S extends TriggerSchema>(config: UseInlineTriggerConfig<S>): void {\n const runtime = useRuntime();\n const scope = useScope();\n // Stable id — auto-generated unless the caller pinned one.\n const idRef = useRef<string | undefined>(undefined);\n if (idRef.current === undefined) {\n idRef.current = config.id ?? `inline:${(++inlineCounter).toString(36)}`;\n }\n\n // Keep the latest handler in a ref so the runtime always calls the freshest\n // body (closure capture without re-creating the trigger on every render).\n const handlerRef = useRef(config.do);\n handlerRef.current = config.do;\n\n const stableHandler = useMemo(\n () => ((ctx: Parameters<typeof config.do>[0]) => handlerRef.current(ctx)) as typeof config.do,\n [],\n );\n\n // Capture event-name at first render — runtime needs the index built upfront.\n const eventNameRef = useRef(config.on);\n if (eventNameRef.current !== config.on) {\n // DEV-only sanity: changing `on` between renders is a misuse.\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n if (env !== 'production') {\n // eslint-disable-next-line no-console -- DEV warn\n console.warn(\n `[triggery] useInlineTrigger(${idRef.current}): \"on\" must be stable between renders ` +\n `(got \"${String(config.on)}\" but was \"${String(eventNameRef.current)}\")`,\n );\n }\n }\n\n useEffect(() => {\n const trigger = createTrigger<S>(\n {\n id: idRef.current as string,\n // biome-ignore lint/suspicious/noExplicitAny: inline trigger has runtime-only schema\n events: [eventNameRef.current] as any,\n handler: stableHandler,\n ...(scope !== '' && { scope }),\n },\n runtime,\n ) as Trigger<S>;\n return () => trigger.dispose();\n }, [runtime, stableHandler, scope]);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@triggery/react",
3
+ "version": "0.1.0",
4
+ "description": "React bindings for Triggery — hook-first orchestration layer",
5
+ "license": "MIT",
6
+ "author": "Aleksey Skhomenko",
7
+ "homepage": "https://triggeryjs.github.io/triggery",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/triggeryjs/triggery.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "bugs": "https://github.com/triggeryjs/triggery/issues",
14
+ "funding": [
15
+ {
16
+ "type": "patreon",
17
+ "url": "https://www.patreon.com/triggery"
18
+ },
19
+ {
20
+ "type": "boosty",
21
+ "url": "https://boosty.to/triggery"
22
+ }
23
+ ],
24
+ "keywords": [
25
+ "react",
26
+ "triggery",
27
+ "hooks",
28
+ "orchestration",
29
+ "business-logic"
30
+ ],
31
+ "type": "module",
32
+ "main": "./dist/index.js",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "source": "./src/index.ts",
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./package.json": "./package.json"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "README.md",
47
+ "LICENSE",
48
+ "CHANGELOG.md"
49
+ ],
50
+ "sideEffects": false,
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "peerDependencies": {
55
+ "react": ">=18.0.0",
56
+ "@triggery/core": "0.1.0"
57
+ },
58
+ "devDependencies": {
59
+ "@testing-library/react": "^16.3.2",
60
+ "@types/react": "^19.2.14",
61
+ "happy-dom": "^20.9.0",
62
+ "react": "^19.2.6",
63
+ "react-dom": "^19.2.6",
64
+ "tsup": "^8.5.1",
65
+ "typescript": "^6.0.3",
66
+ "vitest": "^4.1.6",
67
+ "@triggery/core": "0.1.0"
68
+ },
69
+ "scripts": {
70
+ "build": "tsup",
71
+ "dev": "tsup --watch",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest",
74
+ "test:coverage": "vitest run --coverage",
75
+ "clean": "rm -rf dist *.tsbuildinfo"
76
+ }
77
+ }