@liveblocks/core 3.13.2 → 3.13.3-rc1

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.13.2";
9
+ var PKG_VERSION = "3.13.3-rc1";
10
10
  var PKG_FORMAT = "cjs";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -2760,6 +2760,14 @@ var FSM = class {
2760
2760
  // `foo.bar.*`, then `foo.*`, and finally, `*`.
2761
2761
  //
2762
2762
  #cleanupStack;
2763
+ //
2764
+ // The entry times stack tracks when each state level was entered, using
2765
+ // performance.now() timestamps. This parallels the cleanup stack structure.
2766
+ //
2767
+ // For example, if you are in state `foo.bar.qux`, the stack contains:
2768
+ // [timestamp for *, timestamp for foo.*, timestamp for foo.bar.*, timestamp for foo.bar.qux]
2769
+ //
2770
+ #entryTimesStack;
2763
2771
  #enterFns;
2764
2772
  // Used to provide better error messages
2765
2773
  #knownEventTypes;
@@ -2816,6 +2824,7 @@ var FSM = class {
2816
2824
  this.#states = /* @__PURE__ */ new Set();
2817
2825
  this.#enterFns = /* @__PURE__ */ new Map();
2818
2826
  this.#cleanupStack = [];
2827
+ this.#entryTimesStack = [];
2819
2828
  this.#knownEventTypes = /* @__PURE__ */ new Set();
2820
2829
  this.#allowedTransitions = /* @__PURE__ */ new Map();
2821
2830
  this.#currentContext = new SafeContext(initialContext);
@@ -2824,14 +2833,16 @@ var FSM = class {
2824
2833
  willTransition: makeEventSource(),
2825
2834
  didIgnoreEvent: makeEventSource(),
2826
2835
  willExitState: makeEventSource(),
2827
- didEnterState: makeEventSource()
2836
+ didEnterState: makeEventSource(),
2837
+ didExitState: makeEventSource()
2828
2838
  };
2829
2839
  this.events = {
2830
2840
  didReceiveEvent: this.#eventHub.didReceiveEvent.observable,
2831
2841
  willTransition: this.#eventHub.willTransition.observable,
2832
2842
  didIgnoreEvent: this.#eventHub.didIgnoreEvent.observable,
2833
2843
  willExitState: this.#eventHub.willExitState.observable,
2834
- didEnterState: this.#eventHub.didEnterState.observable
2844
+ didEnterState: this.#eventHub.didEnterState.observable,
2845
+ didExitState: this.#eventHub.didExitState.observable
2835
2846
  };
2836
2847
  }
2837
2848
  get context() {
@@ -3017,10 +3028,23 @@ var FSM = class {
3017
3028
  */
3018
3029
  #exit(levels) {
3019
3030
  this.#eventHub.willExitState.notify(this.currentState);
3031
+ const now2 = performance.now();
3032
+ const parts = this.currentState.split(".");
3020
3033
  this.#currentContext.allowPatching((patchableContext) => {
3021
3034
  levels = _nullishCoalesce(levels, () => ( this.#cleanupStack.length));
3022
3035
  for (let i = 0; i < levels; i++) {
3023
3036
  _optionalChain([this, 'access', _35 => _35.#cleanupStack, 'access', _36 => _36.pop, 'call', _37 => _37(), 'optionalCall', _38 => _38(patchableContext)]);
3037
+ const entryTime = this.#entryTimesStack.pop();
3038
+ if (entryTime !== void 0 && // ...but avoid computing state names if nobody is listening
3039
+ this.#eventHub.didExitState.count() > 0) {
3040
+ const depth = this.#entryTimesStack.length;
3041
+ if (depth === 0) continue;
3042
+ const state = depth === parts.length ? this.currentState : `${parts.slice(0, depth).join(".")}.*`;
3043
+ this.#eventHub.didExitState.notify({
3044
+ state,
3045
+ durationMs: now2 - entryTime
3046
+ });
3047
+ }
3024
3048
  }
3025
3049
  });
3026
3050
  }
@@ -3033,6 +3057,7 @@ var FSM = class {
3033
3057
  this.currentState,
3034
3058
  _nullishCoalesce(levels, () => ( this.currentState.split(".").length + 1))
3035
3059
  );
3060
+ const now2 = performance.now();
3036
3061
  this.#currentContext.allowPatching((patchableContext) => {
3037
3062
  for (const pattern of enterPatterns) {
3038
3063
  const enterFn = this.#enterFns.get(pattern);
@@ -3042,6 +3067,7 @@ var FSM = class {
3042
3067
  } else {
3043
3068
  this.#cleanupStack.push(null);
3044
3069
  }
3070
+ this.#entryTimesStack.push(now2);
3045
3071
  }
3046
3072
  });
