@triggery/signals 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/signals
2
+
3
+ ## 0.1.0
4
+
5
+ First public preview release.
6
+
7
+ Signals adapter for Triggery — read a signal's value from a trigger condition without engaging signal-tracking on the host component
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,71 @@
1
+ # @triggery/signals
2
+
3
+ Read a signal from a Triggery condition without engaging signal-tracking on the host component. Compatible with:
4
+
5
+ * [`@preact/signals-core`](https://github.com/preactjs/signals)
6
+ * [`alien-signals`](https://github.com/stackblitz/alien-signals)
7
+ * Any structurally-similar signal exposing `peek()` or `.value`
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @triggery/core @triggery/react @triggery/signals
13
+ # + the signal library of your choice, e.g.
14
+ pnpm add @preact/signals-core
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```tsx
20
+ import { signal } from '@preact/signals-core';
21
+ import { createTrigger } from '@triggery/core';
22
+ import { useSignalCondition } from '@triggery/signals';
23
+
24
+ const settings = signal({ sound: true, notifications: true });
25
+
26
+ const messageTrigger = createTrigger<{
27
+ events: { 'new-message': { text: string } };
28
+ conditions: { settings: { sound: boolean; notifications: boolean } };
29
+ actions: { showToast: { body: string } };
30
+ }>({
31
+ id: 'message-received',
32
+ events: ['new-message'],
33
+ required: ['settings'],
34
+ handler({ event, conditions, actions }) {
35
+ if (!conditions.settings.notifications) return;
36
+ actions.showToast?.({ body: event.payload.text });
37
+ },
38
+ });
39
+
40
+ function SettingsBridge() {
41
+ useSignalCondition(messageTrigger, 'settings', settings);
42
+ return null;
43
+ }
44
+ ```
45
+
46
+ With a selector:
47
+
48
+ ```ts
49
+ useSignalCondition(messageTrigger, 'settings', profile, (p) => p.settings);
50
+ ```
51
+
52
+ ## How it works
53
+
54
+ Pull-only: the signal is read **only** when a trigger fires. The adapter prefers `peek()` (no dependency tracking) and falls back to `.value` if `peek` is missing. Either way, no subscriber is registered against the signal, so the host component never re-renders on signal updates.
55
+
56
+ If a component also needs to render the signal, use `useSignal()` from `@preact/signals-react` or the equivalent in your signal library — the two paths are orthogonal.
57
+
58
+ ## API
59
+
60
+ ```ts
61
+ useSignalCondition<V, S, K>(
62
+ trigger: Trigger<S>,
63
+ name: K,
64
+ signal: { peek?(): V; readonly value?: V },
65
+ selector?: (value: V) => ConditionMap<S>[K],
66
+ ): void
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,42 @@
1
+ import { TriggerSchema, ConditionKey, Trigger, ConditionMap } from '@triggery/core';
2
+
3
+ /**
4
+ * Structural shape that matches every signal flavour we know about: a `peek()`
5
+ * method (preferred — never engages dependency tracking) plus a `.value`
6
+ * accessor (used as a fallback).
7
+ *
8
+ * - `@preact/signals-core` ✓ has both `peek()` and `.value`
9
+ * - `alien-signals` ✓ uses `.peek()` / `.value`
10
+ * - any TC39-style Signal ✓ if the `peek` method is present
11
+ */
12
+ interface SignalLike<V> {
13
+ peek?(): V;
14
+ readonly value?: V;
15
+ }
16
+ /**
17
+ * Wire a signal into a Triggery condition.
18
+ *
19
+ * The runtime is pull-only — the signal is read **only** when a trigger
20
+ * fires, via `peek()` so no dependency tracking is engaged. The host
21
+ * component is never subscribed to the signal.
22
+ *
23
+ * If a component also needs to render the signal, use the signal library's
24
+ * own integration (`useSignal()`, `<Signal />`, etc.) — the two paths are
25
+ * orthogonal.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * import { signal } from '@preact/signals-core';
30
+ * import { useSignalCondition } from '@triggery/signals';
31
+ *
32
+ * const settings = signal({ sound: true, notifications: true });
33
+ *
34
+ * function SettingsBridge() {
35
+ * useSignalCondition(messageTrigger, 'settings', settings);
36
+ * return null;
37
+ * }
38
+ * ```
39
+ */
40
+ declare function useSignalCondition<V, S extends TriggerSchema, K extends ConditionKey<S>>(trigger: Trigger<S>, name: K, signal: SignalLike<V>, selector?: (value: V) => ConditionMap<S>[K]): void;
41
+
42
+ export { type SignalLike, useSignalCondition };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import { useCondition } from '@triggery/react';
2
+
3
+ // src/index.ts
4
+ function readSignal(signal) {
5
+ if (typeof signal.peek === "function") return signal.peek();
6
+ return signal.value;
7
+ }
8
+ function useSignalCondition(trigger, name, signal, selector) {
9
+ useCondition(
10
+ trigger,
11
+ name,
12
+ () => {
13
+ const value = readSignal(signal);
14
+ return selector ? selector(value) : value;
15
+ },
16
+ [signal, selector]
17
+ );
18
+ }
19
+
20
+ export { useSignalCondition };
21
+ //# sourceMappingURL=index.js.map
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAqBA,SAAS,WAAc,MAAA,EAA0B;AAC/C,EAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,UAAA,EAAY,OAAO,OAAO,IAAA,EAAK;AAC1D,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;AA0BO,SAAS,kBAAA,CACd,OAAA,EACA,IAAA,EACA,MAAA,EACA,QAAA,EACM;AACN,EAAA,YAAA;AAAA,IACE,OAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAM;AACJ,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,CAAA;AAC/B,MAAA,OAAO,QAAA,GAAW,QAAA,CAAS,KAAK,CAAA,GAAK,KAAA;AAAA,IACvC,CAAA;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,GACnB;AACF","file":"index.js","sourcesContent":["import type { ConditionKey, ConditionMap, Trigger, TriggerSchema } from '@triggery/core';\nimport { useCondition } from '@triggery/react';\n\n/**\n * Structural shape that matches every signal flavour we know about: a `peek()`\n * method (preferred — never engages dependency tracking) plus a `.value`\n * accessor (used as a fallback).\n *\n * - `@preact/signals-core` ✓ has both `peek()` and `.value`\n * - `alien-signals` ✓ uses `.peek()` / `.value`\n * - any TC39-style Signal ✓ if the `peek` method is present\n */\nexport interface SignalLike<V> {\n peek?(): V;\n readonly value?: V;\n}\n\n/**\n * Read a signal's value without engaging dependency tracking.\n * Prefers `peek()` when available, falls back to `.value`.\n */\nfunction readSignal<V>(signal: SignalLike<V>): V {\n if (typeof signal.peek === 'function') return signal.peek();\n return signal.value as V;\n}\n\n/**\n * Wire a signal into a Triggery condition.\n *\n * The runtime is pull-only — the signal is read **only** when a trigger\n * fires, via `peek()` so no dependency tracking is engaged. The host\n * component is never subscribed to the signal.\n *\n * If a component also needs to render the signal, use the signal library's\n * own integration (`useSignal()`, `<Signal />`, etc.) — the two paths are\n * orthogonal.\n *\n * @example\n * ```tsx\n * import { signal } from '@preact/signals-core';\n * import { useSignalCondition } from '@triggery/signals';\n *\n * const settings = signal({ sound: true, notifications: true });\n *\n * function SettingsBridge() {\n * useSignalCondition(messageTrigger, 'settings', settings);\n * return null;\n * }\n * ```\n */\nexport function useSignalCondition<V, S extends TriggerSchema, K extends ConditionKey<S>>(\n trigger: Trigger<S>,\n name: K,\n signal: SignalLike<V>,\n selector?: (value: V) => ConditionMap<S>[K],\n): void {\n useCondition(\n trigger,\n name,\n () => {\n const value = readSignal(signal);\n return selector ? selector(value) : (value as unknown as ConditionMap<S>[K]);\n },\n [signal, selector],\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@triggery/signals",
3
+ "version": "0.1.0",
4
+ "description": "Signals adapter for Triggery — read a signal's value from a trigger condition without engaging signal-tracking on the host component",
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/signals"
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
+ "triggery",
26
+ "signals",
27
+ "preact",
28
+ "adapter",
29
+ "react"
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
+ "@triggery/react": "0.1.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@preact/signals-core": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "devDependencies": {
65
+ "@preact/signals-core": "^1.13.2",
66
+ "@testing-library/react": "^16.3.2",
67
+ "@types/react": "^19.2.14",
68
+ "happy-dom": "^20.9.0",
69
+ "react": "^19.2.6",
70
+ "react-dom": "^19.2.6",
71
+ "tsup": "^8.5.1",
72
+ "typescript": "^6.0.3",
73
+ "vitest": "^4.1.6",
74
+ "@triggery/react": "0.1.0",
75
+ "@triggery/core": "0.1.0"
76
+ },
77
+ "scripts": {
78
+ "build": "tsup",
79
+ "dev": "tsup --watch",
80
+ "test": "vitest run",
81
+ "test:watch": "vitest",
82
+ "test:coverage": "vitest run --coverage",
83
+ "clean": "rm -rf dist *.tsbuildinfo"
84
+ }
85
+ }