Closed zgotsch closed 2 years ago
Latest commit: 77b2a13a76da0dbd9b2c7003c1872daca90c5760
The changes in this PR will be included in the next version bump.
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
@zgotsch is attempting to deploy a commit to the Stately Team on Vercel.
A member of the Team first needs to authorize it.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
Name | Status | Preview | Updated |
---|---|---|---|
xstate-viz | ✅ Ready (Inspect) | Visit Preview | Oct 14, 2022 at 1:32PM (UTC) |
My error looks similar to what yall are experiencing, too, hope we can ship this fast
I have a state machine which refuses to render in the visualizer, but would render when unrelated changes were made -- usually changes that would make other parts of the visualization smaller. I did some investigation and found that the ELK wrapping was to blame.
Following the pattern where compaction is disabled after a failed attempt, I made it so that wrapping is disabled after a second attempt. The graph that results isn't pretty, but it's strictly better than not being able to use the visualizer.
Failing Machine Input (I would have made it more minimal but small changes cause the rendering to succeed)
```typescript import { createMachine, assign, StateFrom, spawn, actions, ActorRefFrom, ActionObject, AnyEventObject, } from "xstate"; const pure = actions.pure; createMachine( { context: { id: null, players: new Map(), avMeetingId: null, gameUrl: null, primerUrl: null, queueId: null, }, predictableActionArguments: true, invoke: { src: "avLoader", id: "av_loader", onDone: [ { actions: "assignAvId", }, ], }, id: "cart", initial: "preparing", on: { ADD_PLAYER: { actions: "addPlayer", }, PLAYER_CONNECTED: { actions: "playerConnected", }, PLAYER_DISCONNECTED: { actions: "playerDisconnected", }, PLAYER_EXITED: { actions: "playerExited", }, TIMEOUT: { target: "#dead", }, "*": { actions: "removePlayer", cond: (_ctx, event) => event.type.startsWith("done.invoke.player."), }, }, states: { playing: { exit: "notifyPlayersGameEnded", on: { GAME_ENDED_PLACEHOLDER: { target: "postgame", }, }, }, postgame: { entry: "startAllowedToLeave", exit: "endAllowedToLeave", initial: "reflecting", on: { NEXT_LOOP_PLACEHOLDER: { target: "preparing", }, }, states: { reflecting: { on: { REFLECTION_TIMER_EXPIRED: "deciding", }, }, deciding: { on: { DECIDING_COMPLETE: "showing_decision", }, }, showing_decision: { after: { 5000: "awaiting_backfill", }, }, awaiting_backfill: { always: { target: "#cart.preparing", cond: "backfillComplete", }, }, }, }, preparing: { entry: "resetLoop", type: "parallel", states: { pregame: { initial: "loading_primer", states: { loading_primer: { invoke: { src: "primerLoader", id: "primer_loader", onDone: [ { actions: "assignPrimerUrl", target: "primer_ready_check", }, ], }, on: { TIMEOUT: { target: "#dead", }, }, }, primer_ready_check: { always: { cond: "primerReady", target: "priming", }, }, priming: { on: { PRIMER_ENDED: [ { cond: "gameReady", target: "#cart.playing", }, { target: "priming", internal: false, }, ], }, }, }, }, loading_game: { invoke: { src: "gameLoader", id: "game_loader", onDone: [ { actions: "assignGameUrl", }, ], }, on: { TIMEOUT: { target: "#dead", }, }, }, }, }, dead: { id: "dead", type: "final", }, }, }, { guards: { gameReady: (ctx) => ctx.gameUrl != null, // TODO(zgotsch): Primer readiness also depends on teams as well as backfill completion primerReady: (ctx) => ctx.primerUrl != null && ctx.avMeetingId != null, }, actions: { resetLoop: assign((_ctx) => ({ gameUrl: null, primerUrl: null })), assignGameUrl: assign({ gameUrl: (_ctx, event) => event.data ?? null, }), assignPrimerUrl: assign({ primerUrl: (_ctx, event) => event.data ?? null, }), assignAvId: assign({ avMeetingId: (_ctx, event) => event.data ?? null, }), addPlayer: assign({ players: (ctx, event) => { const newPlayers = new Map(ctx.players); newPlayers.set( event.playerId, spawn(playerMachine, { name: `player.${event.playerId}`, sync: true, }) ); return newPlayers; }, }), removePlayer: pure((ctx, event) => { const playerId = (event as AnyEventObject).type.split(".").pop() as | UserId | undefined; invariant( playerId != null, "Trying to remove a player without knowing the id. This should never happen due to the cond" ); const newPlayers = new Map(ctx.players); newPlayers.delete(playerId); return [ assign({ players: newPlayers }), send({ type: "PLAYER_REMOVED", playerId: playerId }), ] as Array