3047
3073
  this.#eventHub.didEnterState.notify(this.currentState);
@@ -3259,23 +3285,20 @@ function isCloseEvent(error3) {
3259
3285
  return !(error3 instanceof Error) && error3.type === "close";
3260
3286
  }
3261
3287
  function enableTracing(machine) {
3262
- const start = (/* @__PURE__ */ new Date()).getTime();
3263
3288
  function log2(...args) {
3264
- warn(
3265
- `${(((/* @__PURE__ */ new Date()).getTime() - start) / 1e3).toFixed(2)} [FSM #${machine.id}]`,
3266
- ...args
3267
- );
3289
+ warn(`[FSM #${machine.id}]`, ...args);
3268
3290
  }
3269
3291
  const unsubs = [
3270
3292
  machine.events.didReceiveEvent.subscribe((e) => log2(`Event ${e.type}`)),
3271
3293
  machine.events.willTransition.subscribe(
3272
3294
  ({ from, to }) => log2("Transitioning", from, "\u2192", to)
3273
3295
  ),
3296
+ machine.events.didExitState.subscribe(
3297
+ ({ state, durationMs }) => log2(`Exited ${state} after ${durationMs.toFixed(0)}ms`)
3298
+ ),
3274
3299
  machine.events.didIgnoreEvent.subscribe(
3275
3300
  (e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
3276
3301
  )
3277
- // machine.events.willExitState.subscribe((s) => log("Exiting state", s)),
3278
- // machine.events.didEnterState.subscribe((s) => log("Entering state", s)),
3279
3302
  ];
3280
3303
  return () => {
3281
3304
  for (const unsub of unsubs) {
@@ -3416,6 +3439,8 @@ function createConnectionStateMachine(delegates, options) {
3416
3439
  // OK state. This is done by resolving the Promise.
3417
3440
  //
3418
3441
  async (ctx, signal) => {
3442
+ const socketEpoch = performance.now();
3443
+ let socketOpenAt = null;
3419
3444
  let capturedPrematureEvent = null;
3420
3445
  let unconfirmedSocket = null;
3421
3446
  const connect$ = new Promise(
@@ -3437,6 +3462,12 @@ function createConnectionStateMachine(delegates, options) {
3437
3462
  function waitForActorId(event) {
3438
3463
  const serverMsg = tryParseJson(event.data);
3439
3464
  if (_optionalChain([serverMsg, 'optionalAccess', _40 => _40.type]) === ServerMsgCode.ROOM_STATE) {
3465
+ if (options.enableDebugLogging && socketOpenAt !== null) {
3466
+ const elapsed = performance.now() - socketOpenAt;
3467
+ warn(
3468
+ `[FSM #${machine.id}] Socket open \u2192 ROOM_STATE: ${elapsed.toFixed(0)}ms`
3469
+ );
3470
+ }
3440
3471
  didReceiveActor();
3441
3472
  }
3442
3473
  }
@@ -3447,6 +3478,13 @@ function createConnectionStateMachine(delegates, options) {
3447
3478
  socket.addEventListener("error", reject);
3448
3479
  socket.addEventListener("close", reject);
3449
3480
  socket.addEventListener("open", () => {
3481
+ socketOpenAt = performance.now();
3482
+ if (options.enableDebugLogging) {
3483
+ const elapsed = socketOpenAt - socketEpoch;
3484
+ warn(
3485
+ `[FSM #${machine.id}] Socket epoch \u2192 open: ${elapsed.toFixed(0)}ms`
3486
+ );
3487
+ }
3450
3488
  socket.addEventListener("error", onSocketError);
3451
3489
  socket.addEventListener("close", onSocketClose);
3452
3490
  const unsub = () => {
@@ -5952,7 +5990,7 @@ var OpCode = Object.freeze({
5952
5990
  CREATE_MAP: 7,
5953
5991
  CREATE_REGISTER: 8
5954
5992
  });
5955
- function isAckOp(op) {
5993
+ function isIgnoredOp(op) {
5956
5994
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5957
5995
  }
5958
5996
 
@@ -9292,7 +9330,7 @@ function createRoom(options, config) {
9292
9330
  };
9293
9331
  }
9294
9332
  function applyOp(op, source) {
9295
- if (isAckOp(op)) {
9333
+ if (isIgnoredOp(op)) {
9296
9334
  return { modified: false };
9297
9335
  }
9298
9336
  switch (op.type) {