@the-link/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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2026 Zohayr SLILEH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
20
+
package/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # React
2
+
3
+ React bindings for The Link.
4
+
@@ -0,0 +1,21 @@
1
+ import Property from "@the-link/core/property";
2
+ /**
3
+ * React hook for reading a Property value reactively.
4
+ *
5
+ * The Property remains the source of truth. React reads its current snapshot
6
+ * and subscribes only for invalidation, so there is no mirrored state that can
7
+ * fall out of step between rendering and effect setup.
8
+ *
9
+ * @param property Property instance to synchronize with React state
10
+ * @returns Current property value, updated when the property publishes `change`
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * function ThemeLabel({ theme }: { theme: Property<string> }) {
15
+ * const value = useProperty(theme)
16
+ *
17
+ * return <span>{value}</span>
18
+ * }
19
+ * ```
20
+ */
21
+ export default function useProperty<Value>(property: Property<Value>): Value;
@@ -0,0 +1,25 @@
1
+ import { useCallback, useSyncExternalStore } from "react";
2
+ /**
3
+ * React hook for reading a Property value reactively.
4
+ *
5
+ * The Property remains the source of truth. React reads its current snapshot
6
+ * and subscribes only for invalidation, so there is no mirrored state that can
7
+ * fall out of step between rendering and effect setup.
8
+ *
9
+ * @param property Property instance to synchronize with React state
10
+ * @returns Current property value, updated when the property publishes `change`
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * function ThemeLabel({ theme }: { theme: Property<string> }) {
15
+ * const value = useProperty(theme)
16
+ *
17
+ * return <span>{value}</span>
18
+ * }
19
+ * ```
20
+ */
21
+ export default function useProperty(property) {
22
+ const subscribe = useCallback((changed) => property.tunnel.subscribe("change", changed), [property]);
23
+ const snapshot = useCallback(() => property.value, [property]);
24
+ return useSyncExternalStore(subscribe, snapshot, snapshot);
25
+ }
@@ -0,0 +1,60 @@
1
+ import Tunnel, { Subscriber } from "@the-link/core/tunnel";
2
+ /**
3
+ * React hook helper for a Tunnel instance.
4
+ *
5
+ * Provides React-friendly subscription and state helpers around Tunnel events.
6
+ * Subscriptions are registered in effects and cleaned up automatically, while
7
+ * event payloads can be mirrored into React state for rendering.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * function StatusView({ tunnel }: { tunnel: Tunnel }) {
12
+ * const reactTunnel = ReactTunnel.useFactory(tunnel)
13
+ * const status = reactTunnel.useFirstState("status:update", "idle")
14
+ *
15
+ * return <span>{status}</span>
16
+ * }
17
+ * ```
18
+ */
19
+ export default class ReactTunnel {
20
+ /**
21
+ * Tunnel instance used by the React hook helpers.
22
+ */
23
+ readonly tunnel: Tunnel;
24
+ /**
25
+ * Initialize a ReactTunnel wrapper around a Tunnel.
26
+ *
27
+ * @param tunnel Tunnel instance to integrate with React components
28
+ */
29
+ constructor(tunnel: Tunnel);
30
+ /**
31
+ * Create a memoized ReactTunnel instance for a Tunnel.
32
+ *
33
+ * @param tunnel Tunnel instance to wrap
34
+ * @returns Stable ReactTunnel wrapper while the Tunnel reference is unchanged
35
+ */
36
+ static useFactory(tunnel: Tunnel): ReactTunnel;
37
+ /**
38
+ * Subscribe to a Tunnel event with React lifecycle cleanup.
39
+ *
40
+ * @param event Event identifier to subscribe to
41
+ * @param subscriber Handler invoked when the event is published
42
+ */
43
+ useSubscribe(event: string, subscriber: Subscriber): void;
44
+ /**
45
+ * Mirror a Tunnel event's payload values into React state.
46
+ *
47
+ * @param event Event identifier used for state updates
48
+ * @param defaultResults Initial state values before the event is published
49
+ * @returns Latest event payload values
50
+ */
51
+ useState<Results extends unknown[]>(event: string, defaultResults: Results): Results;
52
+ /**
53
+ * Mirror the first value of a Tunnel event payload into React state.
54
+ *
55
+ * @param event Event identifier used for state updates
56
+ * @param defaultResult Initial value before the event is published
57
+ * @returns Latest first event payload value
58
+ */
59
+ useFirstState<Result>(event: string, defaultResult: Result): Result;
60
+ }
@@ -0,0 +1,80 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ /**
3
+ * React hook helper for a Tunnel instance.
4
+ *
5
+ * Provides React-friendly subscription and state helpers around Tunnel events.
6
+ * Subscriptions are registered in effects and cleaned up automatically, while
7
+ * event payloads can be mirrored into React state for rendering.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * function StatusView({ tunnel }: { tunnel: Tunnel }) {
12
+ * const reactTunnel = ReactTunnel.useFactory(tunnel)
13
+ * const status = reactTunnel.useFirstState("status:update", "idle")
14
+ *
15
+ * return <span>{status}</span>
16
+ * }
17
+ * ```
18
+ */
19
+ export default class ReactTunnel {
20
+ /**
21
+ * Tunnel instance used by the React hook helpers.
22
+ */
23
+ tunnel;
24
+ /**
25
+ * Initialize a ReactTunnel wrapper around a Tunnel.
26
+ *
27
+ * @param tunnel Tunnel instance to integrate with React components
28
+ */
29
+ constructor(tunnel) {
30
+ // Store the tunnel used by all helper hooks.
31
+ this.tunnel = tunnel;
32
+ }
33
+ /**
34
+ * Create a memoized ReactTunnel instance for a Tunnel.
35
+ *
36
+ * @param tunnel Tunnel instance to wrap
37
+ * @returns Stable ReactTunnel wrapper while the Tunnel reference is unchanged
38
+ */
39
+ static useFactory(tunnel) {
40
+ return useMemo(() => new ReactTunnel(tunnel), [tunnel]);
41
+ }
42
+ /**
43
+ * Subscribe to a Tunnel event with React lifecycle cleanup.
44
+ *
45
+ * @param event Event identifier to subscribe to
46
+ * @param subscriber Handler invoked when the event is published
47
+ */
48
+ useSubscribe(event, subscriber) {
49
+ useEffect(() => {
50
+ // Register on mount or when the event/subscriber pair changes.
51
+ this.tunnel.subscribe(event, subscriber);
52
+ // Remove the exact subscriber when React cleans up the effect.
53
+ return () => this.tunnel.unsubscribe(event, subscriber);
54
+ }, [event, subscriber]);
55
+ }
56
+ /**
57
+ * Mirror a Tunnel event's payload values into React state.
58
+ *
59
+ * @param event Event identifier used for state updates
60
+ * @param defaultResults Initial state values before the event is published
61
+ * @returns Latest event payload values
62
+ */
63
+ useState(event, defaultResults) {
64
+ const [results, setResults] = useState(defaultResults);
65
+ // Store each event publication payload as the next React state value.
66
+ const subscriber = useCallback((...results) => setResults(results), []);
67
+ this.useSubscribe(event, subscriber);
68
+ return results;
69
+ }
70
+ /**
71
+ * Mirror the first value of a Tunnel event payload into React state.
72
+ *
73
+ * @param event Event identifier used for state updates
74
+ * @param defaultResult Initial value before the event is published
75
+ * @returns Latest first event payload value
76
+ */
77
+ useFirstState(event, defaultResult) {
78
+ return this.useState(event, [defaultResult])[0];
79
+ }
80
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@the-link/react",
3
+ "version": "0.1.0",
4
+ "description": "React bindings for The Link.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "dist/react-tunnel.js",
8
+ "types": "dist/react-tunnel.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/react-tunnel.d.ts",
12
+ "default": "./dist/react-tunnel.js"
13
+ },
14
+ "./property": {
15
+ "types": "./dist/property-hook.d.ts",
16
+ "default": "./dist/property-hook.js"
17
+ }
18
+ },
19
+ "files": ["dist", "LICENSE", "README.md"],
20
+ "author": "Zohayr SLILEH",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/the-l-ink/react.git"
25
+ },
26
+ "homepage": "https://the-l.ink",
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
30
+ },
31
+ "packageManager": "bun@1.3.14",
32
+ "scripts": {
33
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
34
+ "check": "tsc --noEmit",
35
+ "build": "node --run clean && tsc",
36
+ "verify:package": "npm pack --dry-run --ignore-scripts",
37
+ "verify": "node --run check && node --run build && node --run verify:package",
38
+ "prepack": "node --run build"
39
+ },
40
+ "peerDependencies": {
41
+ "@the-link/core": "^0.1.0",
42
+ "react": "^19.2.0"
43
+ },
44
+ "devDependencies": {
45
+ "@the-link/core": "^0.1.0",
46
+ "@types/react": "^19.2.18",
47
+ "react": "^19.2.8",
48
+ "typescript": "^6.0.3"
49
+ }
50
+ }