@computekit/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/dist/index.cjs +170 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/src/index.tsx +410 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var core = require('@computekit/core');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/index.tsx
|
|
8
|
+
var ComputeKitContext = react.createContext(null);
|
|
9
|
+
function ComputeKitProvider({
|
|
10
|
+
options,
|
|
11
|
+
instance,
|
|
12
|
+
children
|
|
13
|
+
}) {
|
|
14
|
+
const kit = react.useMemo(() => {
|
|
15
|
+
return instance ?? new core.ComputeKit(options);
|
|
16
|
+
}, [instance, options]);
|
|
17
|
+
react.useEffect(() => {
|
|
18
|
+
return () => {
|
|
19
|
+
if (!instance) {
|
|
20
|
+
kit.terminate();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}, [kit, instance]);
|
|
24
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ComputeKitContext.Provider, { value: kit, children });
|
|
25
|
+
}
|
|
26
|
+
function useComputeKit() {
|
|
27
|
+
const kit = react.useContext(ComputeKitContext);
|
|
28
|
+
if (!kit) {
|
|
29
|
+
throw new Error("useComputeKit must be used within a ComputeKitProvider");
|
|
30
|
+
}
|
|
31
|
+
return kit;
|
|
32
|
+
}
|
|
33
|
+
function useCompute(functionName, options = {}) {
|
|
34
|
+
const kit = useComputeKit();
|
|
35
|
+
const abortControllerRef = react.useRef(null);
|
|
36
|
+
const [state, setState] = react.useState({
|
|
37
|
+
data: null,
|
|
38
|
+
loading: false,
|
|
39
|
+
error: null,
|
|
40
|
+
progress: null
|
|
41
|
+
});
|
|
42
|
+
const reset = react.useCallback(() => {
|
|
43
|
+
setState({
|
|
44
|
+
data: null,
|
|
45
|
+
loading: false,
|
|
46
|
+
error: null,
|
|
47
|
+
progress: null
|
|
48
|
+
});
|
|
49
|
+
}, []);
|
|
50
|
+
const cancel = react.useCallback(() => {
|
|
51
|
+
if (abortControllerRef.current) {
|
|
52
|
+
abortControllerRef.current.abort();
|
|
53
|
+
abortControllerRef.current = null;
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
const run = react.useCallback(
|
|
57
|
+
async (input, runOptions) => {
|
|
58
|
+
cancel();
|
|
59
|
+
const abortController = new AbortController();
|
|
60
|
+
abortControllerRef.current = abortController;
|
|
61
|
+
if (options.resetOnRun !== false) {
|
|
62
|
+
setState((prev) => ({
|
|
63
|
+
...prev,
|
|
64
|
+
loading: true,
|
|
65
|
+
error: null,
|
|
66
|
+
progress: null
|
|
67
|
+
}));
|
|
68
|
+
} else {
|
|
69
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const result = await kit.run(functionName, input, {
|
|
73
|
+
...options,
|
|
74
|
+
...runOptions,
|
|
75
|
+
signal: runOptions?.signal ?? abortController.signal,
|
|
76
|
+
onProgress: (progress) => {
|
|
77
|
+
setState((prev) => ({ ...prev, progress }));
|
|
78
|
+
options.onProgress?.(progress);
|
|
79
|
+
runOptions?.onProgress?.(progress);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (!abortController.signal.aborted) {
|
|
83
|
+
setState({
|
|
84
|
+
data: result,
|
|
85
|
+
loading: false,
|
|
86
|
+
error: null,
|
|
87
|
+
progress: null
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (!abortController.signal.aborted) {
|
|
92
|
+
setState({
|
|
93
|
+
data: null,
|
|
94
|
+
loading: false,
|
|
95
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
96
|
+
progress: null
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
[kit, functionName, options, cancel]
|
|
102
|
+
);
|
|
103
|
+
react.useEffect(() => {
|
|
104
|
+
if (options.autoRun && options.initialInput !== void 0) {
|
|
105
|
+
run(options.initialInput);
|
|
106
|
+
}
|
|
107
|
+
}, []);
|
|
108
|
+
react.useEffect(() => {
|
|
109
|
+
return () => {
|
|
110
|
+
cancel();
|
|
111
|
+
};
|
|
112
|
+
}, [cancel]);
|
|
113
|
+
return {
|
|
114
|
+
...state,
|
|
115
|
+
run,
|
|
116
|
+
reset,
|
|
117
|
+
cancel
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function useComputeCallback(functionName, options) {
|
|
121
|
+
const kit = useComputeKit();
|
|
122
|
+
return react.useCallback(
|
|
123
|
+
(input, runOptions) => {
|
|
124
|
+
return kit.run(functionName, input, {
|
|
125
|
+
...options,
|
|
126
|
+
...runOptions
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
[kit, functionName, options]
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
function useComputeFunction(name, fn, options) {
|
|
133
|
+
const kit = useComputeKit();
|
|
134
|
+
react.useEffect(() => {
|
|
135
|
+
kit.register(name, fn);
|
|
136
|
+
}, [kit, name, fn]);
|
|
137
|
+
return useCompute(name, options);
|
|
138
|
+
}
|
|
139
|
+
function usePoolStats(refreshInterval = 0) {
|
|
140
|
+
const kit = useComputeKit();
|
|
141
|
+
const [stats, setStats] = react.useState(() => kit.getStats());
|
|
142
|
+
react.useEffect(() => {
|
|
143
|
+
if (refreshInterval <= 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const interval = setInterval(() => {
|
|
147
|
+
setStats(kit.getStats());
|
|
148
|
+
}, refreshInterval);
|
|
149
|
+
return () => clearInterval(interval);
|
|
150
|
+
}, [kit, refreshInterval]);
|
|
151
|
+
return stats;
|
|
152
|
+
}
|
|
153
|
+
function useWasmSupport() {
|
|
154
|
+
const kit = useComputeKit();
|
|
155
|
+
return kit.isWasmSupported();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Object.defineProperty(exports, "ComputeKit", {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
get: function () { return core.ComputeKit; }
|
|
161
|
+
});
|
|
162
|
+
exports.ComputeKitProvider = ComputeKitProvider;
|
|
163
|
+
exports.useCompute = useCompute;
|
|
164
|
+
exports.useComputeCallback = useComputeCallback;
|
|
165
|
+
exports.useComputeFunction = useComputeFunction;
|
|
166
|
+
exports.useComputeKit = useComputeKit;
|
|
167
|
+
exports.usePoolStats = usePoolStats;
|
|
168
|
+
exports.useWasmSupport = useWasmSupport;
|
|
169
|
+
//# sourceMappingURL=index.cjs.map
|
|
170
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["createContext","useMemo","ComputeKit","useEffect","useContext","useRef","useState","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ComputeKit React Bindings\n * React hooks and utilities for ComputeKit\n */\n\nimport {\n useState,\n useEffect,\n useCallback,\n useRef,\n useMemo,\n createContext,\n useContext,\n type ReactNode,\n} from 'react';\n\nimport {\n ComputeKit,\n type ComputeKitOptions,\n type ComputeOptions,\n type ComputeProgress,\n type PoolStats,\n} from '@computekit/core';\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ComputeKitContext = createContext<ComputeKit | null>(null);\n\n/**\n * Props for ComputeKitProvider\n */\nexport interface ComputeKitProviderProps {\n /** ComputeKit options */\n options?: ComputeKitOptions;\n /** Custom ComputeKit instance */\n instance?: ComputeKit;\n /** Children */\n children: ReactNode;\n}\n\n/**\n * Provider component for ComputeKit\n *\n * @example\n * ```tsx\n * import { ComputeKitProvider } from '@computekit/react';\n *\n * function App() {\n * return (\n * <ComputeKitProvider options={{ maxWorkers: 4 }}>\n * <MyApp />\n * </ComputeKitProvider>\n * );\n * }\n * ```\n */\nexport function ComputeKitProvider({\n options,\n instance,\n children,\n}: ComputeKitProviderProps): JSX.Element {\n const kit = useMemo(() => {\n return instance ?? new ComputeKit(options);\n }, [instance, options]);\n\n useEffect(() => {\n return () => {\n // Only terminate if we created the instance\n if (!instance) {\n kit.terminate();\n }\n };\n }, [kit, instance]);\n\n return <ComputeKitContext.Provider value={kit}>{children}</ComputeKitContext.Provider>;\n}\n\n/**\n * Get the ComputeKit instance from context\n */\nexport function useComputeKit(): ComputeKit {\n const kit = useContext(ComputeKitContext);\n if (!kit) {\n throw new Error('useComputeKit must be used within a ComputeKitProvider');\n }\n return kit;\n}\n\n// ============================================================================\n// useCompute Hook\n// ============================================================================\n\n/**\n * State returned by useCompute\n */\nexport interface UseComputeState<T> {\n /** The computed result */\n data: T | null;\n /** Loading state */\n loading: boolean;\n /** Error if computation failed */\n error: Error | null;\n /** Progress information */\n progress: ComputeProgress | null;\n}\n\n/**\n * Actions returned by useCompute\n */\nexport interface UseComputeActions<TInput> {\n /** Execute the compute function */\n run: (input: TInput, options?: ComputeOptions) => Promise<void>;\n /** Reset the state */\n reset: () => void;\n /** Cancel ongoing computation */\n cancel: () => void;\n}\n\n/**\n * Return type for useCompute\n */\nexport type UseComputeReturn<TInput, TOutput> = UseComputeState<TOutput> &\n UseComputeActions<TInput>;\n\n/**\n * Options for useCompute hook\n */\nexport interface UseComputeOptions extends ComputeOptions {\n /** Automatically run on mount with initial input */\n autoRun?: boolean;\n /** Initial input for autoRun */\n initialInput?: unknown;\n /** Reset state on new run */\n resetOnRun?: boolean;\n}\n\n/**\n * Hook for running compute functions\n *\n * @example\n * ```tsx\n * function FibonacciCalculator() {\n * const { data, loading, error, run } = useCompute<number, number>('fibonacci');\n *\n * return (\n * <div>\n * <button onClick={() => run(50)} disabled={loading}>\n * Calculate Fibonacci(50)\n * </button>\n * {loading && <p>Computing...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {data !== null && <p>Result: {data}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useCompute<TInput = unknown, TOutput = unknown>(\n functionName: string,\n options: UseComputeOptions = {}\n): UseComputeReturn<TInput, TOutput> {\n const kit = useComputeKit();\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const [state, setState] = useState<UseComputeState<TOutput>>({\n data: null,\n loading: false,\n error: null,\n progress: null,\n });\n\n const reset = useCallback(() => {\n setState({\n data: null,\n loading: false,\n error: null,\n progress: null,\n });\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n }\n }, []);\n\n const run = useCallback(\n async (input: TInput, runOptions?: ComputeOptions) => {\n // Cancel any ongoing computation\n cancel();\n\n // Create new abort controller\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // Reset state if configured\n if (options.resetOnRun !== false) {\n setState((prev) => ({\n ...prev,\n loading: true,\n error: null,\n progress: null,\n }));\n } else {\n setState((prev) => ({ ...prev, loading: true }));\n }\n\n try {\n const result = await kit.run<TInput, TOutput>(functionName, input, {\n ...options,\n ...runOptions,\n signal: runOptions?.signal ?? abortController.signal,\n onProgress: (progress) => {\n setState((prev) => ({ ...prev, progress }));\n options.onProgress?.(progress);\n runOptions?.onProgress?.(progress);\n },\n });\n\n if (!abortController.signal.aborted) {\n setState({\n data: result,\n loading: false,\n error: null,\n progress: null,\n });\n }\n } catch (err) {\n if (!abortController.signal.aborted) {\n setState({\n data: null,\n loading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n progress: null,\n });\n }\n }\n },\n [kit, functionName, options, cancel]\n );\n\n // Auto-run on mount if configured\n useEffect(() => {\n if (options.autoRun && options.initialInput !== undefined) {\n run(options.initialInput as TInput);\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cancel();\n };\n }, [cancel]);\n\n return {\n ...state,\n run,\n reset,\n cancel,\n };\n}\n\n// ============================================================================\n// useComputeCallback Hook\n// ============================================================================\n\n/**\n * Hook that returns a memoized async function for compute operations\n *\n * @example\n * ```tsx\n * function Calculator() {\n * const calculate = useComputeCallback<number[], number>('sum');\n *\n * const handleClick = async () => {\n * const result = await calculate([1, 2, 3, 4, 5]);\n * console.log(result);\n * };\n *\n * return <button onClick={handleClick}>Calculate Sum</button>;\n * }\n * ```\n */\nexport function useComputeCallback<TInput = unknown, TOutput = unknown>(\n functionName: string,\n options?: ComputeOptions\n): (input: TInput, runOptions?: ComputeOptions) => Promise<TOutput> {\n const kit = useComputeKit();\n\n return useCallback(\n (input: TInput, runOptions?: ComputeOptions) => {\n return kit.run<TInput, TOutput>(functionName, input, {\n ...options,\n ...runOptions,\n });\n },\n [kit, functionName, options]\n );\n}\n\n// ============================================================================\n// useComputeFunction Hook\n// ============================================================================\n\n/**\n * Hook to register and use a compute function\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { run, loading, data } = useComputeFunction(\n * 'myFunction',\n * (input: number) => input * 2\n * );\n *\n * return (\n * <button onClick={() => run(5)} disabled={loading}>\n * {loading ? 'Computing...' : `Result: ${data}`}\n * </button>\n * );\n * }\n * ```\n */\nexport function useComputeFunction<TInput = unknown, TOutput = unknown>(\n name: string,\n fn: (input: TInput) => TOutput | Promise<TOutput>,\n options?: UseComputeOptions\n): UseComputeReturn<TInput, TOutput> {\n const kit = useComputeKit();\n\n // Register function on mount\n useEffect(() => {\n kit.register(name, fn);\n }, [kit, name, fn]);\n\n return useCompute<TInput, TOutput>(name, options);\n}\n\n// ============================================================================\n// usePoolStats Hook\n// ============================================================================\n\n/**\n * Hook to get worker pool statistics\n *\n * @example\n * ```tsx\n * function PoolMonitor() {\n * const stats = usePoolStats(1000); // Update every second\n *\n * return (\n * <div>\n * <p>Active Workers: {stats.activeWorkers}</p>\n * <p>Queue Length: {stats.queueLength}</p>\n * <p>Tasks Completed: {stats.tasksCompleted}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function usePoolStats(refreshInterval: number = 0): PoolStats {\n const kit = useComputeKit();\n const [stats, setStats] = useState<PoolStats>(() => kit.getStats());\n\n useEffect(() => {\n // For one-time fetch (refreshInterval <= 0), we rely on the initial state\n if (refreshInterval <= 0) {\n return;\n }\n\n const interval = setInterval(() => {\n setStats(kit.getStats());\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [kit, refreshInterval]);\n\n return stats;\n}\n\n// ============================================================================\n// useWasmSupport Hook\n// ============================================================================\n\n/**\n * Hook to check WASM support\n */\nexport function useWasmSupport(): boolean {\n const kit = useComputeKit();\n return kit.isWasmSupported();\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport type {\n ComputeKitOptions,\n ComputeOptions,\n ComputeProgress,\n PoolStats,\n} from '@computekit/core';\n\nexport { ComputeKit } from '@computekit/core';\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ComputeKitOptions, ComputeKit, ComputeProgress, ComputeOptions, PoolStats } from '@computekit/core';
|
|
3
|
+
export { ComputeKit, ComputeKitOptions, ComputeOptions, ComputeProgress, PoolStats } from '@computekit/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ComputeKit React Bindings
|
|
7
|
+
* React hooks and utilities for ComputeKit
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for ComputeKitProvider
|
|
12
|
+
*/
|
|
13
|
+
interface ComputeKitProviderProps {
|
|
14
|
+
/** ComputeKit options */
|
|
15
|
+
options?: ComputeKitOptions;
|
|
16
|
+
/** Custom ComputeKit instance */
|
|
17
|
+
instance?: ComputeKit;
|
|
18
|
+
/** Children */
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Provider component for ComputeKit
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { ComputeKitProvider } from '@computekit/react';
|
|
27
|
+
*
|
|
28
|
+
* function App() {
|
|
29
|
+
* return (
|
|
30
|
+
* <ComputeKitProvider options={{ maxWorkers: 4 }}>
|
|
31
|
+
* <MyApp />
|
|
32
|
+
* </ComputeKitProvider>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function ComputeKitProvider({ options, instance, children, }: ComputeKitProviderProps): JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* Get the ComputeKit instance from context
|
|
40
|
+
*/
|
|
41
|
+
declare function useComputeKit(): ComputeKit;
|
|
42
|
+
/**
|
|
43
|
+
* State returned by useCompute
|
|
44
|
+
*/
|
|
45
|
+
interface UseComputeState<T> {
|
|
46
|
+
/** The computed result */
|
|
47
|
+
data: T | null;
|
|
48
|
+
/** Loading state */
|
|
49
|
+
loading: boolean;
|
|
50
|
+
/** Error if computation failed */
|
|
51
|
+
error: Error | null;
|
|
52
|
+
/** Progress information */
|
|
53
|
+
progress: ComputeProgress | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Actions returned by useCompute
|
|
57
|
+
*/
|
|
58
|
+
interface UseComputeActions<TInput> {
|
|
59
|
+
/** Execute the compute function */
|
|
60
|
+
run: (input: TInput, options?: ComputeOptions) => Promise<void>;
|
|
61
|
+
/** Reset the state */
|
|
62
|
+
reset: () => void;
|
|
63
|
+
/** Cancel ongoing computation */
|
|
64
|
+
cancel: () => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return type for useCompute
|
|
68
|
+
*/
|
|
69
|
+
type UseComputeReturn<TInput, TOutput> = UseComputeState<TOutput> & UseComputeActions<TInput>;
|
|
70
|
+
/**
|
|
71
|
+
* Options for useCompute hook
|
|
72
|
+
*/
|
|
73
|
+
interface UseComputeOptions extends ComputeOptions {
|
|
74
|
+
/** Automatically run on mount with initial input */
|
|
75
|
+
autoRun?: boolean;
|
|
76
|
+
/** Initial input for autoRun */
|
|
77
|
+
initialInput?: unknown;
|
|
78
|
+
/** Reset state on new run */
|
|
79
|
+
resetOnRun?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Hook for running compute functions
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* function FibonacciCalculator() {
|
|
87
|
+
* const { data, loading, error, run } = useCompute<number, number>('fibonacci');
|
|
88
|
+
*
|
|
89
|
+
* return (
|
|
90
|
+
* <div>
|
|
91
|
+
* <button onClick={() => run(50)} disabled={loading}>
|
|
92
|
+
* Calculate Fibonacci(50)
|
|
93
|
+
* </button>
|
|
94
|
+
* {loading && <p>Computing...</p>}
|
|
95
|
+
* {error && <p>Error: {error.message}</p>}
|
|
96
|
+
* {data !== null && <p>Result: {data}</p>}
|
|
97
|
+
* </div>
|
|
98
|
+
* );
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare function useCompute<TInput = unknown, TOutput = unknown>(functionName: string, options?: UseComputeOptions): UseComputeReturn<TInput, TOutput>;
|
|
103
|
+
/**
|
|
104
|
+
* Hook that returns a memoized async function for compute operations
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function Calculator() {
|
|
109
|
+
* const calculate = useComputeCallback<number[], number>('sum');
|
|
110
|
+
*
|
|
111
|
+
* const handleClick = async () => {
|
|
112
|
+
* const result = await calculate([1, 2, 3, 4, 5]);
|
|
113
|
+
* console.log(result);
|
|
114
|
+
* };
|
|
115
|
+
*
|
|
116
|
+
* return <button onClick={handleClick}>Calculate Sum</button>;
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function useComputeCallback<TInput = unknown, TOutput = unknown>(functionName: string, options?: ComputeOptions): (input: TInput, runOptions?: ComputeOptions) => Promise<TOutput>;
|
|
121
|
+
/**
|
|
122
|
+
* Hook to register and use a compute function
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* function MyComponent() {
|
|
127
|
+
* const { run, loading, data } = useComputeFunction(
|
|
128
|
+
* 'myFunction',
|
|
129
|
+
* (input: number) => input * 2
|
|
130
|
+
* );
|
|
131
|
+
*
|
|
132
|
+
* return (
|
|
133
|
+
* <button onClick={() => run(5)} disabled={loading}>
|
|
134
|
+
* {loading ? 'Computing...' : `Result: ${data}`}
|
|
135
|
+
* </button>
|
|
136
|
+
* );
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function useComputeFunction<TInput = unknown, TOutput = unknown>(name: string, fn: (input: TInput) => TOutput | Promise<TOutput>, options?: UseComputeOptions): UseComputeReturn<TInput, TOutput>;
|
|
141
|
+
/**
|
|
142
|
+
* Hook to get worker pool statistics
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```tsx
|
|
146
|
+
* function PoolMonitor() {
|
|
147
|
+
* const stats = usePoolStats(1000); // Update every second
|
|
148
|
+
*
|
|
149
|
+
* return (
|
|
150
|
+
* <div>
|
|
151
|
+
* <p>Active Workers: {stats.activeWorkers}</p>
|
|
152
|
+
* <p>Queue Length: {stats.queueLength}</p>
|
|
153
|
+
* <p>Tasks Completed: {stats.tasksCompleted}</p>
|
|
154
|
+
* </div>
|
|
155
|
+
* );
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function usePoolStats(refreshInterval?: number): PoolStats;
|
|
160
|
+
/**
|
|
161
|
+
* Hook to check WASM support
|
|
162
|
+
*/
|
|
163
|
+
declare function useWasmSupport(): boolean;
|
|
164
|
+
|
|
165
|
+
export { ComputeKitProvider, type ComputeKitProviderProps, type UseComputeActions, type UseComputeOptions, type UseComputeReturn, type UseComputeState, useCompute, useComputeCallback, useComputeFunction, useComputeKit, usePoolStats, useWasmSupport };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ComputeKitOptions, ComputeKit, ComputeProgress, ComputeOptions, PoolStats } from '@computekit/core';
|
|
3
|
+
export { ComputeKit, ComputeKitOptions, ComputeOptions, ComputeProgress, PoolStats } from '@computekit/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ComputeKit React Bindings
|
|
7
|
+
* React hooks and utilities for ComputeKit
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for ComputeKitProvider
|
|
12
|
+
*/
|
|
13
|
+
interface ComputeKitProviderProps {
|
|
14
|
+
/** ComputeKit options */
|
|
15
|
+
options?: ComputeKitOptions;
|
|
16
|
+
/** Custom ComputeKit instance */
|
|
17
|
+
instance?: ComputeKit;
|
|
18
|
+
/** Children */
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Provider component for ComputeKit
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { ComputeKitProvider } from '@computekit/react';
|
|
27
|
+
*
|
|
28
|
+
* function App() {
|
|
29
|
+
* return (
|
|
30
|
+
* <ComputeKitProvider options={{ maxWorkers: 4 }}>
|
|
31
|
+
* <MyApp />
|
|
32
|
+
* </ComputeKitProvider>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function ComputeKitProvider({ options, instance, children, }: ComputeKitProviderProps): JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* Get the ComputeKit instance from context
|
|
40
|
+
*/
|
|
41
|
+
declare function useComputeKit(): ComputeKit;
|
|
42
|
+
/**
|
|
43
|
+
* State returned by useCompute
|
|
44
|
+
*/
|
|
45
|
+
interface UseComputeState<T> {
|
|
46
|
+
/** The computed result */
|
|
47
|
+
data: T | null;
|
|
48
|
+
/** Loading state */
|
|
49
|
+
loading: boolean;
|
|
50
|
+
/** Error if computation failed */
|
|
51
|
+
error: Error | null;
|
|
52
|
+
/** Progress information */
|
|
53
|
+
progress: ComputeProgress | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Actions returned by useCompute
|
|
57
|
+
*/
|
|
58
|
+
interface UseComputeActions<TInput> {
|
|
59
|
+
/** Execute the compute function */
|
|
60
|
+
run: (input: TInput, options?: ComputeOptions) => Promise<void>;
|
|
61
|
+
/** Reset the state */
|
|
62
|
+
reset: () => void;
|
|
63
|
+
/** Cancel ongoing computation */
|
|
64
|
+
cancel: () => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return type for useCompute
|
|
68
|
+
*/
|
|
69
|
+
type UseComputeReturn<TInput, TOutput> = UseComputeState<TOutput> & UseComputeActions<TInput>;
|
|
70
|
+
/**
|
|
71
|
+
* Options for useCompute hook
|
|
72
|
+
*/
|
|
73
|
+
interface UseComputeOptions extends ComputeOptions {
|
|
74
|
+
/** Automatically run on mount with initial input */
|
|
75
|
+
autoRun?: boolean;
|
|
76
|
+
/** Initial input for autoRun */
|
|
77
|
+
initialInput?: unknown;
|
|
78
|
+
/** Reset state on new run */
|
|
79
|
+
resetOnRun?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Hook for running compute functions
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* function FibonacciCalculator() {
|
|
87
|
+
* const { data, loading, error, run } = useCompute<number, number>('fibonacci');
|
|
88
|
+
*
|
|
89
|
+
* return (
|
|
90
|
+
* <div>
|
|
91
|
+
* <button onClick={() => run(50)} disabled={loading}>
|
|
92
|
+
* Calculate Fibonacci(50)
|
|
93
|
+
* </button>
|
|
94
|
+
* {loading && <p>Computing...</p>}
|
|
95
|
+
* {error && <p>Error: {error.message}</p>}
|
|
96
|
+
* {data !== null && <p>Result: {data}</p>}
|
|
97
|
+
* </div>
|
|
98
|
+
* );
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare function useCompute<TInput = unknown, TOutput = unknown>(functionName: string, options?: UseComputeOptions): UseComputeReturn<TInput, TOutput>;
|
|
103
|
+
/**
|
|
104
|
+
* Hook that returns a memoized async function for compute operations
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function Calculator() {
|
|
109
|
+
* const calculate = useComputeCallback<number[], number>('sum');
|
|
110
|
+
*
|
|
111
|
+
* const handleClick = async () => {
|
|
112
|
+
* const result = await calculate([1, 2, 3, 4, 5]);
|
|
113
|
+
* console.log(result);
|
|
114
|
+
* };
|
|
115
|
+
*
|
|
116
|
+
* return <button onClick={handleClick}>Calculate Sum</button>;
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function useComputeCallback<TInput = unknown, TOutput = unknown>(functionName: string, options?: ComputeOptions): (input: TInput, runOptions?: ComputeOptions) => Promise<TOutput>;
|
|
121
|
+
/**
|
|
122
|
+
* Hook to register and use a compute function
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* function MyComponent() {
|
|
127
|
+
* const { run, loading, data } = useComputeFunction(
|
|
128
|
+
* 'myFunction',
|
|
129
|
+
* (input: number) => input * 2
|
|
130
|
+
* );
|
|
131
|
+
*
|
|
132
|
+
* return (
|
|
133
|
+
* <button onClick={() => run(5)} disabled={loading}>
|
|
134
|
+
* {loading ? 'Computing...' : `Result: ${data}`}
|
|
135
|
+
* </button>
|
|
136
|
+
* );
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function useComputeFunction<TInput = unknown, TOutput = unknown>(name: string, fn: (input: TInput) => TOutput | Promise<TOutput>, options?: UseComputeOptions): UseComputeReturn<TInput, TOutput>;
|
|
141
|
+
/**
|
|
142
|
+
* Hook to get worker pool statistics
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```tsx
|
|
146
|
+
* function PoolMonitor() {
|
|
147
|
+
* const stats = usePoolStats(1000); // Update every second
|
|
148
|
+
*
|
|
149
|
+
* return (
|
|
150
|
+
* <div>
|
|
151
|
+
* <p>Active Workers: {stats.activeWorkers}</p>
|
|
152
|
+
* <p>Queue Length: {stats.queueLength}</p>
|
|
153
|
+
* <p>Tasks Completed: {stats.tasksCompleted}</p>
|
|
154
|
+
* </div>
|
|
155
|
+
* );
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function usePoolStats(refreshInterval?: number): PoolStats;
|
|
160
|
+
/**
|
|
161
|
+
* Hook to check WASM support
|
|
162
|
+
*/
|
|
163
|
+
declare function useWasmSupport(): boolean;
|
|
164
|
+
|
|
165
|
+
export { ComputeKitProvider, type ComputeKitProviderProps, type UseComputeActions, type UseComputeOptions, type UseComputeReturn, type UseComputeState, useCompute, useComputeCallback, useComputeFunction, useComputeKit, usePoolStats, useWasmSupport };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { createContext, useMemo, useEffect, useContext, useRef, useState, useCallback } from 'react';
|
|
2
|
+
import { ComputeKit } from '@computekit/core';
|
|
3
|
+
export { ComputeKit } from '@computekit/core';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/index.tsx
|
|
7
|
+
var ComputeKitContext = createContext(null);
|
|
8
|
+
function ComputeKitProvider({
|
|
9
|
+
options,
|
|
10
|
+
instance,
|
|
11
|
+
children
|
|
12
|
+
}) {
|
|
13
|
+
const kit = useMemo(() => {
|
|
14
|
+
return instance ?? new ComputeKit(options);
|
|
15
|
+
}, [instance, options]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
return () => {
|
|
18
|
+
if (!instance) {
|
|
19
|
+
kit.terminate();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}, [kit, instance]);
|
|
23
|
+
return /* @__PURE__ */ jsx(ComputeKitContext.Provider, { value: kit, children });
|
|
24
|
+
}
|
|
25
|
+
function useComputeKit() {
|
|
26
|
+
const kit = useContext(ComputeKitContext);
|
|
27
|
+
if (!kit) {
|
|
28
|
+
throw new Error("useComputeKit must be used within a ComputeKitProvider");
|
|
29
|
+
}
|
|
30
|
+
return kit;
|
|
31
|
+
}
|
|
32
|
+
function useCompute(functionName, options = {}) {
|
|
33
|
+
const kit = useComputeKit();
|
|
34
|
+
const abortControllerRef = useRef(null);
|
|
35
|
+
const [state, setState] = useState({
|
|
36
|
+
data: null,
|
|
37
|
+
loading: false,
|
|
38
|
+
error: null,
|
|
39
|
+
progress: null
|
|
40
|
+
});
|
|
41
|
+
const reset = useCallback(() => {
|
|
42
|
+
setState({
|
|
43
|
+
data: null,
|
|
44
|
+
loading: false,
|
|
45
|
+
error: null,
|
|
46
|
+
progress: null
|
|
47
|
+
});
|
|
48
|
+
}, []);
|
|
49
|
+
const cancel = useCallback(() => {
|
|
50
|
+
if (abortControllerRef.current) {
|
|
51
|
+
abortControllerRef.current.abort();
|
|
52
|
+
abortControllerRef.current = null;
|
|
53
|
+
}
|
|
54
|
+
}, []);
|
|
55
|
+
const run = useCallback(
|
|
56
|
+
async (input, runOptions) => {
|
|
57
|
+
cancel();
|
|
58
|
+
const abortController = new AbortController();
|
|
59
|
+
abortControllerRef.current = abortController;
|
|
60
|
+
if (options.resetOnRun !== false) {
|
|
61
|
+
setState((prev) => ({
|
|
62
|
+
...prev,
|
|
63
|
+
loading: true,
|
|
64
|
+
error: null,
|
|
65
|
+
progress: null
|
|
66
|
+
}));
|
|
67
|
+
} else {
|
|
68
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const result = await kit.run(functionName, input, {
|
|
72
|
+
...options,
|
|
73
|
+
...runOptions,
|
|
74
|
+
signal: runOptions?.signal ?? abortController.signal,
|
|
75
|
+
onProgress: (progress) => {
|
|
76
|
+
setState((prev) => ({ ...prev, progress }));
|
|
77
|
+
options.onProgress?.(progress);
|
|
78
|
+
runOptions?.onProgress?.(progress);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (!abortController.signal.aborted) {
|
|
82
|
+
setState({
|
|
83
|
+
data: result,
|
|
84
|
+
loading: false,
|
|
85
|
+
error: null,
|
|
86
|
+
progress: null
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (!abortController.signal.aborted) {
|
|
91
|
+
setState({
|
|
92
|
+
data: null,
|
|
93
|
+
loading: false,
|
|
94
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
95
|
+
progress: null
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[kit, functionName, options, cancel]
|
|
101
|
+
);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (options.autoRun && options.initialInput !== void 0) {
|
|
104
|
+
run(options.initialInput);
|
|
105
|
+
}
|
|
106
|
+
}, []);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
return () => {
|
|
109
|
+
cancel();
|
|
110
|
+
};
|
|
111
|
+
}, [cancel]);
|
|
112
|
+
return {
|
|
113
|
+
...state,
|
|
114
|
+
run,
|
|
115
|
+
reset,
|
|
116
|
+
cancel
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function useComputeCallback(functionName, options) {
|
|
120
|
+
const kit = useComputeKit();
|
|
121
|
+
return useCallback(
|
|
122
|
+
(input, runOptions) => {
|
|
123
|
+
return kit.run(functionName, input, {
|
|
124
|
+
...options,
|
|
125
|
+
...runOptions
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
[kit, functionName, options]
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function useComputeFunction(name, fn, options) {
|
|
132
|
+
const kit = useComputeKit();
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
kit.register(name, fn);
|
|
135
|
+
}, [kit, name, fn]);
|
|
136
|
+
return useCompute(name, options);
|
|
137
|
+
}
|
|
138
|
+
function usePoolStats(refreshInterval = 0) {
|
|
139
|
+
const kit = useComputeKit();
|
|
140
|
+
const [stats, setStats] = useState(() => kit.getStats());
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (refreshInterval <= 0) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const interval = setInterval(() => {
|
|
146
|
+
setStats(kit.getStats());
|
|
147
|
+
}, refreshInterval);
|
|
148
|
+
return () => clearInterval(interval);
|
|
149
|
+
}, [kit, refreshInterval]);
|
|
150
|
+
return stats;
|
|
151
|
+
}
|
|
152
|
+
function useWasmSupport() {
|
|
153
|
+
const kit = useComputeKit();
|
|
154
|
+
return kit.isWasmSupported();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { ComputeKitProvider, useCompute, useComputeCallback, useComputeFunction, useComputeKit, usePoolStats, useWasmSupport };
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
159
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ComputeKit React Bindings\n * React hooks and utilities for ComputeKit\n */\n\nimport {\n useState,\n useEffect,\n useCallback,\n useRef,\n useMemo,\n createContext,\n useContext,\n type ReactNode,\n} from 'react';\n\nimport {\n ComputeKit,\n type ComputeKitOptions,\n type ComputeOptions,\n type ComputeProgress,\n type PoolStats,\n} from '@computekit/core';\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ComputeKitContext = createContext<ComputeKit | null>(null);\n\n/**\n * Props for ComputeKitProvider\n */\nexport interface ComputeKitProviderProps {\n /** ComputeKit options */\n options?: ComputeKitOptions;\n /** Custom ComputeKit instance */\n instance?: ComputeKit;\n /** Children */\n children: ReactNode;\n}\n\n/**\n * Provider component for ComputeKit\n *\n * @example\n * ```tsx\n * import { ComputeKitProvider } from '@computekit/react';\n *\n * function App() {\n * return (\n * <ComputeKitProvider options={{ maxWorkers: 4 }}>\n * <MyApp />\n * </ComputeKitProvider>\n * );\n * }\n * ```\n */\nexport function ComputeKitProvider({\n options,\n instance,\n children,\n}: ComputeKitProviderProps): JSX.Element {\n const kit = useMemo(() => {\n return instance ?? new ComputeKit(options);\n }, [instance, options]);\n\n useEffect(() => {\n return () => {\n // Only terminate if we created the instance\n if (!instance) {\n kit.terminate();\n }\n };\n }, [kit, instance]);\n\n return <ComputeKitContext.Provider value={kit}>{children}</ComputeKitContext.Provider>;\n}\n\n/**\n * Get the ComputeKit instance from context\n */\nexport function useComputeKit(): ComputeKit {\n const kit = useContext(ComputeKitContext);\n if (!kit) {\n throw new Error('useComputeKit must be used within a ComputeKitProvider');\n }\n return kit;\n}\n\n// ============================================================================\n// useCompute Hook\n// ============================================================================\n\n/**\n * State returned by useCompute\n */\nexport interface UseComputeState<T> {\n /** The computed result */\n data: T | null;\n /** Loading state */\n loading: boolean;\n /** Error if computation failed */\n error: Error | null;\n /** Progress information */\n progress: ComputeProgress | null;\n}\n\n/**\n * Actions returned by useCompute\n */\nexport interface UseComputeActions<TInput> {\n /** Execute the compute function */\n run: (input: TInput, options?: ComputeOptions) => Promise<void>;\n /** Reset the state */\n reset: () => void;\n /** Cancel ongoing computation */\n cancel: () => void;\n}\n\n/**\n * Return type for useCompute\n */\nexport type UseComputeReturn<TInput, TOutput> = UseComputeState<TOutput> &\n UseComputeActions<TInput>;\n\n/**\n * Options for useCompute hook\n */\nexport interface UseComputeOptions extends ComputeOptions {\n /** Automatically run on mount with initial input */\n autoRun?: boolean;\n /** Initial input for autoRun */\n initialInput?: unknown;\n /** Reset state on new run */\n resetOnRun?: boolean;\n}\n\n/**\n * Hook for running compute functions\n *\n * @example\n * ```tsx\n * function FibonacciCalculator() {\n * const { data, loading, error, run } = useCompute<number, number>('fibonacci');\n *\n * return (\n * <div>\n * <button onClick={() => run(50)} disabled={loading}>\n * Calculate Fibonacci(50)\n * </button>\n * {loading && <p>Computing...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {data !== null && <p>Result: {data}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useCompute<TInput = unknown, TOutput = unknown>(\n functionName: string,\n options: UseComputeOptions = {}\n): UseComputeReturn<TInput, TOutput> {\n const kit = useComputeKit();\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const [state, setState] = useState<UseComputeState<TOutput>>({\n data: null,\n loading: false,\n error: null,\n progress: null,\n });\n\n const reset = useCallback(() => {\n setState({\n data: null,\n loading: false,\n error: null,\n progress: null,\n });\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n }\n }, []);\n\n const run = useCallback(\n async (input: TInput, runOptions?: ComputeOptions) => {\n // Cancel any ongoing computation\n cancel();\n\n // Create new abort controller\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // Reset state if configured\n if (options.resetOnRun !== false) {\n setState((prev) => ({\n ...prev,\n loading: true,\n error: null,\n progress: null,\n }));\n } else {\n setState((prev) => ({ ...prev, loading: true }));\n }\n\n try {\n const result = await kit.run<TInput, TOutput>(functionName, input, {\n ...options,\n ...runOptions,\n signal: runOptions?.signal ?? abortController.signal,\n onProgress: (progress) => {\n setState((prev) => ({ ...prev, progress }));\n options.onProgress?.(progress);\n runOptions?.onProgress?.(progress);\n },\n });\n\n if (!abortController.signal.aborted) {\n setState({\n data: result,\n loading: false,\n error: null,\n progress: null,\n });\n }\n } catch (err) {\n if (!abortController.signal.aborted) {\n setState({\n data: null,\n loading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n progress: null,\n });\n }\n }\n },\n [kit, functionName, options, cancel]\n );\n\n // Auto-run on mount if configured\n useEffect(() => {\n if (options.autoRun && options.initialInput !== undefined) {\n run(options.initialInput as TInput);\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cancel();\n };\n }, [cancel]);\n\n return {\n ...state,\n run,\n reset,\n cancel,\n };\n}\n\n// ============================================================================\n// useComputeCallback Hook\n// ============================================================================\n\n/**\n * Hook that returns a memoized async function for compute operations\n *\n * @example\n * ```tsx\n * function Calculator() {\n * const calculate = useComputeCallback<number[], number>('sum');\n *\n * const handleClick = async () => {\n * const result = await calculate([1, 2, 3, 4, 5]);\n * console.log(result);\n * };\n *\n * return <button onClick={handleClick}>Calculate Sum</button>;\n * }\n * ```\n */\nexport function useComputeCallback<TInput = unknown, TOutput = unknown>(\n functionName: string,\n options?: ComputeOptions\n): (input: TInput, runOptions?: ComputeOptions) => Promise<TOutput> {\n const kit = useComputeKit();\n\n return useCallback(\n (input: TInput, runOptions?: ComputeOptions) => {\n return kit.run<TInput, TOutput>(functionName, input, {\n ...options,\n ...runOptions,\n });\n },\n [kit, functionName, options]\n );\n}\n\n// ============================================================================\n// useComputeFunction Hook\n// ============================================================================\n\n/**\n * Hook to register and use a compute function\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { run, loading, data } = useComputeFunction(\n * 'myFunction',\n * (input: number) => input * 2\n * );\n *\n * return (\n * <button onClick={() => run(5)} disabled={loading}>\n * {loading ? 'Computing...' : `Result: ${data}`}\n * </button>\n * );\n * }\n * ```\n */\nexport function useComputeFunction<TInput = unknown, TOutput = unknown>(\n name: string,\n fn: (input: TInput) => TOutput | Promise<TOutput>,\n options?: UseComputeOptions\n): UseComputeReturn<TInput, TOutput> {\n const kit = useComputeKit();\n\n // Register function on mount\n useEffect(() => {\n kit.register(name, fn);\n }, [kit, name, fn]);\n\n return useCompute<TInput, TOutput>(name, options);\n}\n\n// ============================================================================\n// usePoolStats Hook\n// ============================================================================\n\n/**\n * Hook to get worker pool statistics\n *\n * @example\n * ```tsx\n * function PoolMonitor() {\n * const stats = usePoolStats(1000); // Update every second\n *\n * return (\n * <div>\n * <p>Active Workers: {stats.activeWorkers}</p>\n * <p>Queue Length: {stats.queueLength}</p>\n * <p>Tasks Completed: {stats.tasksCompleted}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function usePoolStats(refreshInterval: number = 0): PoolStats {\n const kit = useComputeKit();\n const [stats, setStats] = useState<PoolStats>(() => kit.getStats());\n\n useEffect(() => {\n // For one-time fetch (refreshInterval <= 0), we rely on the initial state\n if (refreshInterval <= 0) {\n return;\n }\n\n const interval = setInterval(() => {\n setStats(kit.getStats());\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [kit, refreshInterval]);\n\n return stats;\n}\n\n// ============================================================================\n// useWasmSupport Hook\n// ============================================================================\n\n/**\n * Hook to check WASM support\n */\nexport function useWasmSupport(): boolean {\n const kit = useComputeKit();\n return kit.isWasmSupported();\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport type {\n ComputeKitOptions,\n ComputeOptions,\n ComputeProgress,\n PoolStats,\n} from '@computekit/core';\n\nexport { ComputeKit } from '@computekit/core';\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@computekit/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React bindings for ComputeKit - WASM + Worker toolkit",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"typecheck": "tsc --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@computekit/core": "*"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=17.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "^18.2.45",
|
|
34
|
+
"react": "^18.2.0",
|
|
35
|
+
"tsup": "^8.0.1",
|
|
36
|
+
"typescript": "^5.3.3",
|
|
37
|
+
"vitest": "^1.1.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"react",
|
|
41
|
+
"wasm",
|
|
42
|
+
"webassembly",
|
|
43
|
+
"workers",
|
|
44
|
+
"hooks",
|
|
45
|
+
"compute",
|
|
46
|
+
"performance"
|
|
47
|
+
],
|
|
48
|
+
"author": "Ghassen Lassoued <https://github.com/tapava>",
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://github.com/tapava/compute-kit",
|
|
53
|
+
"directory": "packages/react"
|
|
54
|
+
},
|
|
55
|
+
"sideEffects": false
|
|
56
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComputeKit React Bindings
|
|
3
|
+
* React hooks and utilities for ComputeKit
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
useState,
|
|
8
|
+
useEffect,
|
|
9
|
+
useCallback,
|
|
10
|
+
useRef,
|
|
11
|
+
useMemo,
|
|
12
|
+
createContext,
|
|
13
|
+
useContext,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ComputeKit,
|
|
19
|
+
type ComputeKitOptions,
|
|
20
|
+
type ComputeOptions,
|
|
21
|
+
type ComputeProgress,
|
|
22
|
+
type PoolStats,
|
|
23
|
+
} from '@computekit/core';
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Context
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
const ComputeKitContext = createContext<ComputeKit | null>(null);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Props for ComputeKitProvider
|
|
33
|
+
*/
|
|
34
|
+
export interface ComputeKitProviderProps {
|
|
35
|
+
/** ComputeKit options */
|
|
36
|
+
options?: ComputeKitOptions;
|
|
37
|
+
/** Custom ComputeKit instance */
|
|
38
|
+
instance?: ComputeKit;
|
|
39
|
+
/** Children */
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Provider component for ComputeKit
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* import { ComputeKitProvider } from '@computekit/react';
|
|
49
|
+
*
|
|
50
|
+
* function App() {
|
|
51
|
+
* return (
|
|
52
|
+
* <ComputeKitProvider options={{ maxWorkers: 4 }}>
|
|
53
|
+
* <MyApp />
|
|
54
|
+
* </ComputeKitProvider>
|
|
55
|
+
* );
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function ComputeKitProvider({
|
|
60
|
+
options,
|
|
61
|
+
instance,
|
|
62
|
+
children,
|
|
63
|
+
}: ComputeKitProviderProps): JSX.Element {
|
|
64
|
+
const kit = useMemo(() => {
|
|
65
|
+
return instance ?? new ComputeKit(options);
|
|
66
|
+
}, [instance, options]);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
return () => {
|
|
70
|
+
// Only terminate if we created the instance
|
|
71
|
+
if (!instance) {
|
|
72
|
+
kit.terminate();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}, [kit, instance]);
|
|
76
|
+
|
|
77
|
+
return <ComputeKitContext.Provider value={kit}>{children}</ComputeKitContext.Provider>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the ComputeKit instance from context
|
|
82
|
+
*/
|
|
83
|
+
export function useComputeKit(): ComputeKit {
|
|
84
|
+
const kit = useContext(ComputeKitContext);
|
|
85
|
+
if (!kit) {
|
|
86
|
+
throw new Error('useComputeKit must be used within a ComputeKitProvider');
|
|
87
|
+
}
|
|
88
|
+
return kit;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// useCompute Hook
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* State returned by useCompute
|
|
97
|
+
*/
|
|
98
|
+
export interface UseComputeState<T> {
|
|
99
|
+
/** The computed result */
|
|
100
|
+
data: T | null;
|
|
101
|
+
/** Loading state */
|
|
102
|
+
loading: boolean;
|
|
103
|
+
/** Error if computation failed */
|
|
104
|
+
error: Error | null;
|
|
105
|
+
/** Progress information */
|
|
106
|
+
progress: ComputeProgress | null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Actions returned by useCompute
|
|
111
|
+
*/
|
|
112
|
+
export interface UseComputeActions<TInput> {
|
|
113
|
+
/** Execute the compute function */
|
|
114
|
+
run: (input: TInput, options?: ComputeOptions) => Promise<void>;
|
|
115
|
+
/** Reset the state */
|
|
116
|
+
reset: () => void;
|
|
117
|
+
/** Cancel ongoing computation */
|
|
118
|
+
cancel: () => void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Return type for useCompute
|
|
123
|
+
*/
|
|
124
|
+
export type UseComputeReturn<TInput, TOutput> = UseComputeState<TOutput> &
|
|
125
|
+
UseComputeActions<TInput>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Options for useCompute hook
|
|
129
|
+
*/
|
|
130
|
+
export interface UseComputeOptions extends ComputeOptions {
|
|
131
|
+
/** Automatically run on mount with initial input */
|
|
132
|
+
autoRun?: boolean;
|
|
133
|
+
/** Initial input for autoRun */
|
|
134
|
+
initialInput?: unknown;
|
|
135
|
+
/** Reset state on new run */
|
|
136
|
+
resetOnRun?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Hook for running compute functions
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```tsx
|
|
144
|
+
* function FibonacciCalculator() {
|
|
145
|
+
* const { data, loading, error, run } = useCompute<number, number>('fibonacci');
|
|
146
|
+
*
|
|
147
|
+
* return (
|
|
148
|
+
* <div>
|
|
149
|
+
* <button onClick={() => run(50)} disabled={loading}>
|
|
150
|
+
* Calculate Fibonacci(50)
|
|
151
|
+
* </button>
|
|
152
|
+
* {loading && <p>Computing...</p>}
|
|
153
|
+
* {error && <p>Error: {error.message}</p>}
|
|
154
|
+
* {data !== null && <p>Result: {data}</p>}
|
|
155
|
+
* </div>
|
|
156
|
+
* );
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export function useCompute<TInput = unknown, TOutput = unknown>(
|
|
161
|
+
functionName: string,
|
|
162
|
+
options: UseComputeOptions = {}
|
|
163
|
+
): UseComputeReturn<TInput, TOutput> {
|
|
164
|
+
const kit = useComputeKit();
|
|
165
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
166
|
+
|
|
167
|
+
const [state, setState] = useState<UseComputeState<TOutput>>({
|
|
168
|
+
data: null,
|
|
169
|
+
loading: false,
|
|
170
|
+
error: null,
|
|
171
|
+
progress: null,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const reset = useCallback(() => {
|
|
175
|
+
setState({
|
|
176
|
+
data: null,
|
|
177
|
+
loading: false,
|
|
178
|
+
error: null,
|
|
179
|
+
progress: null,
|
|
180
|
+
});
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
const cancel = useCallback(() => {
|
|
184
|
+
if (abortControllerRef.current) {
|
|
185
|
+
abortControllerRef.current.abort();
|
|
186
|
+
abortControllerRef.current = null;
|
|
187
|
+
}
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
190
|
+
const run = useCallback(
|
|
191
|
+
async (input: TInput, runOptions?: ComputeOptions) => {
|
|
192
|
+
// Cancel any ongoing computation
|
|
193
|
+
cancel();
|
|
194
|
+
|
|
195
|
+
// Create new abort controller
|
|
196
|
+
const abortController = new AbortController();
|
|
197
|
+
abortControllerRef.current = abortController;
|
|
198
|
+
|
|
199
|
+
// Reset state if configured
|
|
200
|
+
if (options.resetOnRun !== false) {
|
|
201
|
+
setState((prev) => ({
|
|
202
|
+
...prev,
|
|
203
|
+
loading: true,
|
|
204
|
+
error: null,
|
|
205
|
+
progress: null,
|
|
206
|
+
}));
|
|
207
|
+
} else {
|
|
208
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const result = await kit.run<TInput, TOutput>(functionName, input, {
|
|
213
|
+
...options,
|
|
214
|
+
...runOptions,
|
|
215
|
+
signal: runOptions?.signal ?? abortController.signal,
|
|
216
|
+
onProgress: (progress) => {
|
|
217
|
+
setState((prev) => ({ ...prev, progress }));
|
|
218
|
+
options.onProgress?.(progress);
|
|
219
|
+
runOptions?.onProgress?.(progress);
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (!abortController.signal.aborted) {
|
|
224
|
+
setState({
|
|
225
|
+
data: result,
|
|
226
|
+
loading: false,
|
|
227
|
+
error: null,
|
|
228
|
+
progress: null,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
} catch (err) {
|
|
232
|
+
if (!abortController.signal.aborted) {
|
|
233
|
+
setState({
|
|
234
|
+
data: null,
|
|
235
|
+
loading: false,
|
|
236
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
237
|
+
progress: null,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
[kit, functionName, options, cancel]
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Auto-run on mount if configured
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (options.autoRun && options.initialInput !== undefined) {
|
|
248
|
+
run(options.initialInput as TInput);
|
|
249
|
+
}
|
|
250
|
+
// Only run on mount
|
|
251
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
// Cleanup on unmount
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
return () => {
|
|
257
|
+
cancel();
|
|
258
|
+
};
|
|
259
|
+
}, [cancel]);
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
...state,
|
|
263
|
+
run,
|
|
264
|
+
reset,
|
|
265
|
+
cancel,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// useComputeCallback Hook
|
|
271
|
+
// ============================================================================
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Hook that returns a memoized async function for compute operations
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```tsx
|
|
278
|
+
* function Calculator() {
|
|
279
|
+
* const calculate = useComputeCallback<number[], number>('sum');
|
|
280
|
+
*
|
|
281
|
+
* const handleClick = async () => {
|
|
282
|
+
* const result = await calculate([1, 2, 3, 4, 5]);
|
|
283
|
+
* console.log(result);
|
|
284
|
+
* };
|
|
285
|
+
*
|
|
286
|
+
* return <button onClick={handleClick}>Calculate Sum</button>;
|
|
287
|
+
* }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
export function useComputeCallback<TInput = unknown, TOutput = unknown>(
|
|
291
|
+
functionName: string,
|
|
292
|
+
options?: ComputeOptions
|
|
293
|
+
): (input: TInput, runOptions?: ComputeOptions) => Promise<TOutput> {
|
|
294
|
+
const kit = useComputeKit();
|
|
295
|
+
|
|
296
|
+
return useCallback(
|
|
297
|
+
(input: TInput, runOptions?: ComputeOptions) => {
|
|
298
|
+
return kit.run<TInput, TOutput>(functionName, input, {
|
|
299
|
+
...options,
|
|
300
|
+
...runOptions,
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
[kit, functionName, options]
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// useComputeFunction Hook
|
|
309
|
+
// ============================================================================
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Hook to register and use a compute function
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```tsx
|
|
316
|
+
* function MyComponent() {
|
|
317
|
+
* const { run, loading, data } = useComputeFunction(
|
|
318
|
+
* 'myFunction',
|
|
319
|
+
* (input: number) => input * 2
|
|
320
|
+
* );
|
|
321
|
+
*
|
|
322
|
+
* return (
|
|
323
|
+
* <button onClick={() => run(5)} disabled={loading}>
|
|
324
|
+
* {loading ? 'Computing...' : `Result: ${data}`}
|
|
325
|
+
* </button>
|
|
326
|
+
* );
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
export function useComputeFunction<TInput = unknown, TOutput = unknown>(
|
|
331
|
+
name: string,
|
|
332
|
+
fn: (input: TInput) => TOutput | Promise<TOutput>,
|
|
333
|
+
options?: UseComputeOptions
|
|
334
|
+
): UseComputeReturn<TInput, TOutput> {
|
|
335
|
+
const kit = useComputeKit();
|
|
336
|
+
|
|
337
|
+
// Register function on mount
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
kit.register(name, fn);
|
|
340
|
+
}, [kit, name, fn]);
|
|
341
|
+
|
|
342
|
+
return useCompute<TInput, TOutput>(name, options);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// usePoolStats Hook
|
|
347
|
+
// ============================================================================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Hook to get worker pool statistics
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```tsx
|
|
354
|
+
* function PoolMonitor() {
|
|
355
|
+
* const stats = usePoolStats(1000); // Update every second
|
|
356
|
+
*
|
|
357
|
+
* return (
|
|
358
|
+
* <div>
|
|
359
|
+
* <p>Active Workers: {stats.activeWorkers}</p>
|
|
360
|
+
* <p>Queue Length: {stats.queueLength}</p>
|
|
361
|
+
* <p>Tasks Completed: {stats.tasksCompleted}</p>
|
|
362
|
+
* </div>
|
|
363
|
+
* );
|
|
364
|
+
* }
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
export function usePoolStats(refreshInterval: number = 0): PoolStats {
|
|
368
|
+
const kit = useComputeKit();
|
|
369
|
+
const [stats, setStats] = useState<PoolStats>(() => kit.getStats());
|
|
370
|
+
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
// For one-time fetch (refreshInterval <= 0), we rely on the initial state
|
|
373
|
+
if (refreshInterval <= 0) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const interval = setInterval(() => {
|
|
378
|
+
setStats(kit.getStats());
|
|
379
|
+
}, refreshInterval);
|
|
380
|
+
|
|
381
|
+
return () => clearInterval(interval);
|
|
382
|
+
}, [kit, refreshInterval]);
|
|
383
|
+
|
|
384
|
+
return stats;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ============================================================================
|
|
388
|
+
// useWasmSupport Hook
|
|
389
|
+
// ============================================================================
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Hook to check WASM support
|
|
393
|
+
*/
|
|
394
|
+
export function useWasmSupport(): boolean {
|
|
395
|
+
const kit = useComputeKit();
|
|
396
|
+
return kit.isWasmSupported();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Exports
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
403
|
+
export type {
|
|
404
|
+
ComputeKitOptions,
|
|
405
|
+
ComputeOptions,
|
|
406
|
+
ComputeProgress,
|
|
407
|
+
PoolStats,
|
|
408
|
+
} from '@computekit/core';
|
|
409
|
+
|
|
410
|
+
export { ComputeKit } from '@computekit/core';
|