Skip to content

Commit afb5807

Browse files
committed
make system subscribable, and use snapshot
1 parent 69fe023 commit afb5807

3 files changed

Lines changed: 81 additions & 111 deletions

File tree

packages/core/src/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ export { and, not, or, stateIn } from './guards.ts';
1717
export { setup } from './setup.ts';
1818
export type {
1919
ActorSystem,
20+
SystemSnapshot,
2021
InspectedActorEvent,
2122
InspectedEventEvent,
2223
InspectedSnapshotEvent,
23-
InspectionEvent,
24-
RegisteredActorEvent,
25-
UnregisteredActorEvent,
26-
RegistrationEvent
24+
InspectionEvent
2725
} from './system.ts';
2826
export { toPromise } from './toPromise.ts';
2927
export {

packages/core/src/system.ts

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,7 @@ function createScheduledEventId(
4848
}
4949

5050
export interface ActorSystem<T extends ActorSystemInfo>
51-
extends Subscribable<
52-
RegistrationEvent<
53-
T['actors'][keyof T['actors']],
54-
keyof T['actors'] & string
55-
>
56-
> {
51+
extends Subscribable<SystemSnapshot> {
5752
/**
5853
* @internal
5954
*/
@@ -87,15 +82,15 @@ export interface ActorSystem<T extends ActorSystemInfo>
8782
event: AnyEventObject
8883
) => void;
8984
scheduler: Scheduler;
90-
getSnapshot: () => {
91-
_scheduledEvents: Record<string, ScheduledEvent>;
92-
};
85+
getSnapshot: () => SystemSnapshot;
9386
/**
9487
* @internal
9588
*/
96-
_snapshot: {
97-
_scheduledEvents: Record<ScheduledEventId, ScheduledEvent>;
98-
};
89+
_updateSnapshot: (snapshot: SystemSnapshot) => void;
90+
/**
91+
* @internal
92+
*/
93+
_snapshot: SystemSnapshot;
9994
start: () => void;
10095
}
10196

@@ -113,9 +108,7 @@ export function createSystem<T extends ActorSystemInfo>(
113108
const keyedActors = new Map<keyof T['actors'], AnyActorRef | undefined>();
114109
const reverseKeyedActors = new WeakMap<AnyActorRef, keyof T['actors']>();
115110
const inspectionObservers = new Set<Observer<InspectionEvent>>();
116-
const registrationObservers = new Set<
117-
Observer<RegistrationEvent<T['actors'][keyof T['actors']]>>
118-
>();
111+
const systemObservers = new Set<Observer<SystemSnapshot>>();
119112
const timerMap: { [id: ScheduledEventId]: number } = {};
120113
const clock = options.clock;
121114

@@ -169,23 +162,36 @@ export function createSystem<T extends ActorSystemInfo>(
169162
}
170163
};
171164

165+
function makeActorsFromChildren() {
166+
const actors = {} as Record<keyof T['actors'], AnyActorRef>;
167+
for (const [key, actorRef] of children) {
168+
const systemId = reverseKeyedActors.get(actorRef);
169+
if (systemId !== undefined) {
170+
actors[systemId] = actorRef;
171+
}
172+
}
173+
return actors;
174+
}
175+
172176
const system: ActorSystem<T> = {
173177
_snapshot: {
174178
_scheduledEvents:
175-
(options?.snapshot && (options.snapshot as any).scheduler) ?? {}
179+
(options?.snapshot && (options.snapshot as any).scheduler) ?? {},
180+
actors: makeActorsFromChildren()
176181
},
182+
177183
_bookId: () => `x:${idCounter++}`,
178184
_register: (sessionId, actorRef) => {
179185
children.set(sessionId, actorRef);
180186
const systemId = reverseKeyedActors.get(actorRef);
181187
if (systemId !== undefined) {
182-
const event = {
183-
type: `@xstate.actor.register`,
184-
systemId: systemId as string,
185-
actorRef: actorRef as T['actors'][keyof T['actors']]
186-
} as const;
187-
registrationObservers.forEach((listener) => {
188-
listener.next?.(event);
188+
const currentSnapshot = system.getSnapshot();
189+
system._updateSnapshot({
190+
...currentSnapshot,
191+
actors: {
192+
...currentSnapshot.actors,
193+
[systemId]: actorRef
194+
}
189195
});
190196
}
191197
return sessionId;
@@ -197,13 +203,13 @@ export function createSystem<T extends ActorSystemInfo>(
197203
if (systemId !== undefined) {
198204
keyedActors.delete(systemId);
199205
reverseKeyedActors.delete(actorRef);
200-
const event = {
201-
type: `@xstate.actor.unregister`,
202-
systemId: systemId as string,
203-
actorRef: actorRef as T['actors'][keyof T['actors']]
204-
} as const;
205-
registrationObservers.forEach((listener) => {
206-
listener.next?.(event);
206+
const {
207+
_scheduledEvents,
208+
actors: { [systemId]: _, ...actors }
209+
} = system.getSnapshot();
210+
system._updateSnapshot({
211+
_scheduledEvents,
212+
actors
207213
});
208214
}
209215
},
@@ -212,8 +218,8 @@ export function createSystem<T extends ActorSystemInfo>(
212218
},
213219
subscribe: (
214220
nextListenerOrObserver:
215-
| ((event: RegistrationEvent<T['actors'][keyof T['actors']]>) => void)
216-
| Observer<RegistrationEvent<T['actors'][keyof T['actors']]>>,
221+
| ((event: SystemSnapshot) => void)
222+
| Observer<SystemSnapshot>,
217223
errorListener?: (error: any) => void,
218224
completeListener?: () => void
219225
) => {
@@ -223,25 +229,11 @@ export function createSystem<T extends ActorSystemInfo>(
223229
completeListener
224230
);
225231

226-
if (rootActor._processingStatus !== ProcessingStatus.Stopped) {
227-
registrationObservers.add(observer);
228-
} else {
229-
const snapshot = rootActor.getSnapshot();
230-
switch (snapshot.status) {
231-
case 'done':
232-
try {
233-
observer.complete?.();
234-
} catch (err) {
235-
reportUnhandledError(err);
236-
}
237-
break;
238-
// can this error?
239-
}
240-
}
232+
systemObservers.add(observer);
241233

242234
return {
243235
unsubscribe: () => {
244-
registrationObservers.delete(observer);
236+
systemObservers.delete(observer);
245237
}
246238
};
247239
},
@@ -280,9 +272,13 @@ export function createSystem<T extends ActorSystemInfo>(
280272
},
281273
scheduler,
282274
getSnapshot: () => {
283-
return {
284-
_scheduledEvents: { ...system._snapshot._scheduledEvents }
285-
};
275+
return system._snapshot;
276+
},
277+
_updateSnapshot: (snapshot) => {
278+
system._snapshot = snapshot;
279+
systemObservers.forEach((listener) => {
280+
listener.next?.(snapshot);
281+
});
286282
},
287283
start: () => {
288284
const scheduledEvets = system._snapshot._scheduledEvents;
@@ -332,27 +328,7 @@ export type InspectionEvent =
332328
| InspectedEventEvent
333329
| InspectedActorEvent;
334330

335-
export interface RegisteredActorEvent<
336-
TActorRef extends AnyActorRef,
337-
TSystemId extends string = string
338-
> {
339-
type: `@xstate.actor.register`;
340-
systemId: TSystemId;
341-
actorRef: TActorRef;
331+
export interface SystemSnapshot {
332+
_scheduledEvents: Record<ScheduledEventId, ScheduledEvent>;
333+
actors: Record<string, AnyActorRef>;
342334
}
343-
344-
export interface UnregisteredActorEvent<
345-
TActorRef extends AnyActorRef,
346-
TSystemId extends string = string
347-
> {
348-
type: `@xstate.actor.unregister`;
349-
systemId: TSystemId;
350-
actorRef: TActorRef;
351-
}
352-
353-
export type RegistrationEvent<
354-
TActorRef extends AnyActorRef = AnyActorRef,
355-
TSystemId extends string = string
356-
> =
357-
| RegisteredActorEvent<TActorRef, TSystemId>
358-
| UnregisteredActorEvent<TActorRef, TSystemId>;

packages/core/test/system.test.ts

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -565,23 +565,23 @@ describe('system', () => {
565565

566566
const actorRef = createActor(machine).start();
567567

568-
const events: string[] = [];
568+
let keysOverTime: string[][] = [
569+
Object.keys(actorRef.system.getSnapshot().actors)
570+
];
569571

570-
actorRef.system.subscribe((event) => {
571-
events.push(`${event.type}.${event.systemId}`);
572+
actorRef.system.subscribe((snapshot) => {
573+
keysOverTime.push(Object.keys(snapshot.actors));
572574
});
573575

574576
actorRef.send({ type: 'to_b' });
575-
expect(events).toEqual([
576-
`@xstate.actor.unregister.${aSystemId}`,
577-
`@xstate.actor.register.${bSystemId}`
578-
]);
577+
expect(keysOverTime).toEqual([['a_child'], [], ['b_child']]);
579578
actorRef.send({ type: 'to_a' });
580-
expect(events).toEqual([
581-
`@xstate.actor.unregister.${aSystemId}`,
582-
`@xstate.actor.register.${bSystemId}`,
583-
`@xstate.actor.unregister.${bSystemId}`,
584-
`@xstate.actor.register.${aSystemId}`
579+
expect(keysOverTime).toEqual([
580+
['a_child'],
581+
[],
582+
['b_child'],
583+
[],
584+
['a_child']
585585
]);
586586
});
587587

@@ -614,39 +614,35 @@ describe('system', () => {
614614

615615
const actorRef = createActor(machine).start();
616616

617-
const events: string[] = [];
618-
const unsubscribedEvents: string[] = [];
617+
const keysOverTime: string[][] = [
618+
Object.keys(actorRef.system.getSnapshot().actors)
619+
];
620+
const unsubscribedKeysOverTime: string[][] = [
621+
Object.keys(actorRef.system.getSnapshot().actors)
622+
];
619623

620624
const subscription = actorRef.system.subscribe((event) => {
621-
events.push(`${event.type}.${event.systemId}`);
625+
keysOverTime.push(Object.keys(event.actors));
622626
});
623627

624628
actorRef.system.subscribe((event) => {
625-
unsubscribedEvents.push(`${event.type}.${event.systemId}`);
629+
unsubscribedKeysOverTime.push(Object.keys(event.actors));
626630
});
627631

628632
actorRef.send({ type: 'to_b' });
629-
expect(events).toEqual([
630-
`@xstate.actor.unregister.${aSystemId}`,
631-
`@xstate.actor.register.${bSystemId}`
632-
]);
633-
expect(unsubscribedEvents).toEqual([
634-
`@xstate.actor.unregister.${aSystemId}`,
635-
`@xstate.actor.register.${bSystemId}`
636-
]);
633+
expect(keysOverTime).toEqual([['a_child'], [], ['b_child']]);
634+
expect(unsubscribedKeysOverTime).toEqual([['a_child'], [], ['b_child']]);
637635

638636
subscription.unsubscribe();
639637
actorRef.send({ type: 'to_a' });
640638

641-
expect(events).toEqual([
642-
`@xstate.actor.unregister.${aSystemId}`,
643-
`@xstate.actor.register.${bSystemId}`
644-
]);
645-
expect(unsubscribedEvents).toEqual([
646-
`@xstate.actor.unregister.${aSystemId}`,
647-
`@xstate.actor.register.${bSystemId}`,
648-
`@xstate.actor.unregister.${bSystemId}`,
649-
`@xstate.actor.register.${aSystemId}`
639+
expect(keysOverTime).toEqual([['a_child'], [], ['b_child']]);
640+
expect(unsubscribedKeysOverTime).toEqual([
641+
['a_child'],
642+
[],
643+
['b_child'],
644+
[],
645+
['a_child']
650646
]);
651647
});
652648
});

0 commit comments

Comments
 (0)