feat: init

This commit is contained in:
Morten Olsen
2023-09-06 10:56:36 +02:00
commit 1f3401a961
55 changed files with 6098 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
import {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { RunnerContext } from './context';
type BlockProps = {
worker: Worker;
action: string;
presenter?: React.FC<any>;
};
const id = (function* () {
let i = 0;
while (true) {
yield i++;
}
})();
const Block: React.FC<BlockProps> = ({
worker,
action,
presenter: Presenter,
}) => {
const currentId = useRef(id.next().value);
const { vars } = useContext(RunnerContext);
const [output, setOutput] = useState<unknown>();
const [error, setError] = useState<unknown>();
const [running, setRunning] = useState<boolean>();
const [duration, setDuration] = useState<number>();
const view = useMemo(() => {
if (error) {
return error.toString();
}
if (Presenter) {
return <Presenter output={output} />;
}
return JSON.stringify(output, null, 2);
}, [output, error, Presenter]);
const runBlock = useCallback(async () => {
setRunning(true);
setError(undefined);
setOutput(undefined);
try {
worker.postMessage({
type: 'run',
action,
vars,
id: currentId.current,
});
} catch (error) {
setError(error);
}
setRunning(false);
}, [worker, vars, action]);
useEffect(() => {
const listener = (event: MessageEvent) => {
const { type, payload, id, duration } = event.data;
if (id !== currentId.current) {
return;
}
setDuration(duration);
setRunning(false);
if (type === 'output') {
setOutput(payload);
}
if (type === 'error') {
setError(payload);
}
};
worker.addEventListener('message', listener);
return () => {
worker.removeEventListener('message', listener);
};
}, [worker]);
return (
<div>
<button onClick={runBlock} disabled={running}>
Run
</button>
{duration && <div>Duration: {duration.toFixed(2)}ms</div>}
{running && <div>Running...</div>}
{view}
</div>
);
};
export { Block };

View File

@@ -0,0 +1,42 @@
import { createContext, useCallback, useMemo } from 'react';
type Vars = Record<string, unknown>;
type RunnerContextValue = {
vars: Vars;
run: (fn: (vars: Vars) => Promise<void>) => Promise<void>;
};
type RunnerProviderProps = {
vars?: Vars;
children: React.ReactNode;
};
const RunnerContext = createContext<RunnerContextValue>({
vars: {},
run: async () => {},
});
const RunnerProvider: React.FC<RunnerProviderProps> = ({
vars = {},
children,
}) => {
const currentVars = useMemo(() => vars, [vars]);
const run = useCallback(
async (fn: (vars: Vars) => Promise<void>) => {
const output = await fn(currentVars);
return output;
},
[currentVars],
);
return (
<RunnerContext.Provider value={{ vars, run }}>
{children}
</RunnerContext.Provider>
);
};
export type { Vars };
export { RunnerContext, RunnerProvider };

View File

@@ -0,0 +1,3 @@
export { RunnerProvider } from './context';
export { Block } from './block';
export { createWorker } from './worker';

View File

@@ -0,0 +1,21 @@
type WorkerFn = Record<string, (...args: any[]) => any>;
const createWorker = (fn: WorkerFn) => {
self.addEventListener('message', (event) => {
const { action, vars = {}, id } = event.data;
const run = async () => {
const startTime = performance.now();
try {
const result = await fn[action](vars);
const endTime = performance.now();
const duration = endTime - startTime;
self.postMessage({ type: 'output', payload: result, id, duration });
} catch (error) {
self.postMessage({ type: 'error', payload: error, id });
}
};
run();
});
};
export { createWorker };