Skip to content

Commit 8be8acd

Browse files
committed
add support for proxying events from webworker to actors in different threads
Signed-off-by: aabidsofi19 <[email protected]>
1 parent 9a8633e commit 8be8acd

File tree

4 files changed

+60
-24
lines changed

4 files changed

+60
-24
lines changed

src/actors/utils.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { AnyActorRef, AnyEventObject, assign, enqueueActions, sendTo } from 'xstate';
55
import { AnyActorSystem } from 'xstate/dist/declarations/src/system';
6+
import { workerEvents } from './worker/events';
67

78
type ContextWithReturnAddress = { returnAddress: AnyActorRef };
89

@@ -39,8 +40,28 @@ export const forwardToActors = (actorSystemIds: string[]) =>
3940

4041
export const deadLetter = (event: AnyEventObject) => ({ type: 'DEAD_LETTER', event });
4142

42-
export const reply = (eventFn: (actionArgs: any, params: any) => AnyEventObject) =>
43-
sendTo(({ context }: { context: ContextWithReturnAddress }) => context.returnAddress, eventFn);
43+
44+
45+
function isInWorker() {
46+
return (
47+
typeof self !== 'undefined' && // 'self' exists
48+
typeof self?.document === 'undefined' // no Window in worker
49+
);
50+
}
51+
52+
export const reply = (eventFn: (actionArgs: any, params: any) => AnyEventObject) => enqueueActions(({ enqueue, ...actionArgs }, params) => {
53+
54+
if (!actionArgs.context.returnAddress) {
55+
console.warn('No return address specified in context for reply action');
56+
return;
57+
}
58+
if (isInWorker()) {
59+
console.log('reply in worker - posting message to main thread', actionArgs.context.returnAddress, eventFn(actionArgs, params));
60+
postMessage(workerEvents.proxyEvent(eventFn(actionArgs, params), actionArgs.context.returnAddress));
61+
return;
62+
}
63+
enqueue.sendTo(actionArgs.context.returnAddress, eventFn);
64+
})
4465

4566
export const XSTATE_DEBUG_EVENT = 'XSTATE_DEBUG_EVENT';
4667

src/actors/validators/dataValidator.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
SnapshotFrom,
88
assign,
99
fromPromise,
10-
sendParent,
1110
setup
1211
} from 'xstate';
1312
import { reply } from '../utils';
@@ -55,15 +54,6 @@ interface DataValidationFailedEvent extends EventObject {
5554
};
5655
}
5756

