@rustra/react 0.1.3

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/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @rustra/react
2
+
3
+ React hooks and context bindings for Rustra command clients.
4
+
5
+ ```tsx
6
+ import { RustraProvider, useCommand } from '@rustra/react';
7
+ import { getItem } from './generated/commands.js';
8
+
9
+ function Item({ id }: { id: string }) {
10
+ const { data, loading, error } = useCommand(getItem, { id });
11
+ if (loading) return <span>Loading...</span>;
12
+ if (error) return <span>{error.message}</span>;
13
+ return <span>{data?.name}</span>;
14
+ }
15
+
16
+ <RustraProvider engine={engine}>
17
+ <Item id="item-1" />
18
+ </RustraProvider>;
19
+ ```
20
+
21
+ The package provides `RustraProvider`, `useRustraEngine`, `useCommand`,
22
+ `useMutation`, and `useEvent`. React is a peer dependency; configure the
23
+ platform-specific Rustra engine before rendering the provider.
@@ -0,0 +1,16 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import type { EngineClient } from '@rustra/types';
3
+ export interface RustraProviderProps {
4
+ engine: EngineClient;
5
+ children: ReactNode;
6
+ }
7
+ /**
8
+ * Provides a scoped Rustra `EngineClient` to component subtree.
9
+ */
10
+ export declare function RustraProvider({ engine, children }: RustraProviderProps): React.ReactElement;
11
+ /**
12
+ * Returns the currently active `EngineClient`, either from `<RustraProvider>` or
13
+ * a default engine invoking the global `invoke` singleton.
14
+ */
15
+ export declare function useRustraEngine(): EngineClient;
16
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AAKjE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAE5F;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAS9C"}
@@ -0,0 +1,24 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
+ import { invoke } from '@rustra/types';
3
+ const RustraContext = createContext(null);
4
+ /**
5
+ * Provides a scoped Rustra `EngineClient` to component subtree.
6
+ */
7
+ export function RustraProvider({ engine, children }) {
8
+ return React.createElement(RustraContext.Provider, { value: engine }, children);
9
+ }
10
+ /**
11
+ * Returns the currently active `EngineClient`, either from `<RustraProvider>` or
12
+ * a default engine invoking the global `invoke` singleton.
13
+ */
14
+ export function useRustraEngine() {
15
+ const contextEngine = useContext(RustraContext);
16
+ return useMemo(() => {
17
+ if (contextEngine)
18
+ return contextEngine;
19
+ return {
20
+ invoke: (command, args, options) => invoke(command, args, options),
21
+ };
22
+ }, [contextEngine]);
23
+ }
24
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAElF,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,MAAM,aAAa,GAAG,aAAa,CAAsB,IAAI,CAAC,CAAC;AAO/D;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAuB;IACtE,OAAO,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IAChD,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,aAAa;YAAE,OAAO,aAAa,CAAC;QACxC,OAAO;YACL,MAAM,EAAE,CAAI,OAAe,EAAE,IAAc,EAAE,OAAuB,EAAc,EAAE,CAClF,MAAM,CAAI,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC;SACpC,CAAC;IACJ,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @rustra/react — React and React Native hooks for rustra-bridge.
3
+ *
4
+ * Provides declarative data fetching (`useCommand`), mutations (`useMutation`),
5
+ * event subscriptions (`useEvent`), and scoped engine context (`RustraProvider`).
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { RustraProvider, useCommand, useMutation } from '@rustra/react';
10
+ * import { getItem, createItem } from './generated/commands.js';
11
+ *
12
+ * function ItemView({ id }: { id: string }) {
13
+ * const { data, loading, error, refetch } = useCommand(getItem, { id });
14
+ * const { mutate, loading: creating } = useMutation(createItem, {
15
+ * onSuccess: () => refetch(),
16
+ * });
17
+ *
18
+ * if (loading) return <div>Loading...</div>;
19
+ * return <div>{data?.item?.name}</div>;
20
+ * }
21
+ * ```
22
+ */
23
+ export { RustraProvider, useRustraEngine } from './context.js';
24
+ export type { RustraProviderProps } from './context.js';
25
+ export { useCommand } from './useCommand.js';
26
+ export type { UseCommandOptions, UseCommandResult, CommandFn, VoidCommandFn, } from './useCommand.js';
27
+ export { useMutation } from './useMutation.js';
28
+ export type { UseMutationOptions, UseMutationResult } from './useMutation.js';
29
+ export { useEvent } from './useEvent.js';
30
+ export type { EventCallback, UnsubscribeFn, SubscribeFn } from './useEvent.js';
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @rustra/react — React and React Native hooks for rustra-bridge.
3
+ *
4
+ * Provides declarative data fetching (`useCommand`), mutations (`useMutation`),
5
+ * event subscriptions (`useEvent`), and scoped engine context (`RustraProvider`).
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { RustraProvider, useCommand, useMutation } from '@rustra/react';
10
+ * import { getItem, createItem } from './generated/commands.js';
11
+ *
12
+ * function ItemView({ id }: { id: string }) {
13
+ * const { data, loading, error, refetch } = useCommand(getItem, { id });
14
+ * const { mutate, loading: creating } = useMutation(createItem, {
15
+ * onSuccess: () => refetch(),
16
+ * });
17
+ *
18
+ * if (loading) return <div>Loading...</div>;
19
+ * return <div>{data?.item?.name}</div>;
20
+ * }
21
+ * ```
22
+ */
23
+ export { RustraProvider, useRustraEngine } from './context.js';
24
+ export { useCommand } from './useCommand.js';
25
+ export { useMutation } from './useMutation.js';
26
+ export { useEvent } from './useEvent.js';
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { createElement } from 'react';
4
+ import { renderToString } from 'react-dom/server';
5
+ import { RustraProvider, useRustraEngine, useCommand, useMutation, useEvent } from './index.js';
6
+ function createTestEngine(dataMap) {
7
+ return {
8
+ async invoke(command, _args) {
9
+ if (command in dataMap) {
10
+ return dataMap[command];
11
+ }
12
+ throw new Error(`Command not found: ${command}`);
13
+ },
14
+ };
15
+ }
16
+ test('RustraProvider supplies engine to useRustraEngine', () => {
17
+ const engine = createTestEngine({ testCmd: 'hello' });
18
+ let capturedEngine = null;
19
+ function TestComponent() {
20
+ capturedEngine = useRustraEngine();
21
+ return createElement('div', null, 'test');
22
+ }
23
+ renderToString(createElement(RustraProvider, { engine, children: createElement(TestComponent) }));
24
+ assert.equal(capturedEngine, engine);
25
+ });
26
+ test('useCommand hook contract and properties', () => {
27
+ async function dummyCmd(input) {
28
+ return { res: input.val * 2 };
29
+ }
30
+ const hookResult = { current: null };
31
+ function TestComponent() {
32
+ const result = useCommand(dummyCmd, { val: 5 }, { enabled: false });
33
+ hookResult.current = result;
34
+ return createElement('div', null, String(result.loading));
35
+ }
36
+ const engine = createTestEngine({ dummyCmd: { res: 10 } });
37
+ renderToString(createElement(RustraProvider, { engine, children: createElement(TestComponent) }));
38
+ const result = hookResult.current;
39
+ assert.ok(result);
40
+ assert.equal(result.loading, false);
41
+ assert.equal(typeof result.refetch, 'function');
42
+ });
43
+ test('useMutation hook contract and execution', async () => {
44
+ async function updateItem(_input) {
45
+ return { updated: true };
46
+ }
47
+ const engine = createTestEngine({ updateItem: { updated: true } });
48
+ const mutationResult = { current: null };
49
+ function TestComponent() {
50
+ mutationResult.current = useMutation(updateItem);
51
+ return createElement('div', null, 'mutation');
52
+ }
53
+ renderToString(createElement(RustraProvider, { engine, children: createElement(TestComponent) }));
54
+ const result = mutationResult.current;
55
+ assert.ok(result);
56
+ assert.equal(typeof result.mutate, 'function');
57
+ assert.equal(typeof result.mutateAsync, 'function');
58
+ assert.equal(typeof result.reset, 'function');
59
+ // Execute mutation
60
+ const res = await result.mutateAsync({ id: 'item-1' });
61
+ assert.deepEqual(res, { updated: true });
62
+ });
63
+ test('useEvent handles subscription and contract', () => {
64
+ let subscribedName = '';
65
+ let unsubscribed = false;
66
+ let handlerCalledWith = null;
67
+ const mockSubscriber = (name, cb) => {
68
+ subscribedName = name;
69
+ cb({ count: 1 });
70
+ return () => {
71
+ unsubscribed = true;
72
+ };
73
+ };
74
+ function TestComponent() {
75
+ useEvent('tick', (payload) => {
76
+ handlerCalledWith = payload;
77
+ }, mockSubscriber);
78
+ return createElement('div', null, 'events');
79
+ }
80
+ const el = createElement(TestComponent);
81
+ assert.ok(el);
82
+ assert.equal(typeof useEvent, 'function');
83
+ // Verify subscriber invocation contract
84
+ const unsub = mockSubscriber('tick', (payload) => {
85
+ handlerCalledWith = payload;
86
+ });
87
+ assert.equal(subscribedName, 'tick');
88
+ assert.deepEqual(handlerCalledWith, { count: 1 });
89
+ unsub();
90
+ assert.equal(unsubscribed, true);
91
+ });
92
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { InvokeOptions } from '@rustra/types';
2
+ export type CommandFn<I, O> = (input: I, options?: InvokeOptions) => Promise<O>;
3
+ export type VoidCommandFn<O> = (options?: InvokeOptions) => Promise<O>;
4
+ export interface UseCommandOptions {
5
+ /** If false, query will not run automatically. Default: true */
6
+ enabled?: boolean;
7
+ }
8
+ export interface UseCommandResult<O> {
9
+ data: O | undefined;
10
+ loading: boolean;
11
+ error: Error | null;
12
+ refetch: () => Promise<O | undefined>;
13
+ }
14
+ export declare function useCommand<I, O>(commandFn: CommandFn<I, O> | VoidCommandFn<O>, input?: I, options?: UseCommandOptions): UseCommandResult<O>;
15
+ //# sourceMappingURL=useCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCommand.d.ts","sourceRoot":"","sources":["../src/useCommand.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAChF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;CACvC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAC7B,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAC7C,KAAK,CAAC,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CAAC,CAAC,CAAC,CAwDrB"}
@@ -0,0 +1,54 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { useRustraEngine } from './context.js';
3
+ export function useCommand(commandFn, input, options) {
4
+ const engine = useRustraEngine();
5
+ const [data, setData] = useState(undefined);
6
+ const [loading, setLoading] = useState(options?.enabled ?? true);
7
+ const [error, setError] = useState(null);
8
+ const abortControllerRef = useRef(null);
9
+ const commandName = commandFn.name;
10
+ const execute = useCallback(async () => {
11
+ if (abortControllerRef.current) {
12
+ abortControllerRef.current.abort();
13
+ }
14
+ const ac = new AbortController();
15
+ abortControllerRef.current = ac;
16
+ setLoading(true);
17
+ setError(null);
18
+ try {
19
+ const res = await engine.invoke(commandName, input, { signal: ac.signal });
20
+ if (!ac.signal.aborted) {
21
+ setData(res);
22
+ setLoading(false);
23
+ }
24
+ return res;
25
+ }
26
+ catch (err) {
27
+ if (!ac.signal.aborted) {
28
+ const parsedError = err instanceof Error ? err : new Error(String(err));
29
+ setError(parsedError);
30
+ setLoading(false);
31
+ }
32
+ return undefined;
33
+ }
34
+ }, [engine, commandName, JSON.stringify(input)]);
35
+ useEffect(() => {
36
+ if (options?.enabled === false) {
37
+ setLoading(false);
38
+ return;
39
+ }
40
+ execute();
41
+ return () => {
42
+ if (abortControllerRef.current) {
43
+ abortControllerRef.current.abort();
44
+ }
45
+ };
46
+ }, [execute, options?.enabled]);
47
+ return {
48
+ data,
49
+ loading,
50
+ error,
51
+ refetch: execute,
52
+ };
53
+ }
54
+ //# sourceMappingURL=useCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCommand.js","sourceRoot":"","sources":["../src/useCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAiB/C,MAAM,UAAU,UAAU,CACxB,SAA6C,EAC7C,KAAS,EACT,OAA2B;IAE3B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAgB,SAAS,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAU,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,kBAAkB,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC;IAEnC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAA4B,EAAE;QAC7D,IAAI,kBAAkB,CAAC,OAAO,EAAE,CAAC;YAC/B,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrC,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,kBAAkB,CAAC,OAAO,GAAG,EAAE,CAAC;QAEhC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAI,WAAW,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9E,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,CAAC;gBACb,UAAU,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxE,QAAQ,CAAC,WAAW,CAAC,CAAC;gBACtB,UAAU,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;YAC/B,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QACD,OAAO,EAAE,CAAC;QAEV,OAAO,GAAG,EAAE;YACV,IAAI,kBAAkB,CAAC,OAAO,EAAE,CAAC;gBAC/B,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAEhC,OAAO;QACL,IAAI;QACJ,OAAO;QACP,KAAK;QACL,OAAO,EAAE,OAAO;KACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type EventCallback<T = unknown> = (payload: T) => void;
2
+ export type UnsubscribeFn = () => void;
3
+ export type SubscribeFn<T = unknown> = (eventName: string, callback: EventCallback<T>) => UnsubscribeFn;
4
+ /**
5
+ * Subscribes to a Rustra event stream and automatically unregisters on unmount.
6
+ *
7
+ * @param eventName Name of the event to listen to (e.g. "progress.tick")
8
+ * @param callback Handler called whenever the event fires
9
+ * @param subscribe Optional subscription function (e.g., from `@rustra/react-native` `subscribeEvent`)
10
+ */
11
+ export declare function useEvent<T = unknown>(eventName: string, callback: EventCallback<T>, subscribe?: SubscribeFn<T>): void;
12
+ //# sourceMappingURL=useEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEvent.d.ts","sourceRoot":"","sources":["../src/useEvent.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AACvC,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,CACrC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,KACvB,aAAa,CAAC;AAEnB;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GACzB,IAAI,CAaN"}
@@ -0,0 +1,23 @@
1
+ import { useEffect, useRef } from 'react';
2
+ /**
3
+ * Subscribes to a Rustra event stream and automatically unregisters on unmount.
4
+ *
5
+ * @param eventName Name of the event to listen to (e.g. "progress.tick")
6
+ * @param callback Handler called whenever the event fires
7
+ * @param subscribe Optional subscription function (e.g., from `@rustra/react-native` `subscribeEvent`)
8
+ */
9
+ export function useEvent(eventName, callback, subscribe) {
10
+ const callbackRef = useRef(callback);
11
+ callbackRef.current = callback;
12
+ useEffect(() => {
13
+ if (!subscribe)
14
+ return;
15
+ const unsubscribe = subscribe(eventName, (payload) => {
16
+ callbackRef.current(payload);
17
+ });
18
+ return () => {
19
+ unsubscribe?.();
20
+ };
21
+ }, [eventName, subscribe]);
22
+ }
23
+ //# sourceMappingURL=useEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEvent.js","sourceRoot":"","sources":["../src/useEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAS1C;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CACtB,SAAiB,EACjB,QAA0B,EAC1B,SAA0B;IAE1B,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS;YAAE,OAAO;QACvB,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACnD,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { InvokeOptions } from '@rustra/types';
2
+ import type { CommandFn, VoidCommandFn } from './useCommand.js';
3
+ export interface UseMutationOptions<I, O> {
4
+ onSuccess?: (data: O, input: I) => void;
5
+ onError?: (error: Error, input: I) => void;
6
+ onSettled?: (data: O | undefined, error: Error | null, input: I) => void;
7
+ }
8
+ export interface UseMutationResult<I, O> {
9
+ mutate: (input: I) => void;
10
+ mutateAsync: (input: I, options?: InvokeOptions) => Promise<O>;
11
+ data: O | undefined;
12
+ loading: boolean;
13
+ error: Error | null;
14
+ reset: () => void;
15
+ }
16
+ export declare function useMutation<I = void, O = unknown>(commandFn: CommandFn<I, O> | VoidCommandFn<O>, options?: UseMutationOptions<I, O>): UseMutationResult<I, O>;
17
+ //# sourceMappingURL=useMutation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMutation.d.ts","sourceRoot":"","sources":["../src/useMutation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhE,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,EAAE,CAAC;IACrC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC3B,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/D,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,EAC/C,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAC7C,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GACjC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAyDzB"}
@@ -0,0 +1,50 @@
1
+ import { useState, useCallback, useRef } from 'react';
2
+ import { useRustraEngine } from './context.js';
3
+ export function useMutation(commandFn, options) {
4
+ const engine = useRustraEngine();
5
+ const [data, setData] = useState(undefined);
6
+ const [loading, setLoading] = useState(false);
7
+ const [error, setError] = useState(null);
8
+ const commandName = commandFn.name;
9
+ const optionsRef = useRef(options);
10
+ optionsRef.current = options;
11
+ const mutateAsync = useCallback(async (input, invokeOptions) => {
12
+ setLoading(true);
13
+ setError(null);
14
+ try {
15
+ const res = await engine.invoke(commandName, input, invokeOptions);
16
+ setData(res);
17
+ setLoading(false);
18
+ optionsRef.current?.onSuccess?.(res, input);
19
+ optionsRef.current?.onSettled?.(res, null, input);
20
+ return res;
21
+ }
22
+ catch (err) {
23
+ const parsedError = err instanceof Error ? err : new Error(String(err));
24
+ setError(parsedError);
25
+ setLoading(false);
26
+ optionsRef.current?.onError?.(parsedError, input);
27
+ optionsRef.current?.onSettled?.(undefined, parsedError, input);
28
+ throw parsedError;
29
+ }
30
+ }, [engine, commandName]);
31
+ const mutate = useCallback((input) => {
32
+ mutateAsync(input).catch(() => {
33
+ // Handled in mutateAsync state & onError callback
34
+ });
35
+ }, [mutateAsync]);
36
+ const reset = useCallback(() => {
37
+ setData(undefined);
38
+ setError(null);
39
+ setLoading(false);
40
+ }, []);
41
+ return {
42
+ mutate,
43
+ mutateAsync,
44
+ data,
45
+ loading,
46
+ error,
47
+ reset,
48
+ };
49
+ }
50
+ //# sourceMappingURL=useMutation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMutation.js","sourceRoot":"","sources":["../src/useMutation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAkB/C,MAAM,UAAU,WAAW,CACzB,SAA6C,EAC7C,OAAkC;IAElC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAgB,SAAS,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,KAAQ,EAAE,aAA6B,EAAc,EAAE;QAC5D,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAI,WAAW,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,CAAC;YACb,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC5C,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAClD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,WAAW,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACxE,QAAQ,CAAC,WAAW,CAAC,CAAC;YACtB,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAClD,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;YAC/D,MAAM,WAAW,CAAC;QACpB,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,WAAW,CAAC,CACtB,CAAC;IAEF,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,KAAQ,EAAE,EAAE;QACX,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC5B,kDAAkD;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,WAAW,CAAC,CACd,CAAC;IAEF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,OAAO,CAAC,SAAS,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,UAAU,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,MAAM;QACN,WAAW;QACX,IAAI;QACJ,OAAO;QACP,KAAK;QACL,KAAK;KACN,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@rustra/react",
3
+ "version": "0.1.3",
4
+ "description": "React and React Native bindings and hooks for rustra-bridge",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "clean": "rm -rf dist",
24
+ "test": "npm run build && node --test dist/index.test.js"
25
+ },
26
+ "peerDependencies": {
27
+ "react": ">=18.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@rustra/types": "^0.1.3"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.0.0",
34
+ "react": "^19.0.0",
35
+ "typescript": "^5.9.0"
36
+ },
37
+ "keywords": [
38
+ "rustra",
39
+ "bridge",
40
+ "react",
41
+ "react-native",
42
+ "hooks"
43
+ ],
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/loopy-lim/rustra.git",
47
+ "directory": "packages/react"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/loopy-lim/rustra/issues"
51
+ },
52
+ "homepage": "https://github.com/loopy-lim/rustra#readme"
53
+ }