@liveblocks/core 3.19.2 → 3.19.3

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 CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.19.2";
9
+ var PKG_VERSION = "3.19.3";
10
10
  var PKG_FORMAT = "cjs";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -2715,6 +2715,7 @@ var HttpClient = class {
2715
2715
  };
2716
2716
 
2717
2717
  // src/lib/fsm.ts
2718
+ var IGNORE = /* @__PURE__ */ Symbol("fsm.ignore");
2718
2719
  function distance(state1, state2) {
2719
2720
  if (state1 === state2) {
2720
2721
  return [0, 0];
@@ -2887,7 +2888,7 @@ var FSM = class {
2887
2888
  this.#eventHub = {
2888
2889
  didReceiveEvent: makeEventSource(),
2889
2890
  willTransition: makeEventSource(),
2890
- didIgnoreEvent: makeEventSource(),
2891
+ didIgnoreUnexpectedEvent: makeEventSource(),
2891
2892
  willExitState: makeEventSource(),
2892
2893
  didEnterState: makeEventSource(),
2893
2894
  didExitState: makeEventSource()
@@ -2895,7 +2896,7 @@ var FSM = class {
2895
2896
  this.events = {
2896
2897
  didReceiveEvent: this.#eventHub.didReceiveEvent.observable,
2897
2898
  willTransition: this.#eventHub.willTransition.observable,
2898
- didIgnoreEvent: this.#eventHub.didIgnoreEvent.observable,
2899
+ didIgnoreUnexpectedEvent: this.#eventHub.didIgnoreUnexpectedEvent.observable,
2899
2900
  willExitState: this.#eventHub.willExitState.observable,
2900
2901
  didEnterState: this.#eventHub.didEnterState.observable,
2901
2902
  didExitState: this.#eventHub.didExitState.observable
@@ -3018,9 +3019,14 @@ var FSM = class {
3018
3019
  * `context` params to conditionally decide which next state to transition
3019
3020
  * to.
3020
3021
  *
3021
- * If you set it to `null`, then the transition will be explicitly forbidden
3022
- * and throw an error. If you don't define a target for a transition, then
3023
- * such events will get ignored.
3022
+ * If you don't define a target for a transition, the event is treated
3023
+ * as unhandled in this state: `didIgnoreUnexpectedEvent` fires and the
3024
+ * state does not change.
3025
+ *
3026
+ * To declare an event as an intentional silent no-op in this state, use
3027
+ * the {@link IGNORE} sentinel — either statically (`{ EVENT: IGNORE }`)
3028
+ * or as a return value from a target function. IGNORE'd events do not
3029
+ * fire `didIgnoreUnexpectedEvent`.
3024
3030
  */
3025
3031
  addTransitions(nameOrPattern, mapping) {
3026
3032
  if (this.#runningState !== 0 /* NOT_STARTED_YET */) {
@@ -3040,7 +3046,12 @@ var FSM = class {
3040
3046
  }
3041
3047
  const target = target_;
3042
3048
  this.#knownEventTypes.add(type);
3043
- if (target !== void 0) {
3049
+ if (target === void 0) {
3050
+ continue;
3051
+ }
3052
+ if (target === IGNORE) {
3053
+ map.set(type, IGNORE);
3054
+ } else {
3044
3055
  const targetFn = typeof target === "function" ? target : () => target;
3045
3056
  map.set(type, targetFn);
3046
3057
  }
@@ -3139,12 +3150,14 @@ var FSM = class {
3139
3150
  if (this.#runningState === 2 /* STOPPED */) {
3140
3151
  return;
3141
3152
  }
3142
- const targetFn = this.#getTargetFn(event.type);
3143
- if (targetFn !== void 0) {
3144
- return this.#transition(event, targetFn);
3145
- } else {
3146
- this.#eventHub.didIgnoreEvent.notify(event);
3153
+ const entry = this.#getTargetFn(event.type);
3154
+ if (entry === IGNORE) {
3155
+ return;
3147
3156
  }
3157
+ if (entry !== void 0) {
3158
+ return this.#transition(event, entry);
3159
+ }
3160
+ this.#eventHub.didIgnoreUnexpectedEvent.notify(event);
3148
3161
  }
3149
3162
  #transition(event, target) {
3150
3163
  this.#eventHub.didReceiveEvent.notify(event);
@@ -3153,8 +3166,7 @@ var FSM = class {
3153
3166
  const nextTarget = targetFn(event, this.#currentContext.current);
3154
3167
  let nextState;
3155
3168
  let effects = void 0;
3156
- if (nextTarget === null) {
3157
- this.#eventHub.didIgnoreEvent.notify(event);
3169
+ if (nextTarget === IGNORE) {
3158
3170
  return;
3159
3171
  }
3160
3172
  if (typeof nextTarget === "string") {
@@ -3373,8 +3385,8 @@ function enableTracing(machine) {
3373
3385
  machine.events.didExitState.subscribe(
3374
3386
  ({ state, durationMs }) => log2(`Exited ${state} after ${durationMs.toFixed(0)}ms`)
3375
3387
  ),
3376
- machine.events.didIgnoreEvent.subscribe(
3377
- (e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
3388
+ machine.events.didIgnoreUnexpectedEvent.subscribe(
3389
+ (e) => log2("Ignored unexpected event", e.type, e, "(no transition declared)")
3378
3390
  )
3379
3391
  ];
3380
3392
  return () => {
@@ -3486,7 +3498,12 @@ function createConnectionStateMachine(delegates, options) {
3486
3498
  );
3487
3499
  const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
3488
3500
  const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
3489
- const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
3501
+ const onSocketMessage = (event) => {
3502
+ machine.send({ type: "ALIVE" });
3503
+ if (event.data !== "pong") {
3504
+ onMessage.notify(event);
3505
+ }
3506
+ };
3490
3507
  function teardownSocket(socket) {
3491
3508
  if (socket) {
3492
3509
  socket.removeEventListener("error", onSocketError);
@@ -3656,7 +3673,13 @@ function createConnectionStateMachine(delegates, options) {
3656
3673
  effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
3657
3674
  };
3658
3675
  }
3659
- );
3676
+ ).addTransitions("@connecting.busy", {
3677
+ // The socket message listener is attached during @connecting.busy (see
3678
+ // onEnterAsync above), so server frames (most notably the actor-id
3679
+ // handshake) can fire onSocketMessage and emit a ALIVE before we
3680
+ // reach @ok.*. That's fine. Heartbeat only matters in @ok.*.
3681
+ ALIVE: IGNORE
3682
+ });
3660
3683
  const sendHeartbeat = {
3661
3684
  target: "@ok.awaiting-pong",
3662
3685
  effect: (ctx) => {
@@ -3671,7 +3694,8 @@ function createConnectionStateMachine(delegates, options) {
3671
3694
  machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, maybeHeartbeat).addTransitions("@ok.connected", {
3672
3695
  NAVIGATOR_OFFLINE: maybeHeartbeat,
3673
3696
  // Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
3674
- WINDOW_GOT_FOCUS: sendHeartbeat
3697
+ WINDOW_GOT_FOCUS: sendHeartbeat,
3698
+ ALIVE: IGNORE
3675
3699
  });
3676
3700
  machine.addTransitions("@idle.zombie", {
3677
3701
  WINDOW_GOT_FOCUS: "@connecting.backoff"
@@ -3692,7 +3716,7 @@ function createConnectionStateMachine(delegates, options) {
3692
3716
  clearTimeout(timerID);
3693
3717
  onMessage.pause();
3694
3718
  };
3695
- }).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3719
+ }).addTransitions("@ok.awaiting-pong", { ALIVE: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
3696
3720
  target: "@connecting.busy",
3697
3721
  // Log implicit connection loss and drop the current open socket
3698
3722
  effect: log(
@@ -3705,7 +3729,7 @@ function createConnectionStateMachine(delegates, options) {
3705
3729
  // not. When still OPEN, don't transition.
3706
3730
  EXPLICIT_SOCKET_ERROR: (_, context) => {
3707
3731
  if (_optionalChain([context, 'access', _48 => _48.socket, 'optionalAccess', _49 => _49.readyState]) === 1) {
3708
- return null;
3732
+ return IGNORE;
3709
3733
  }
3710
3734
  return {
3711
3735
  target: "@connecting.backoff",