58-
const toSerializable = (data: unknown) => {
59-
try {
60-
return JSON.parse(JSON.stringify(data));
61-
} catch {
62-
console.warn('Data is not serializable, using toPlainObject as fallback', data);
63-
return {}
64-
}
65-
}
66-
6757
export const dataValidatorCommands = {
6858
validateData: ({
6959
validationPayload,
@@ -196,7 +186,6 @@ export const dataValidatorMachine = setup({
196186
target: 'idle',
197187
actions: [
198188
'setValidationResults',
199-
// send to parent
200189
reply(
201190
({
202191
context,
@@ -206,7 +195,7 @@ export const dataValidatorMachine = setup({
206195
event: ValidateActorDoneEvent;
207196
}) =>
208197
dataValidatorEvents.dataValidated({
209-
validationPayload: toSerializable(context.validationPayload) as ValidationPayload,
198+
validationPayload: context.validationPayload as ValidationPayload,
210199
validationResults: event.output.validationResults
211200
})
212201
) as any
@@ -218,7 +207,7 @@ export const dataValidatorMachine = setup({
218207
reply(
219208
({ context, event }: { context: ValidationMachineContext; event: ErrorActorEvent }) =>
220209
dataValidatorEvents.dataValidationFailed({
221-
validationPayload: toSerializable(context.validationPayload) as ValidationPayload,
210+
validationPayload: context.validationPayload as ValidationPayload,
222211
systemErrors: event.error
223212
})
224213
) as any,

src/actors/worker/fromWorkerfiedActor.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export const fromWorkerfiedActor = (
3535
worker: Worker
3636
): WorkerActorLogic<EventObject, WorkerInput> => ({
3737
config: Worker,
38+
39+
40+
3841

3942
start: (state, actorScope) => {
4043
const { self, system } = actorScope;
@@ -46,23 +49,39 @@ export const fromWorkerfiedActor = (
4649
};
4750

4851
worker.addEventListener('message', (event) => {
49-
const eventFromWorker = event.data as AnyEventObject;
50-
if (eventFromWorker.type == 'STATE_SNAPSHOT') {
52+
console.log('Message received from worker --> ', event,event.data.type == WORKER_EVENTS.PROXY_EVENT,WORKER_EVENTS.PROXY_EVENT);
53+
const eventPayload = event.data;
54+
if (eventPayload.type === WORKER_EVENTS.STATE_SNAPSHOT) {
55+
const eventFromWorker = eventPayload as STATE_SNAPSHOT_EVENT;
5156
self.send(eventFromWorker);
5257
return state;
5358
}
5459

55-
if (event.type === WORKER_EVENTS.PROXY_EVENT) {
56-
console.log('Proxy event received from worker', eventFromWorker);
57-
const proxyEvent = event as ProxyEvent;
58-
if (proxyEvent.data.to === 'parent' && self._parent) {
60+
if (eventPayload.type === WORKER_EVENTS.PROXY_EVENT) {
61+
62+
const proxyEvent = eventPayload as ProxyEvent;
63+
const targetActorId = proxyEvent.data.to;
64+
const targetEvent = proxyEvent.data.event;
65+
const isToParent = targetActorId === 'parent';
66+
console.log('Proxy event received from worker to', targetActorId, targetEvent, isToParent);
67+
68+
69+
if (isToParent && self._parent) {
5970
console.log('Relaying to parent', proxyEvent.data);
6071
self._parent.send(proxyEvent.data.event);
6172
return state;
6273
}
74+
if (!isToParent) {
75+
const targetActor = system.get(proxyEvent.data.to);
76+
console.log('Relaying to system actor', proxyEvent.data, targetActor,system,self);
77+
if (targetActor){
78+
targetActor.send(proxyEvent.data.event);
79+
}
80+
return state;
81+
}
82+
83+
6384

64-
system.get(proxyEvent.data.to).send(proxyEvent.data.event);
65-
return state;
6685
}
6786
});
6887

@@ -71,6 +90,8 @@ export const fromWorkerfiedActor = (
7190
transition: (state, event, actorScope) => {
7291
const { self } = actorScope;
7392
const workerState = instanceStates.get(self);
93+
console.log('fromWorkerActor transition', state, event, actorScope);
94+
7495
if (event.type === 'xstate.stop') {
7596
console.log('Stopping fromWorkerActor...', state, event, actorScope);
7697
workerState.worker.postMessage(workerCommands.stopActor());
@@ -81,6 +102,9 @@ export const fromWorkerfiedActor = (
81102
error: undefined
82103
};
83104
}
105+
106+
107+
84108
if (event.type == WORKER_EVENTS.STATE_SNAPSHOT) {
85109
const snapshot = (event as STATE_SNAPSHOT_EVENT).data.snapshot as AnyMachineSnapshot;
86110
return {

src/actors/worker/workerfy.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const ProxyActor = setup({
2626
on: {
2727
'*': {
2828
actions: [
29-
({ event, context }) => console.log('Proxying event', event, 'to', context.proxyToId),
29+
({ event, context }) => console.log('Proxying actor event', event, 'to', context.proxyToId),
3030
({ event, context }) => postMessage(workerEvents.proxyEvent(event, context.proxyToId))
3131
]
3232
}
@@ -56,6 +56,8 @@ export const workerfyActor = (actor: AnyActorLogic) => {
5656
}
5757
}).start();
5858

59+
console.log('Worker actor initialized, waiting for commands...');
60+
5961
addEventListener('message', (event) => {
6062
if (event.data.type === WORKER_COMMANDS.START_ACTOR) {
6163
actorRef = createActor(actor, {

0 commit comments

Comments
 (0)