@reactor-team/js-sdk 1.0.6 → 1.0.8
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/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +43 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/.env.example +0 -2
- package/.prettierignore +0 -7
- package/.prettierrc +0 -9
- package/publish_package.sh +0 -45
- package/src/core/CoordinatorClient.ts +0 -167
- package/src/core/GPUMachineClient.ts +0 -172
- package/src/core/Reactor.ts +0 -406
- package/src/core/store.ts +0 -163
- package/src/core/types.ts +0 -99
- package/src/index.ts +0 -5
- package/src/react/ReactorProvider.tsx +0 -86
- package/src/react/ReactorView.tsx +0 -116
- package/src/react/hooks.ts +0 -50
- package/src/types.ts +0 -37
- package/tsconfig.json +0 -19
- package/tsup.config.ts +0 -15
package/src/core/store.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { StoreApi } from "zustand";
|
|
2
|
-
import type { ReactorStatus, ReactorWaitingInfo, ReactorError } from "../types";
|
|
3
|
-
import { Reactor, type Options as ReactorOptions } from "./Reactor";
|
|
4
|
-
import { create } from "zustand/react";
|
|
5
|
-
import { createContext } from "react";
|
|
6
|
-
import type { RemoteVideoTrack } from "livekit-client";
|
|
7
|
-
|
|
8
|
-
export type ReactorStoreApi = ReturnType<typeof createReactorStore>;
|
|
9
|
-
|
|
10
|
-
export interface ReactorState {
|
|
11
|
-
status: ReactorStatus;
|
|
12
|
-
videoTrack: RemoteVideoTrack | null;
|
|
13
|
-
//These are the machine FPS. The machine internally reports to the client the rate at which the frames are
|
|
14
|
-
//being generated. This can be useful for the client to calcolate at which rate to run commands.
|
|
15
|
-
fps?: number;
|
|
16
|
-
waitingInfo?: ReactorWaitingInfo;
|
|
17
|
-
lastError?: ReactorError;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ReactorActions {
|
|
21
|
-
sendMessage(message: any): void;
|
|
22
|
-
connect(): Promise<void>;
|
|
23
|
-
disconnect(): Promise<void>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Internal state not exposed to components
|
|
27
|
-
interface ReactorInternalState {
|
|
28
|
-
reactor: Reactor;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const ReactorContext = createContext<ReactorStoreApi | undefined>(
|
|
32
|
-
undefined
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export type ReactorStore = ReactorState &
|
|
36
|
-
ReactorActions & {
|
|
37
|
-
internal: ReactorInternalState;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const defaultInitState: ReactorState = {
|
|
41
|
-
status: "disconnected",
|
|
42
|
-
videoTrack: null,
|
|
43
|
-
fps: undefined,
|
|
44
|
-
waitingInfo: undefined,
|
|
45
|
-
lastError: undefined,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export interface ReactorInitializationProps extends ReactorOptions {}
|
|
49
|
-
|
|
50
|
-
export const initReactorStore = (
|
|
51
|
-
props: ReactorInitializationProps
|
|
52
|
-
): ReactorState & ReactorInitializationProps => {
|
|
53
|
-
return {
|
|
54
|
-
...defaultInitState,
|
|
55
|
-
// These are only used for dev initialization, not exposed in the store
|
|
56
|
-
...props,
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const createReactorStore = (
|
|
61
|
-
initProps: ReactorInitializationProps,
|
|
62
|
-
publicState: ReactorState = defaultInitState
|
|
63
|
-
): StoreApi<ReactorStore> => {
|
|
64
|
-
console.debug("[ReactorStore] Creating store", {
|
|
65
|
-
coordinatorUrl: initProps.coordinatorUrl,
|
|
66
|
-
initialState: publicState,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return create<ReactorStore>()((set, get) => {
|
|
70
|
-
const reactor = new Reactor(initProps);
|
|
71
|
-
|
|
72
|
-
console.debug("[ReactorStore] Setting up event listeners");
|
|
73
|
-
|
|
74
|
-
reactor.on("statusChanged", (newStatus: ReactorStatus) => {
|
|
75
|
-
console.debug("[ReactorStore] Status changed", {
|
|
76
|
-
oldStatus: get().status,
|
|
77
|
-
newStatus,
|
|
78
|
-
});
|
|
79
|
-
set({ status: newStatus });
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
reactor.on("waitingInfoChanged", (newWaitingInfo: ReactorWaitingInfo) => {
|
|
83
|
-
console.debug("[ReactorStore] Waiting info changed", {
|
|
84
|
-
oldWaitingInfo: get().waitingInfo,
|
|
85
|
-
newWaitingInfo,
|
|
86
|
-
});
|
|
87
|
-
set({ waitingInfo: newWaitingInfo });
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
reactor.on("streamChanged", (videoTrack: RemoteVideoTrack | null) => {
|
|
91
|
-
console.debug("[ReactorStore] Stream changed", {
|
|
92
|
-
hasVideoTrack: !!videoTrack,
|
|
93
|
-
videoTrackKind: videoTrack?.kind,
|
|
94
|
-
videoTrackSid: videoTrack?.sid,
|
|
95
|
-
});
|
|
96
|
-
set({ videoTrack: videoTrack });
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
reactor.on("fps", (fps: number) => {
|
|
100
|
-
console.debug("[ReactorStore] FPS updated", { fps });
|
|
101
|
-
set({ fps: fps });
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
reactor.on("error", (error: ReactorError) => {
|
|
105
|
-
console.debug("[ReactorStore] Error occurred", error);
|
|
106
|
-
set({ lastError: error });
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
...publicState,
|
|
111
|
-
internal: { reactor },
|
|
112
|
-
|
|
113
|
-
// actions
|
|
114
|
-
onMessage: (handler: (message: any) => void) => {
|
|
115
|
-
console.debug("[ReactorStore] Registering message handler");
|
|
116
|
-
|
|
117
|
-
// Simply register the handler
|
|
118
|
-
get().internal.reactor.on("newMessage", handler);
|
|
119
|
-
|
|
120
|
-
// Return a cleanup function that can be called to unregister
|
|
121
|
-
return () => {
|
|
122
|
-
console.debug("[ReactorStore] Cleaning up message handler");
|
|
123
|
-
get().internal.reactor.off("newMessage", handler);
|
|
124
|
-
};
|
|
125
|
-
},
|
|
126
|
-
sendMessage: (mess: any) => {
|
|
127
|
-
console.debug("[ReactorStore] Sending message", { message: mess });
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
get().internal.reactor.sendMessage(mess);
|
|
131
|
-
console.debug("[ReactorStore] Message sent successfully");
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error("[ReactorStore] Failed to send message:", error);
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
connect: async () => {
|
|
138
|
-
console.debug("[ReactorStore] Connect called");
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
await get().internal.reactor.connect();
|
|
142
|
-
console.debug("[ReactorStore] Connect completed successfully");
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error("[ReactorStore] Connect failed:", error);
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
disconnect: async () => {
|
|
149
|
-
console.debug("[ReactorStore] Disconnect called", {
|
|
150
|
-
currentStatus: get().status,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
await get().internal.reactor.disconnect();
|
|
155
|
-
console.debug("[ReactorStore] Disconnect completed successfully");
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.error("[ReactorStore] Disconnect failed:", error);
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
});
|
|
163
|
-
};
|
package/src/core/types.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal types for the Reactor SDK.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
|
|
7
|
-
// Base message schemas corresponding to Python Pydantic models
|
|
8
|
-
|
|
9
|
-
export const ApplicationMessageSchema = z.object({
|
|
10
|
-
type: z.literal("application"),
|
|
11
|
-
data: z.any(), // Can be any JSON-serializable data
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Internal message that notifies the client of the current push rate of the machine
|
|
15
|
-
export const FPSMessageSchema = z.object({
|
|
16
|
-
type: z.literal("fps"),
|
|
17
|
-
data: z.number(),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export const GPUMachineReceiveMessageSchema = z.discriminatedUnion("type", [
|
|
21
|
-
ApplicationMessageSchema,
|
|
22
|
-
FPSMessageSchema,
|
|
23
|
-
]);
|
|
24
|
-
|
|
25
|
-
export const WelcomeMessageSchema = z.object({
|
|
26
|
-
type: z.literal("welcome"),
|
|
27
|
-
data: z.record(z.string(), z.any()),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
export const GPUMachineAssignmentDataSchema = z.object({
|
|
31
|
-
livekitWsUrl: z.string(),
|
|
32
|
-
livekitJwtToken: z.string(),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export const GPUMachineAssignmentMessageSchema = z.object({
|
|
36
|
-
type: z.literal("gpu-machine-assigned"),
|
|
37
|
-
data: GPUMachineAssignmentDataSchema,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export const EchoMessageSchema = z.object({
|
|
41
|
-
type: z.literal("echo"),
|
|
42
|
-
data: z.record(z.string(), z.any()),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
export const WaitingInfoDataSchema = z.object({
|
|
46
|
-
position: z.number().optional(),
|
|
47
|
-
estimatedWaitTime: z.number().optional(),
|
|
48
|
-
averageWaitTime: z.number().optional(),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
export const WaitingInfoMessageSchema = z.object({
|
|
52
|
-
type: z.literal("waiting-info"),
|
|
53
|
-
data: WaitingInfoDataSchema,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export const CoordinatorMessageSchema = z.discriminatedUnion("type", [
|
|
57
|
-
WelcomeMessageSchema,
|
|
58
|
-
GPUMachineAssignmentMessageSchema,
|
|
59
|
-
EchoMessageSchema,
|
|
60
|
-
WaitingInfoMessageSchema,
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
export const GPUMachineSendMessageSchema = z.discriminatedUnion("type", [
|
|
64
|
-
ApplicationMessageSchema,
|
|
65
|
-
]);
|
|
66
|
-
|
|
67
|
-
export const SessionSetupMessageSchema = z.object({
|
|
68
|
-
type: z.literal("sessionSetup"),
|
|
69
|
-
data: z.object({
|
|
70
|
-
modelName: z.string(),
|
|
71
|
-
modelVersion: z.string().default("latest"),
|
|
72
|
-
}),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// TypeScript types derived from schemas
|
|
76
|
-
export type ApplicationMessage = z.infer<typeof ApplicationMessageSchema>;
|
|
77
|
-
export type GPUMachineReceiveMessageSchema = z.infer<
|
|
78
|
-
typeof GPUMachineReceiveMessageSchema
|
|
79
|
-
>;
|
|
80
|
-
export type WelcomeMessage = z.infer<typeof WelcomeMessageSchema>;
|
|
81
|
-
export type GPUMachineAssignmentData = z.infer<
|
|
82
|
-
typeof GPUMachineAssignmentDataSchema
|
|
83
|
-
>;
|
|
84
|
-
export type GPUMachineAssignmentMessage = z.infer<
|
|
85
|
-
typeof GPUMachineAssignmentMessageSchema
|
|
86
|
-
>;
|
|
87
|
-
export type EchoMessage = z.infer<typeof EchoMessageSchema>;
|
|
88
|
-
export type WaitingInfoData = z.infer<typeof WaitingInfoDataSchema>;
|
|
89
|
-
export type WaitingInfoMessage = z.infer<typeof WaitingInfoMessageSchema>;
|
|
90
|
-
export type CoordinatorMessage = z.infer<typeof CoordinatorMessageSchema>;
|
|
91
|
-
export type GPUMachineSendMessage = z.infer<typeof GPUMachineSendMessageSchema>;
|
|
92
|
-
export type SessionSetupMessage = z.infer<typeof SessionSetupMessageSchema>;
|
|
93
|
-
|
|
94
|
-
// Internal connection status for individual components
|
|
95
|
-
export type InternalConnectionStatus =
|
|
96
|
-
| "disconnected"
|
|
97
|
-
| "connecting"
|
|
98
|
-
| "connected"
|
|
99
|
-
| "error";
|
package/src/index.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { ReactNode, useContext, useEffect, useRef } from "react";
|
|
4
|
-
import {
|
|
5
|
-
createReactorStore,
|
|
6
|
-
initReactorStore,
|
|
7
|
-
ReactorContext,
|
|
8
|
-
ReactorStore,
|
|
9
|
-
ReactorStoreApi,
|
|
10
|
-
type ReactorInitializationProps,
|
|
11
|
-
} from "../core/store";
|
|
12
|
-
import { useStore } from "zustand";
|
|
13
|
-
|
|
14
|
-
// Provider props
|
|
15
|
-
interface ReactorProviderProps extends ReactorInitializationProps {
|
|
16
|
-
autoConnect?: boolean;
|
|
17
|
-
children: ReactNode;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// tsx component
|
|
21
|
-
export function ReactorProvider({
|
|
22
|
-
children,
|
|
23
|
-
autoConnect = true,
|
|
24
|
-
...props
|
|
25
|
-
}: ReactorProviderProps) {
|
|
26
|
-
// Stable Reactor instance
|
|
27
|
-
const storeRef = useRef<ReactorStoreApi | undefined>(undefined);
|
|
28
|
-
const firstRender = useRef(true);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Attempts connecting to Reactor, if the autoConnect flag is set.
|
|
32
|
-
*/
|
|
33
|
-
function attemptAutoConnect() {
|
|
34
|
-
const status = storeRef.current?.getState().status;
|
|
35
|
-
|
|
36
|
-
if (autoConnect && status === "disconnected") {
|
|
37
|
-
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
38
|
-
storeRef.current
|
|
39
|
-
?.getState()
|
|
40
|
-
.connect()
|
|
41
|
-
.then(() => {
|
|
42
|
-
console.debug("[ReactorProvider] Autoconnect successful");
|
|
43
|
-
})
|
|
44
|
-
.catch((error) => {
|
|
45
|
-
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (storeRef.current === undefined) {
|
|
51
|
-
console.debug("[ReactorProvider] Creating new reactor store");
|
|
52
|
-
// We create the store without autoconnecting, to avoid duplicate connections.
|
|
53
|
-
// We actually connect when the component is mounted, to be on sync with the react component lifecycle.
|
|
54
|
-
storeRef.current = createReactorStore(initReactorStore(props));
|
|
55
|
-
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
56
|
-
attemptAutoConnect();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
if (firstRender.current) {
|
|
61
|
-
firstRender.current = false;
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
console.debug("[ReactorProvider] Updating reactor store");
|
|
65
|
-
storeRef.current = createReactorStore(initReactorStore(props));
|
|
66
|
-
console.debug("[ReactorProvider] Reactor store updated successfully");
|
|
67
|
-
attemptAutoConnect();
|
|
68
|
-
}, [props, autoConnect]);
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<ReactorContext.Provider value={storeRef.current}>
|
|
72
|
-
{children}
|
|
73
|
-
</ReactorContext.Provider>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function useReactorStore<T = ReactorStore>(
|
|
78
|
-
selector: (state: ReactorStore) => T
|
|
79
|
-
): T {
|
|
80
|
-
const ctx = useContext(ReactorContext);
|
|
81
|
-
if (!ctx) {
|
|
82
|
-
throw new Error("useReactor must be used within a ReactorProvider");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return useStore(ctx, selector);
|
|
86
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useReactor } from "./hooks";
|
|
4
|
-
import { useEffect, useRef } from "react";
|
|
5
|
-
import React from "react";
|
|
6
|
-
|
|
7
|
-
export interface ReactorViewProps {
|
|
8
|
-
width?: number;
|
|
9
|
-
height?: number;
|
|
10
|
-
className?: string;
|
|
11
|
-
style?: React.CSSProperties;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function ReactorView({
|
|
15
|
-
width,
|
|
16
|
-
height,
|
|
17
|
-
className,
|
|
18
|
-
style,
|
|
19
|
-
}: ReactorViewProps) {
|
|
20
|
-
const { videoTrack, status } = useReactor((state) => ({
|
|
21
|
-
videoTrack: state.videoTrack,
|
|
22
|
-
status: state.status,
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
const videoRef = useRef<HTMLVideoElement>(null);
|
|
26
|
-
|
|
27
|
-
console.debug("[ReactorView] Render", {
|
|
28
|
-
hasVideoTrack: !!videoTrack,
|
|
29
|
-
status,
|
|
30
|
-
hasVideoElement: !!videoRef.current,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
console.debug("[ReactorView] Video track effect triggered", {
|
|
35
|
-
hasVideoElement: !!videoRef.current,
|
|
36
|
-
hasVideoTrack: !!videoTrack,
|
|
37
|
-
videoTrackKind: videoTrack?.kind,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (videoRef.current && videoTrack) {
|
|
41
|
-
console.debug("[ReactorView] Attaching video track to element");
|
|
42
|
-
try {
|
|
43
|
-
// Attach the LiveKit track to the video element
|
|
44
|
-
videoTrack.attach(videoRef.current);
|
|
45
|
-
console.debug("[ReactorView] Video track attached successfully");
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error("[ReactorView] Failed to attach video track:", error);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Cleanup: detach when track changes or component unmounts
|
|
51
|
-
return () => {
|
|
52
|
-
console.debug("[ReactorView] Detaching video track from element");
|
|
53
|
-
if (videoRef.current) {
|
|
54
|
-
try {
|
|
55
|
-
videoTrack.detach(videoRef.current);
|
|
56
|
-
console.debug("[ReactorView] Video track detached successfully");
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error("[ReactorView] Failed to detach video track:", error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
} else {
|
|
63
|
-
console.debug("[ReactorView] No video track or element to attach");
|
|
64
|
-
}
|
|
65
|
-
}, [videoTrack]);
|
|
66
|
-
|
|
67
|
-
const showPlaceholder = !videoTrack;
|
|
68
|
-
console.debug("[ReactorView] Placeholder state", { showPlaceholder, status });
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div
|
|
72
|
-
style={{
|
|
73
|
-
position: "relative",
|
|
74
|
-
background: "#000",
|
|
75
|
-
...(width && { width }),
|
|
76
|
-
...(height && { height }),
|
|
77
|
-
...style,
|
|
78
|
-
}}
|
|
79
|
-
className={className}
|
|
80
|
-
>
|
|
81
|
-
<video
|
|
82
|
-
ref={videoRef}
|
|
83
|
-
style={{
|
|
84
|
-
width: "100%",
|
|
85
|
-
height: "100%",
|
|
86
|
-
objectFit: "contain",
|
|
87
|
-
display: showPlaceholder ? "none" : "block",
|
|
88
|
-
}}
|
|
89
|
-
muted
|
|
90
|
-
playsInline
|
|
91
|
-
/>
|
|
92
|
-
{showPlaceholder && (
|
|
93
|
-
<div
|
|
94
|
-
style={{
|
|
95
|
-
position: "absolute",
|
|
96
|
-
top: 0,
|
|
97
|
-
left: 0,
|
|
98
|
-
width: "100%",
|
|
99
|
-
height: "100%",
|
|
100
|
-
color: "#fff",
|
|
101
|
-
display: "flex",
|
|
102
|
-
alignItems: "center",
|
|
103
|
-
justifyContent: "center",
|
|
104
|
-
fontSize: "16px",
|
|
105
|
-
fontFamily: "monospace",
|
|
106
|
-
textAlign: "center",
|
|
107
|
-
padding: "20px",
|
|
108
|
-
boxSizing: "border-box",
|
|
109
|
-
}}
|
|
110
|
-
>
|
|
111
|
-
{status}
|
|
112
|
-
</div>
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
}
|
package/src/react/hooks.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { useReactorStore } from "./ReactorProvider";
|
|
2
|
-
import type { ReactorStore } from "../core/store";
|
|
3
|
-
import { useShallow } from "zustand/shallow";
|
|
4
|
-
import { useEffect, useRef } from "react";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generic hook for accessing selected parts of the Reactor store.
|
|
8
|
-
*
|
|
9
|
-
* @param selector - A function that selects part of the store state.
|
|
10
|
-
* @returns The selected slice from the store.
|
|
11
|
-
*/
|
|
12
|
-
export function useReactor<T>(selector: (state: ReactorStore) => T): T {
|
|
13
|
-
return useReactorStore(useShallow(selector));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Hook for handling message subscriptions with proper React lifecycle management.
|
|
18
|
-
*
|
|
19
|
-
* @param handler - The message handler function
|
|
20
|
-
*/
|
|
21
|
-
export function useReactorMessage(handler: (message: any) => void): void {
|
|
22
|
-
const reactor = useReactor((state) => state.internal.reactor);
|
|
23
|
-
const handlerRef = useRef(handler);
|
|
24
|
-
|
|
25
|
-
// Update the ref when handler changes
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
handlerRef.current = handler;
|
|
28
|
-
}, [handler]);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
console.debug("[useReactorMessage] Setting up message subscription");
|
|
32
|
-
|
|
33
|
-
// Create a stable handler that calls the current ref
|
|
34
|
-
const stableHandler = (message: any) => {
|
|
35
|
-
console.debug("[useReactorMessage] Message received", { message });
|
|
36
|
-
handlerRef.current(message);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Register the handler and get the cleanup function
|
|
40
|
-
reactor.on("newMessage", stableHandler);
|
|
41
|
-
|
|
42
|
-
console.debug("[useReactorMessage] Message handler registered");
|
|
43
|
-
|
|
44
|
-
// Return the cleanup function
|
|
45
|
-
return () => {
|
|
46
|
-
console.debug("[useReactorMessage] Cleaning up message subscription");
|
|
47
|
-
reactor.off("newMessage", stableHandler);
|
|
48
|
-
};
|
|
49
|
-
}, [reactor]);
|
|
50
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type ReactorStatus =
|
|
2
|
-
| "disconnected" // Not connected to anything
|
|
3
|
-
| "connecting" // Establishing connection to coordinator
|
|
4
|
-
| "waiting" // Connected to coordinator, waiting for GPU assignment
|
|
5
|
-
| "ready"; // Connected to GPU machine, can send/receive messages
|
|
6
|
-
|
|
7
|
-
// Queue information when in waiting state
|
|
8
|
-
export interface ReactorWaitingInfo {
|
|
9
|
-
position?: number; // Position in queue
|
|
10
|
-
estimatedWaitTime?: number; // Seconds
|
|
11
|
-
averageWaitTime?: number; // Historical data
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Error information
|
|
15
|
-
export interface ReactorError {
|
|
16
|
-
code: string;
|
|
17
|
-
message: string;
|
|
18
|
-
timestamp: number;
|
|
19
|
-
recoverable: boolean;
|
|
20
|
-
component: "coordinator" | "gpu" | "livekit";
|
|
21
|
-
retryAfter?: number; // Suggested retry delay in seconds
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Enhanced state with metadata
|
|
25
|
-
export interface ReactorState {
|
|
26
|
-
status: ReactorStatus;
|
|
27
|
-
waitingInfo?: ReactorWaitingInfo; // When status is 'waiting'
|
|
28
|
-
lastError?: ReactorError; // Most recent error
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type ReactorEvent =
|
|
32
|
-
| "statusChanged" //updates on the reactor status
|
|
33
|
-
| "waitingInfoChanged" //updates on the waiting info
|
|
34
|
-
| "newMessage" //new messages from the machine (coordinator handled internally)
|
|
35
|
-
| "fps" //update of the fps rate of the machine
|
|
36
|
-
| "streamChanged" //video stream has changed (LiveKit)
|
|
37
|
-
| "error"; //error events with ReactorError details
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES6",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["DOM", "ESNext"],
|
|
6
|
-
"moduleResolution": "Node",
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"emitDeclarationOnly": true,
|
|
9
|
-
"declarationMap": true,
|
|
10
|
-
"outDir": "./dist",
|
|
11
|
-
"jsx": "react-jsx",
|
|
12
|
-
"esModuleInterop": true,
|
|
13
|
-
"skipLibCheck": true,
|
|
14
|
-
"forceConsistentCasingInFileNames": true,
|
|
15
|
-
"strict": true,
|
|
16
|
-
"stripInternal": true
|
|
17
|
-
},
|
|
18
|
-
"include": ["src", "bin"]
|
|
19
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "tsup";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ["src/index.ts"],
|
|
5
|
-
format: ["cjs", "esm"],
|
|
6
|
-
dts: {
|
|
7
|
-
entry: ["src/index.ts"],
|
|
8
|
-
resolve: true,
|
|
9
|
-
},
|
|
10
|
-
splitting: false,
|
|
11
|
-
sourcemap: true,
|
|
12
|
-
clean: true,
|
|
13
|
-
outDir: "dist",
|
|
14
|
-
tsconfig: "tsconfig.json",
|
|
15
|
-
});
|