@liveblocks/core 1.0.12 → 1.1.0-beta2
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.d.ts +156 -59
- package/dist/index.js +1297 -523
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -54,6 +54,19 @@ var __async = (__this, __arguments, generator) => {
|
|
|
54
54
|
function makeEventSource() {
|
|
55
55
|
const _onetimeObservers = /* @__PURE__ */ new Set();
|
|
56
56
|
const _observers = /* @__PURE__ */ new Set();
|
|
57
|
+
let _buffer = null;
|
|
58
|
+
function pause() {
|
|
59
|
+
_buffer = [];
|
|
60
|
+
}
|
|
61
|
+
function unpause() {
|
|
62
|
+
if (_buffer === null) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const event of _buffer) {
|
|
66
|
+
notify(event);
|
|
67
|
+
}
|
|
68
|
+
_buffer = null;
|
|
69
|
+
}
|
|
57
70
|
function subscribe(callback) {
|
|
58
71
|
_observers.add(callback);
|
|
59
72
|
return () => _observers.delete(callback);
|
|
@@ -62,6 +75,25 @@ function makeEventSource() {
|
|
|
62
75
|
_onetimeObservers.add(callback);
|
|
63
76
|
return () => _onetimeObservers.delete(callback);
|
|
64
77
|
}
|
|
78
|
+
function waitUntil(predicate) {
|
|
79
|
+
return __async(this, null, function* () {
|
|
80
|
+
let unsub;
|
|
81
|
+
return new Promise((res) => {
|
|
82
|
+
unsub = subscribe((event) => {
|
|
83
|
+
if (predicate === void 0 || predicate(event)) {
|
|
84
|
+
res(event);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}).finally(() => unsub == null ? void 0 : unsub());
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function notifyOrBuffer(event) {
|
|
91
|
+
if (_buffer !== null) {
|
|
92
|
+
_buffer.push(event);
|
|
93
|
+
} else {
|
|
94
|
+
notify(event);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
65
97
|
function notify(event) {
|
|
66
98
|
_onetimeObservers.forEach((callback) => callback(event));
|
|
67
99
|
_onetimeObservers.clear();
|
|
@@ -71,16 +103,24 @@ function makeEventSource() {
|
|
|
71
103
|
_onetimeObservers.clear();
|
|
72
104
|
_observers.clear();
|
|
73
105
|
}
|
|
106
|
+
function count() {
|
|
107
|
+
return _onetimeObservers.size + _observers.size;
|
|
108
|
+
}
|
|
74
109
|
return {
|
|
75
110
|
// Private/internal control over event emission
|
|
76
|
-
notify,
|
|
111
|
+
notify: notifyOrBuffer,
|
|
77
112
|
subscribe,
|
|
78
113
|
subscribeOnce,
|
|
79
114
|
clear,
|
|
115
|
+
count,
|
|
116
|
+
waitUntil,
|
|
117
|
+
pause,
|
|
118
|
+
unpause,
|
|
80
119
|
// Publicly exposable subscription API
|
|
81
120
|
observable: {
|
|
82
121
|
subscribe,
|
|
83
|
-
subscribeOnce
|
|
122
|
+
subscribeOnce,
|
|
123
|
+
waitUntil
|
|
84
124
|
}
|
|
85
125
|
};
|
|
86
126
|
}
|
|
@@ -117,7 +157,7 @@ var onMessageFromPanel = eventSource.observable;
|
|
|
117
157
|
// src/devtools/index.ts
|
|
118
158
|
var VERSION = true ? (
|
|
119
159
|
/* istanbul ignore next */
|
|
120
|
-
"1.0
|
|
160
|
+
"1.1.0-beta2"
|
|
121
161
|
) : "dev";
|
|
122
162
|
var _devtoolsSetupHasRun = false;
|
|
123
163
|
function setupDevTools(getAllRooms) {
|
|
@@ -159,7 +199,7 @@ function startSyncStream(room) {
|
|
|
159
199
|
fullSync(room);
|
|
160
200
|
unsubsByRoomId.set(room.id, [
|
|
161
201
|
// When the connection status changes
|
|
162
|
-
room.events.
|
|
202
|
+
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
163
203
|
// When storage initializes, send the update
|
|
164
204
|
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
165
205
|
// Any time storage updates, send the new storage root
|
|
@@ -173,7 +213,7 @@ function partialSyncConnection(room) {
|
|
|
173
213
|
sendToPanel({
|
|
174
214
|
msg: "room::sync::partial",
|
|
175
215
|
roomId: room.id,
|
|
176
|
-
status: room.
|
|
216
|
+
status: room.getStatus()
|
|
177
217
|
});
|
|
178
218
|
}
|
|
179
219
|
function partialSyncStorage(room) {
|
|
@@ -214,7 +254,7 @@ function fullSync(room) {
|
|
|
214
254
|
sendToPanel({
|
|
215
255
|
msg: "room::sync::full",
|
|
216
256
|
roomId: room.id,
|
|
217
|
-
status: room.
|
|
257
|
+
status: room.getStatus(),
|
|
218
258
|
storage: (_a = root == null ? void 0 : root.toTreeNode("root").payload) != null ? _a : null,
|
|
219
259
|
me,
|
|
220
260
|
others
|
|
@@ -345,6 +385,989 @@ function nn(value, errmsg = "Expected value to be non-nullable") {
|
|
|
345
385
|
return value;
|
|
346
386
|
}
|
|
347
387
|
|
|
388
|
+
// src/lib/fsm.ts
|
|
389
|
+
function distance(state1, state2) {
|
|
390
|
+
if (state1 === state2) {
|
|
391
|
+
return [0, 0];
|
|
392
|
+
}
|
|
393
|
+
const chunks1 = state1.split(".");
|
|
394
|
+
const chunks2 = state2.split(".");
|
|
395
|
+
const minLen = Math.min(chunks1.length, chunks2.length);
|
|
396
|
+
let shared = 0;
|
|
397
|
+
for (; shared < minLen; shared++) {
|
|
398
|
+
if (chunks1[shared] !== chunks2[shared]) {
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const up = chunks1.length - shared;
|
|
403
|
+
const down = chunks2.length - shared;
|
|
404
|
+
return [up, down];
|
|
405
|
+
}
|
|
406
|
+
function patterns(targetState, levels) {
|
|
407
|
+
const parts = targetState.split(".");
|
|
408
|
+
if (levels < 1 || levels > parts.length + 1) {
|
|
409
|
+
throw new Error("Invalid number of levels");
|
|
410
|
+
}
|
|
411
|
+
const result = [];
|
|
412
|
+
if (levels > parts.length) {
|
|
413
|
+
result.push("*");
|
|
414
|
+
}
|
|
415
|
+
for (let i = parts.length - levels + 1; i < parts.length; i++) {
|
|
416
|
+
const slice = parts.slice(0, i);
|
|
417
|
+
if (slice.length > 0) {
|
|
418
|
+
result.push(slice.join(".") + ".*");
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
result.push(targetState);
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
var SafeContext = class {
|
|
425
|
+
constructor(initialContext) {
|
|
426
|
+
this.curr = initialContext;
|
|
427
|
+
}
|
|
428
|
+
get current() {
|
|
429
|
+
return this.curr;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Call a callback function that allows patching of the context, by
|
|
433
|
+
* calling `context.patch()`. Patching is only allowed for the duration
|
|
434
|
+
* of this window.
|
|
435
|
+
*/
|
|
436
|
+
allowPatching(callback) {
|
|
437
|
+
const self = this;
|
|
438
|
+
let allowed = true;
|
|
439
|
+
const patchableContext = __spreadProps(__spreadValues({}, this.curr), {
|
|
440
|
+
patch(patch) {
|
|
441
|
+
if (allowed) {
|
|
442
|
+
self.curr = Object.assign({}, self.curr, patch);
|
|
443
|
+
for (const pair of Object.entries(patch)) {
|
|
444
|
+
const [key, value] = pair;
|
|
445
|
+
if (key !== "patch") {
|
|
446
|
+
this[key] = value;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
throw new Error("Can no longer patch stale context");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
callback(patchableContext);
|
|
455
|
+
allowed = false;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
var nextId = 1;
|
|
460
|
+
var FSM = class {
|
|
461
|
+
/**
|
|
462
|
+
* Returns the initial state, which is defined by the first call made to
|
|
463
|
+
* .addState().
|
|
464
|
+
*/
|
|
465
|
+
get initialState() {
|
|
466
|
+
const result = this.states.values()[Symbol.iterator]().next();
|
|
467
|
+
if (result.done) {
|
|
468
|
+
throw new Error("No states defined yet");
|
|
469
|
+
} else {
|
|
470
|
+
return result.value;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
get currentState() {
|
|
474
|
+
if (this.currentStateOrNull === null) {
|
|
475
|
+
throw new Error("Not started yet");
|
|
476
|
+
}
|
|
477
|
+
return this.currentStateOrNull;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Starts the machine by entering the initial state.
|
|
481
|
+
*/
|
|
482
|
+
start() {
|
|
483
|
+
if (this.runningState !== 0 /* NOT_STARTED_YET */) {
|
|
484
|
+
throw new Error("State machine has already started");
|
|
485
|
+
}
|
|
486
|
+
this.runningState = 1 /* STARTED */;
|
|
487
|
+
this.currentStateOrNull = this.initialState;
|
|
488
|
+
this.enter(null);
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Stops the state machine. Stopping the state machine will call exit
|
|
493
|
+
* handlers for the current state, but not enter a new state.
|
|
494
|
+
*/
|
|
495
|
+
stop() {
|
|
496
|
+
if (this.runningState !== 1 /* STARTED */) {
|
|
497
|
+
throw new Error("Cannot stop a state machine that isn't started yet");
|
|
498
|
+
}
|
|
499
|
+
this.runningState = 2 /* STOPPED */;
|
|
500
|
+
this.exit(null);
|
|
501
|
+
this.currentStateOrNull = null;
|
|
502
|
+
}
|
|
503
|
+
constructor(initialContext) {
|
|
504
|
+
this.id = nextId++;
|
|
505
|
+
this.runningState = 0 /* NOT_STARTED_YET */;
|
|
506
|
+
this.currentStateOrNull = null;
|
|
507
|
+
this.states = /* @__PURE__ */ new Set();
|
|
508
|
+
this.enterFns = /* @__PURE__ */ new Map();
|
|
509
|
+
this.cleanupStack = [];
|
|
510
|
+
this.knownEventTypes = /* @__PURE__ */ new Set();
|
|
511
|
+
this.allowedTransitions = /* @__PURE__ */ new Map();
|
|
512
|
+
this.currentContext = new SafeContext(initialContext);
|
|
513
|
+
this.eventHub = {
|
|
514
|
+
didReceiveEvent: makeEventSource(),
|
|
515
|
+
willTransition: makeEventSource(),
|
|
516
|
+
didIgnoreEvent: makeEventSource(),
|
|
517
|
+
willExitState: makeEventSource(),
|
|
518
|
+
didEnterState: makeEventSource()
|
|
519
|
+
};
|
|
520
|
+
this.events = {
|
|
521
|
+
didReceiveEvent: this.eventHub.didReceiveEvent.observable,
|
|
522
|
+
willTransition: this.eventHub.willTransition.observable,
|
|
523
|
+
didIgnoreEvent: this.eventHub.didIgnoreEvent.observable,
|
|
524
|
+
willExitState: this.eventHub.willExitState.observable,
|
|
525
|
+
didEnterState: this.eventHub.didEnterState.observable
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
get context() {
|
|
529
|
+
return this.currentContext.current;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Define an explicit finite state in the state machine.
|
|
533
|
+
*/
|
|
534
|
+
addState(state) {
|
|
535
|
+
if (this.runningState !== 0 /* NOT_STARTED_YET */) {
|
|
536
|
+
throw new Error("Already started");
|
|
537
|
+
}
|
|
538
|
+
this.states.add(state);
|
|
539
|
+
return this;
|
|
540
|
+
}
|
|
541
|
+
onEnter(nameOrPattern, enterFn) {
|
|
542
|
+
if (this.runningState !== 0 /* NOT_STARTED_YET */) {
|
|
543
|
+
throw new Error("Already started");
|
|
544
|
+
} else if (this.enterFns.has(nameOrPattern)) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
// TODO We _currently_ don't support multiple .onEnters() for the same
|
|
547
|
+
// state, but this is not a fundamental limitation. Just not
|
|
548
|
+
// implemented yet. If we wanted to, we could make this an array.
|
|
549
|
+
`enter/exit function for ${nameOrPattern} already exists`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
this.enterFns.set(nameOrPattern, enterFn);
|
|
553
|
+
return this;
|
|
554
|
+
}
|
|
555
|
+
onEnterAsync(nameOrPattern, promiseFn, onOK, onError) {
|
|
556
|
+
return this.onEnter(nameOrPattern, () => {
|
|
557
|
+
let cancelled = false;
|
|
558
|
+
void promiseFn(this.currentContext.current).then(
|
|
559
|
+
// On OK
|
|
560
|
+
(data) => {
|
|
561
|
+
if (!cancelled) {
|
|
562
|
+
this.transition({ type: "ASYNC_OK", data }, onOK);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
// On Error
|
|
566
|
+
(reason) => {
|
|
567
|
+
if (!cancelled) {
|
|
568
|
+
this.transition({ type: "ASYNC_ERROR", reason }, onError);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
return () => {
|
|
573
|
+
cancelled = true;
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
getStatesMatching(nameOrPattern) {
|
|
578
|
+
const matches = [];
|
|
579
|
+
if (nameOrPattern === "*") {
|
|
580
|
+
for (const state of this.states) {
|
|
581
|
+
matches.push(state);
|
|
582
|
+
}
|
|
583
|
+
} else if (nameOrPattern.endsWith(".*")) {
|
|
584
|
+
const prefix = nameOrPattern.slice(0, -1);
|
|
585
|
+
for (const state of this.states) {
|
|
586
|
+
if (state.startsWith(prefix)) {
|
|
587
|
+
matches.push(state);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
} else {
|
|
591
|
+
const name = nameOrPattern;
|
|
592
|
+
if (this.states.has(name)) {
|
|
593
|
+
matches.push(name);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (matches.length === 0) {
|
|
597
|
+
throw new Error(`No states match ${JSON.stringify(nameOrPattern)}`);
|
|
598
|
+
}
|
|
599
|
+
return matches;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Define all allowed outgoing transitions for a state.
|
|
603
|
+
*
|
|
604
|
+
* The targets for each event can be defined as a function which returns the
|
|
605
|
+
* next state to transition to. These functions can look at the `event` or
|
|
606
|
+
* `context` params to conditionally decide which next state to transition
|
|
607
|
+
* to.
|
|
608
|
+
*
|
|
609
|
+
* If you set it to `null`, then the transition will be explicitly forbidden
|
|
610
|
+
* and throw an error. If you don't define a target for a transition, then
|
|
611
|
+
* such events will get ignored.
|
|
612
|
+
*/
|
|
613
|
+
addTransitions(nameOrPattern, mapping) {
|
|
614
|
+
if (this.runningState !== 0 /* NOT_STARTED_YET */) {
|
|
615
|
+
throw new Error("Already started");
|
|
616
|
+
}
|
|
617
|
+
for (const srcState of this.getStatesMatching(nameOrPattern)) {
|
|
618
|
+
let map = this.allowedTransitions.get(srcState);
|
|
619
|
+
if (map === void 0) {
|
|
620
|
+
map = /* @__PURE__ */ new Map();
|
|
621
|
+
this.allowedTransitions.set(srcState, map);
|
|
622
|
+
}
|
|
623
|
+
for (const [type, target_] of Object.entries(mapping)) {
|
|
624
|
+
if (map.has(type)) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
`Trying to set transition "${type}" on "${srcState}" (via "${nameOrPattern}"), but a transition already exists there.`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
const target = target_;
|
|
630
|
+
this.knownEventTypes.add(type);
|
|
631
|
+
if (target !== void 0) {
|
|
632
|
+
const targetFn = typeof target === "function" ? target : () => target;
|
|
633
|
+
map.set(type, targetFn);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Like `.addTransition()`, but takes an (anonymous) transition whenever the
|
|
641
|
+
* timer fires.
|
|
642
|
+
*
|
|
643
|
+
* @param stateOrPattern The state name, or state group pattern name.
|
|
644
|
+
* @param after Number of milliseconds after which to take the
|
|
645
|
+
* transition. If in the mean time, another transition
|
|
646
|
+
* is taken, the timer will get cancelled.
|
|
647
|
+
* @param target The target state to go to.
|
|
648
|
+
*/
|
|
649
|
+
addTimedTransition(stateOrPattern, after2, target) {
|
|
650
|
+
return this.onEnter(stateOrPattern, () => {
|
|
651
|
+
const ms = typeof after2 === "function" ? after2(this.currentContext.current) : after2;
|
|
652
|
+
const timeoutID = setTimeout(() => {
|
|
653
|
+
this.transition({ type: "TIMER" }, target);
|
|
654
|
+
}, ms);
|
|
655
|
+
return () => {
|
|
656
|
+
clearTimeout(timeoutID);
|
|
657
|
+
};
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
getTargetFn(eventName) {
|
|
661
|
+
var _a;
|
|
662
|
+
return (_a = this.allowedTransitions.get(this.currentState)) == null ? void 0 : _a.get(eventName);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Exits the current state, and executes any necessary cleanup functions.
|
|
666
|
+
* Call this before changing the current state to the next state.
|
|
667
|
+
*
|
|
668
|
+
* @param levels Defines how many "levels" of nesting will be exited. For
|
|
669
|
+
* example, if you transition from `foo.bar.qux` to `foo.bar.baz`, then
|
|
670
|
+
* the level is 1. But if you transition from `foo.bar.qux` to `bla.bla`,
|
|
671
|
+
* then the level is 3.
|
|
672
|
+
*/
|
|
673
|
+
exit(levels) {
|
|
674
|
+
this.eventHub.willExitState.notify(this.currentState);
|
|
675
|
+
this.currentContext.allowPatching((patchableContext) => {
|
|
676
|
+
var _a;
|
|
677
|
+
levels = levels != null ? levels : this.cleanupStack.length;
|
|
678
|
+
for (let i = 0; i < levels; i++) {
|
|
679
|
+
(_a = this.cleanupStack.pop()) == null ? void 0 : _a(patchableContext);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Enters the current state, and executes any necessary onEnter handlers.
|
|
685
|
+
* Call this directly _after_ setting the current state to the next state.
|
|
686
|
+
*/
|
|
687
|
+
enter(levels) {
|
|
688
|
+
const enterPatterns = patterns(
|
|
689
|
+
this.currentState,
|
|
690
|
+
levels != null ? levels : this.currentState.split(".").length + 1
|
|
691
|
+
);
|
|
692
|
+
this.currentContext.allowPatching((patchableContext) => {
|
|
693
|
+
for (const pattern of enterPatterns) {
|
|
694
|
+
const enterFn = this.enterFns.get(pattern);
|
|
695
|
+
const cleanupFn = enterFn == null ? void 0 : enterFn(patchableContext);
|
|
696
|
+
if (typeof cleanupFn === "function") {
|
|
697
|
+
this.cleanupStack.push(cleanupFn);
|
|
698
|
+
} else {
|
|
699
|
+
this.cleanupStack.push(null);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
this.eventHub.didEnterState.notify(this.currentState);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Sends an event to the machine, which may cause an internal state
|
|
707
|
+
* transition to happen. When that happens, will trigger side effects.
|
|
708
|
+
*/
|
|
709
|
+
send(event) {
|
|
710
|
+
const targetFn = this.getTargetFn(event.type);
|
|
711
|
+
if (targetFn !== void 0) {
|
|
712
|
+
return this.transition(event, targetFn);
|
|
713
|
+
}
|
|
714
|
+
if (!this.knownEventTypes.has(event.type)) {
|
|
715
|
+
throw new Error(`Invalid event ${JSON.stringify(event.type)}`);
|
|
716
|
+
} else {
|
|
717
|
+
this.eventHub.didIgnoreEvent.notify(event);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
transition(event, target) {
|
|
721
|
+
this.eventHub.didReceiveEvent.notify(event);
|
|
722
|
+
const oldState = this.currentState;
|
|
723
|
+
const targetFn = typeof target === "function" ? target : () => target;
|
|
724
|
+
const nextTarget = targetFn(event, this.currentContext.current);
|
|
725
|
+
let nextState;
|
|
726
|
+
let effects = void 0;
|
|
727
|
+
if (nextTarget === null) {
|
|
728
|
+
this.eventHub.didIgnoreEvent.notify(event);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (typeof nextTarget === "string") {
|
|
732
|
+
nextState = nextTarget;
|
|
733
|
+
} else {
|
|
734
|
+
nextState = nextTarget.target;
|
|
735
|
+
effects = Array.isArray(nextTarget.effect) ? nextTarget.effect : [nextTarget.effect];
|
|
736
|
+
}
|
|
737
|
+
if (!this.states.has(nextState)) {
|
|
738
|
+
throw new Error(`Invalid next state name: ${JSON.stringify(nextState)}`);
|
|
739
|
+
}
|
|
740
|
+
this.eventHub.willTransition.notify({ from: oldState, to: nextState });
|
|
741
|
+
const [up, down] = distance(this.currentState, nextState);
|
|
742
|
+
if (up > 0) {
|
|
743
|
+
this.exit(up);
|
|
744
|
+
}
|
|
745
|
+
this.currentStateOrNull = nextState;
|
|
746
|
+
if (effects !== void 0) {
|
|
747
|
+
const effectsToRun = effects;
|
|
748
|
+
this.currentContext.allowPatching((patchableContext) => {
|
|
749
|
+
for (const effect of effectsToRun) {
|
|
750
|
+
if (typeof effect === "function") {
|
|
751
|
+
effect(patchableContext, event);
|
|
752
|
+
} else {
|
|
753
|
+
patchableContext.patch(effect);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
if (down > 0) {
|
|
759
|
+
this.enter(down);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// src/lib/utils.ts
|
|
765
|
+
function isPlainObject(blob) {
|
|
766
|
+
return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
|
|
767
|
+
}
|
|
768
|
+
function fromEntries(iterable) {
|
|
769
|
+
const obj = {};
|
|
770
|
+
for (const [key, val] of iterable) {
|
|
771
|
+
obj[key] = val;
|
|
772
|
+
}
|
|
773
|
+
return obj;
|
|
774
|
+
}
|
|
775
|
+
function entries(obj) {
|
|
776
|
+
return Object.entries(obj);
|
|
777
|
+
}
|
|
778
|
+
function tryParseJson(rawMessage) {
|
|
779
|
+
try {
|
|
780
|
+
return JSON.parse(rawMessage);
|
|
781
|
+
} catch (e) {
|
|
782
|
+
return void 0;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
function b64decode(b64value) {
|
|
786
|
+
try {
|
|
787
|
+
const formattedValue = b64value.replace(/-/g, "+").replace(/_/g, "/");
|
|
788
|
+
const decodedValue = decodeURIComponent(
|
|
789
|
+
atob(formattedValue).split("").map(function(c) {
|
|
790
|
+
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
|
791
|
+
}).join("")
|
|
792
|
+
);
|
|
793
|
+
return decodedValue;
|
|
794
|
+
} catch (err) {
|
|
795
|
+
return atob(b64value);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function compact(items) {
|
|
799
|
+
return items.filter(
|
|
800
|
+
(item) => item !== null && item !== void 0
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
function compactObject(obj) {
|
|
804
|
+
const newObj = __spreadValues({}, obj);
|
|
805
|
+
Object.keys(obj).forEach((k) => {
|
|
806
|
+
const key = k;
|
|
807
|
+
if (newObj[key] === void 0) {
|
|
808
|
+
delete newObj[key];
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
return newObj;
|
|
812
|
+
}
|
|
813
|
+
function withTimeout(promise, millis, errmsg = "Timed out") {
|
|
814
|
+
return __async(this, null, function* () {
|
|
815
|
+
let timerID;
|
|
816
|
+
const timer$ = new Promise((_, reject) => {
|
|
817
|
+
timerID = setTimeout(() => {
|
|
818
|
+
reject(new Error(errmsg));
|
|
819
|
+
}, millis);
|
|
820
|
+
});
|
|
821
|
+
return Promise.race([promise, timer$]).finally(() => clearTimeout(timerID));
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/connection.ts
|
|
826
|
+
function newToLegacyStatus(status) {
|
|
827
|
+
switch (status) {
|
|
828
|
+
case "connecting":
|
|
829
|
+
return "connecting";
|
|
830
|
+
case "connected":
|
|
831
|
+
return "open";
|
|
832
|
+
case "reconnecting":
|
|
833
|
+
return "unavailable";
|
|
834
|
+
case "disconnected":
|
|
835
|
+
return "failed";
|
|
836
|
+
case "initial":
|
|
837
|
+
return "closed";
|
|
838
|
+
default:
|
|
839
|
+
return "closed";
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function toNewConnectionStatus(machine) {
|
|
843
|
+
const state = machine.currentState;
|
|
844
|
+
switch (state) {
|
|
845
|
+
case "@ok.connected":
|
|
846
|
+
case "@ok.awaiting-pong":
|
|
847
|
+
return "connected";
|
|
848
|
+
case "@idle.initial":
|
|
849
|
+
return "initial";
|
|
850
|
+
case "@auth.busy":
|
|
851
|
+
case "@auth.backoff":
|
|
852
|
+
case "@connecting.busy":
|
|
853
|
+
case "@connecting.backoff":
|
|
854
|
+
return machine.context.successCount > 0 ? "reconnecting" : "connecting";
|
|
855
|
+
case "@idle.failed":
|
|
856
|
+
return "disconnected";
|
|
857
|
+
default:
|
|
858
|
+
return assertNever(state, "Unknown state");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
var BACKOFF_DELAYS = [250, 500, 1e3, 2e3, 4e3, 8e3, 1e4];
|
|
862
|
+
var RESET_DELAY = BACKOFF_DELAYS[0] - 1;
|
|
863
|
+
var BACKOFF_DELAYS_SLOW = [2e3, 3e4, 6e4, 3e5];
|
|
864
|
+
var HEARTBEAT_INTERVAL = 3e4;
|
|
865
|
+
var PONG_TIMEOUT = 2e3;
|
|
866
|
+
var AUTH_TIMEOUT = 1e4;
|
|
867
|
+
var SOCKET_CONNECT_TIMEOUT = 1e4;
|
|
868
|
+
var StopRetrying = class extends Error {
|
|
869
|
+
constructor(reason) {
|
|
870
|
+
super(reason);
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
var LiveblocksError = class extends Error {
|
|
874
|
+
constructor(message, code) {
|
|
875
|
+
super(message);
|
|
876
|
+
this.code = code;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
function nextBackoffDelay(currentDelay, delays = BACKOFF_DELAYS) {
|
|
880
|
+
var _a;
|
|
881
|
+
return (_a = delays.find((delay) => delay > currentDelay)) != null ? _a : delays[delays.length - 1];
|
|
882
|
+
}
|
|
883
|
+
function increaseBackoffDelay(context) {
|
|
884
|
+
context.patch({ backoffDelay: nextBackoffDelay(context.backoffDelay) });
|
|
885
|
+
}
|
|
886
|
+
function increaseBackoffDelayAggressively(context) {
|
|
887
|
+
context.patch({
|
|
888
|
+
backoffDelay: nextBackoffDelay(context.backoffDelay, BACKOFF_DELAYS_SLOW)
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
function resetSuccessCount(context) {
|
|
892
|
+
context.patch({ successCount: 0 });
|
|
893
|
+
}
|
|
894
|
+
function log(level, message) {
|
|
895
|
+
const logger = level === 2 /* ERROR */ ? error : level === 1 /* WARN */ ? warn : (
|
|
896
|
+
/* black hole */
|
|
897
|
+
() => {
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
return () => {
|
|
901
|
+
logger(message);
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function logPrematureErrorOrCloseEvent(e) {
|
|
905
|
+
const conn = "Connection to Liveblocks websocket server";
|
|
906
|
+
return (ctx) => {
|
|
907
|
+
if (e instanceof Error) {
|
|
908
|
+
warn(`${conn} could not be established. ${String(e)}`);
|
|
909
|
+
} else {
|
|
910
|
+
warn(
|
|
911
|
+
isCloseEvent(e) ? `${conn} closed prematurely (code: ${e.code}). Retrying in ${ctx.backoffDelay}ms.` : `${conn} could not be established.`
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function logCloseEvent(event) {
|
|
917
|
+
return (ctx) => {
|
|
918
|
+
warn(
|
|
919
|
+
`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${ctx.backoffDelay}ms.`
|
|
920
|
+
);
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
var logPermanentClose = log(
|
|
924
|
+
1 /* WARN */,
|
|
925
|
+
"Connection to WebSocket closed permanently. Won't retry."
|
|
926
|
+
);
|
|
927
|
+
function sendHeartbeat(ctx) {
|
|
928
|
+
var _a;
|
|
929
|
+
(_a = ctx.socket) == null ? void 0 : _a.send("ping");
|
|
930
|
+
}
|
|
931
|
+
function isCloseEvent(error2) {
|
|
932
|
+
return !(error2 instanceof Error) && error2.type === "close";
|
|
933
|
+
}
|
|
934
|
+
function isCustomCloseEvent(error2) {
|
|
935
|
+
return isCloseEvent(error2) && error2.code >= 4e3 && error2.code < 4100;
|
|
936
|
+
}
|
|
937
|
+
function enableTracing(machine) {
|
|
938
|
+
const start = (/* @__PURE__ */ new Date()).getTime();
|
|
939
|
+
function log2(...args) {
|
|
940
|
+
warn(
|
|
941
|
+
`${(((/* @__PURE__ */ new Date()).getTime() - start) / 1e3).toFixed(2)} [FSM #${machine.id}]`,
|
|
942
|
+
...args
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
const unsubs = [
|
|
946
|
+
machine.events.didReceiveEvent.subscribe((e) => log2(`Event ${e.type}`)),
|
|
947
|
+
machine.events.willTransition.subscribe(
|
|
948
|
+
({ from, to }) => log2("Transitioning", from, "\u2192", to)
|
|
949
|
+
),
|
|
950
|
+
machine.events.didIgnoreEvent.subscribe(
|
|
951
|
+
(e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
|
|
952
|
+
)
|
|
953
|
+
// machine.events.willExitState.subscribe((s) => log("Exiting state", s)),
|
|
954
|
+
// machine.events.didEnterState.subscribe((s) => log("Entering state", s)),
|
|
955
|
+
];
|
|
956
|
+
return () => {
|
|
957
|
+
for (const unsub of unsubs) {
|
|
958
|
+
unsub();
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function defineConnectivityEvents(machine) {
|
|
963
|
+
const statusDidChange = makeEventSource();
|
|
964
|
+
const didConnect = makeEventSource();
|
|
965
|
+
const didDisconnect = makeEventSource();
|
|
966
|
+
let lastStatus = null;
|
|
967
|
+
const unsubscribe = machine.events.didEnterState.subscribe(() => {
|
|
968
|
+
const currStatus = toNewConnectionStatus(machine);
|
|
969
|
+
if (currStatus !== lastStatus) {
|
|
970
|
+
statusDidChange.notify(currStatus);
|
|
971
|
+
}
|
|
972
|
+
if (lastStatus === "connected" && currStatus !== "connected") {
|
|
973
|
+
didDisconnect.notify();
|
|
974
|
+
} else if (lastStatus !== "connected" && currStatus === "connected") {
|
|
975
|
+
didConnect.notify();
|
|
976
|
+
}
|
|
977
|
+
lastStatus = currStatus;
|
|
978
|
+
});
|
|
979
|
+
return {
|
|
980
|
+
statusDidChange: statusDidChange.observable,
|
|
981
|
+
didConnect: didConnect.observable,
|
|
982
|
+
didDisconnect: didDisconnect.observable,
|
|
983
|
+
unsubscribe
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
var assign = (patch) => (ctx) => ctx.patch(patch);
|
|
987
|
+
function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
988
|
+
const onMessage = makeEventSource();
|
|
989
|
+
onMessage.pause();
|
|
990
|
+
const onLiveblocksError = makeEventSource();
|
|
991
|
+
const initialContext = {
|
|
992
|
+
successCount: 0,
|
|
993
|
+
token: null,
|
|
994
|
+
socket: null,
|
|
995
|
+
backoffDelay: RESET_DELAY
|
|
996
|
+
};
|
|
997
|
+
const machine = new FSM(initialContext).addState("@idle.initial").addState("@idle.failed").addState("@auth.busy").addState("@auth.backoff").addState("@connecting.busy").addState("@connecting.backoff").addState("@ok.connected").addState("@ok.awaiting-pong");
|
|
998
|
+
machine.addTransitions("*", {
|
|
999
|
+
RECONNECT: {
|
|
1000
|
+
target: "@auth.backoff",
|
|
1001
|
+
effect: [increaseBackoffDelay, resetSuccessCount]
|
|
1002
|
+
},
|
|
1003
|
+
DISCONNECT: "@idle.initial"
|
|
1004
|
+
});
|
|
1005
|
+
machine.onEnter("@idle.*", resetSuccessCount).addTransitions("@idle.*", {
|
|
1006
|
+
CONNECT: (_, ctx) => (
|
|
1007
|
+
// If we still have a known token, try to reconnect to the socket directly,
|
|
1008
|
+
// otherwise, try to obtain a new token
|
|
1009
|
+
ctx.token !== null ? "@connecting.busy" : "@auth.busy"
|
|
1010
|
+
)
|
|
1011
|
+
});
|
|
1012
|
+
machine.addTransitions("@auth.backoff", {
|
|
1013
|
+
NAVIGATOR_ONLINE: {
|
|
1014
|
+
target: "@auth.busy",
|
|
1015
|
+
effect: assign({ backoffDelay: RESET_DELAY })
|
|
1016
|
+
}
|
|
1017
|
+
}).addTimedTransition(
|
|
1018
|
+
"@auth.backoff",
|
|
1019
|
+
(ctx) => ctx.backoffDelay,
|
|
1020
|
+
"@auth.busy"
|
|
1021
|
+
).onEnterAsync(
|
|
1022
|
+
"@auth.busy",
|
|
1023
|
+
() => withTimeout(delegates.authenticate(), AUTH_TIMEOUT),
|
|
1024
|
+
// On successful authentication
|
|
1025
|
+
(okEvent) => ({
|
|
1026
|
+
target: "@connecting.busy",
|
|
1027
|
+
effect: assign({
|
|
1028
|
+
token: okEvent.data,
|
|
1029
|
+
backoffDelay: RESET_DELAY
|
|
1030
|
+
})
|
|
1031
|
+
}),
|
|
1032
|
+
// Auth failed
|
|
1033
|
+
(failedEvent) => {
|
|
1034
|
+
if (failedEvent.reason instanceof StopRetrying) {
|
|
1035
|
+
return {
|
|
1036
|
+
target: "@idle.failed",
|
|
1037
|
+
effect: log(2 /* ERROR */, failedEvent.reason.message)
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
return {
|
|
1041
|
+
target: "@auth.backoff",
|
|
1042
|
+
effect: [
|
|
1043
|
+
increaseBackoffDelay,
|
|
1044
|
+
log(
|
|
1045
|
+
2 /* ERROR */,
|
|
1046
|
+
`Authentication failed: ${failedEvent.reason instanceof Error ? failedEvent.reason.message : String(failedEvent.reason)}`
|
|
1047
|
+
)
|
|
1048
|
+
]
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
|
|
1053
|
+
const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
|
|
1054
|
+
const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
|
|
1055
|
+
function teardownSocket(socket) {
|
|
1056
|
+
if (socket) {
|
|
1057
|
+
socket.removeEventListener("error", onSocketError);
|
|
1058
|
+
socket.removeEventListener("close", onSocketClose);
|
|
1059
|
+
socket.removeEventListener("message", onSocketMessage);
|
|
1060
|
+
socket.close();
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
machine.addTransitions("@connecting.backoff", {
|
|
1064
|
+
NAVIGATOR_ONLINE: {
|
|
1065
|
+
target: "@connecting.busy",
|
|
1066
|
+
effect: assign({ backoffDelay: RESET_DELAY })
|
|
1067
|
+
}
|
|
1068
|
+
}).addTimedTransition(
|
|
1069
|
+
"@connecting.backoff",
|
|
1070
|
+
(ctx) => ctx.backoffDelay,
|
|
1071
|
+
"@connecting.busy"
|
|
1072
|
+
).onEnterAsync(
|
|
1073
|
+
"@connecting.busy",
|
|
1074
|
+
//
|
|
1075
|
+
// Use the "createSocket" delegate function (provided to the
|
|
1076
|
+
// ManagedSocket) to create the actual WebSocket connection instance.
|
|
1077
|
+
// Then, set up all the necessary event listeners, and wait for the
|
|
1078
|
+
// "open" event to occur.
|
|
1079
|
+
//
|
|
1080
|
+
// When the "open" event happens, we're ready to transition to the
|
|
1081
|
+
// OK state. This is done by resolving the Promise.
|
|
1082
|
+
//
|
|
1083
|
+
(ctx) => __async(this, null, function* () {
|
|
1084
|
+
let capturedPrematureEvent = null;
|
|
1085
|
+
const connect$ = new Promise(
|
|
1086
|
+
(resolve, rej) => {
|
|
1087
|
+
if (ctx.token === null) {
|
|
1088
|
+
throw new Error("No auth token");
|
|
1089
|
+
}
|
|
1090
|
+
const socket = delegates.createSocket(ctx.token);
|
|
1091
|
+
function reject(event) {
|
|
1092
|
+
capturedPrematureEvent = event;
|
|
1093
|
+
socket.removeEventListener("message", onSocketMessage);
|
|
1094
|
+
rej(event);
|
|
1095
|
+
}
|
|
1096
|
+
socket.addEventListener("message", onSocketMessage);
|
|
1097
|
+
socket.addEventListener("error", reject);
|
|
1098
|
+
socket.addEventListener("close", reject);
|
|
1099
|
+
socket.addEventListener("open", () => {
|
|
1100
|
+
socket.addEventListener("error", onSocketError);
|
|
1101
|
+
socket.addEventListener("close", onSocketClose);
|
|
1102
|
+
const unsub = () => {
|
|
1103
|
+
socket.removeEventListener("error", reject);
|
|
1104
|
+
socket.removeEventListener("close", reject);
|
|
1105
|
+
};
|
|
1106
|
+
resolve([socket, unsub]);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
);
|
|
1110
|
+
return withTimeout(connect$, SOCKET_CONNECT_TIMEOUT).then(
|
|
1111
|
+
//
|
|
1112
|
+
// Part 3:
|
|
1113
|
+
// By now, our "open" event has fired, and the promise has been
|
|
1114
|
+
// resolved. Two possible scenarios:
|
|
1115
|
+
//
|
|
1116
|
+
// 1. The happy path. Most likely.
|
|
1117
|
+
// 2. Uh-oh. A premature close/error event has been observed. Let's
|
|
1118
|
+
// reject the promise after all.
|
|
1119
|
+
//
|
|
1120
|
+
// Any close/error event that will get scheduled after this point
|
|
1121
|
+
// onwards, will be caught in the OK state, and dealt with
|
|
1122
|
+
// accordingly.
|
|
1123
|
+
//
|
|
1124
|
+
([socket, unsub]) => {
|
|
1125
|
+
unsub();
|
|
1126
|
+
if (capturedPrematureEvent) {
|
|
1127
|
+
throw capturedPrematureEvent;
|
|
1128
|
+
}
|
|
1129
|
+
return socket;
|
|
1130
|
+
}
|
|
1131
|
+
);
|
|
1132
|
+
}),
|
|
1133
|
+
// Only transition to OK state after a successfully opened WebSocket connection
|
|
1134
|
+
(okEvent) => ({
|
|
1135
|
+
target: "@ok.connected",
|
|
1136
|
+
effect: assign({
|
|
1137
|
+
socket: okEvent.data,
|
|
1138
|
+
backoffDelay: RESET_DELAY
|
|
1139
|
+
})
|
|
1140
|
+
}),
|
|
1141
|
+
// If the WebSocket connection cannot be established
|
|
1142
|
+
(failure) => {
|
|
1143
|
+
const err = failure.reason;
|
|
1144
|
+
if (err instanceof StopRetrying) {
|
|
1145
|
+
return {
|
|
1146
|
+
target: "@idle.failed",
|
|
1147
|
+
effect: log(2 /* ERROR */, err.message)
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
if (isCloseEvent(err) && err.code === 4999) {
|
|
1151
|
+
return {
|
|
1152
|
+
target: "@idle.failed",
|
|
1153
|
+
effect: log(2 /* ERROR */, err.reason)
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
if (isCustomCloseEvent(err) && err.code !== 4001) {
|
|
1157
|
+
return {
|
|
1158
|
+
target: "@connecting.backoff",
|
|
1159
|
+
effect: [
|
|
1160
|
+
increaseBackoffDelayAggressively,
|
|
1161
|
+
logPrematureErrorOrCloseEvent(err)
|
|
1162
|
+
]
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
return {
|
|
1166
|
+
target: "@auth.backoff",
|
|
1167
|
+
effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
);
|
|
1171
|
+
machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, {
|
|
1172
|
+
target: "@ok.awaiting-pong",
|
|
1173
|
+
effect: sendHeartbeat
|
|
1174
|
+
}).addTransitions("@ok.connected", {
|
|
1175
|
+
WINDOW_GOT_FOCUS: { target: "@ok.awaiting-pong", effect: sendHeartbeat }
|
|
1176
|
+
});
|
|
1177
|
+
const noPongAction = {
|
|
1178
|
+
target: "@connecting.busy",
|
|
1179
|
+
// Log implicit connection loss and drop the current open socket
|
|
1180
|
+
effect: log(
|
|
1181
|
+
1 /* WARN */,
|
|
1182
|
+
"Received no pong from server, assume implicit connection loss."
|
|
1183
|
+
)
|
|
1184
|
+
};
|
|
1185
|
+
machine.onEnter("@ok.*", (ctx) => {
|
|
1186
|
+
ctx.patch({ successCount: ctx.successCount + 1 });
|
|
1187
|
+
const timerID = setTimeout(
|
|
1188
|
+
// On the next tick, start delivering all messages that have already
|
|
1189
|
+
// been received, and continue synchronous delivery of all future
|
|
1190
|
+
// incoming messages.
|
|
1191
|
+
onMessage.unpause,
|
|
1192
|
+
0
|
|
1193
|
+
);
|
|
1194
|
+
return (ctx2) => {
|
|
1195
|
+
teardownSocket(ctx2.socket);
|
|
1196
|
+
ctx2.patch({ socket: null });
|
|
1197
|
+
clearTimeout(timerID);
|
|
1198
|
+
onMessage.pause();
|
|
1199
|
+
};
|
|
1200
|
+
}).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, noPongAction).addTransitions("@ok.awaiting-pong", { PONG_TIMEOUT: noPongAction }).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTransitions("@ok.*", {
|
|
1201
|
+
// When a socket receives an error, this can cause the closing of the
|
|
1202
|
+
// socket, or not. So always check to see if the socket is still OPEN or
|
|
1203
|
+
// not. When still OPEN, don't transition.
|
|
1204
|
+
EXPLICIT_SOCKET_ERROR: (_, context) => {
|
|
1205
|
+
var _a;
|
|
1206
|
+
if (((_a = context.socket) == null ? void 0 : _a.readyState) === 1) {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
return {
|
|
1210
|
+
target: "@connecting.backoff",
|
|
1211
|
+
effect: increaseBackoffDelay
|
|
1212
|
+
};
|
|
1213
|
+
},
|
|
1214
|
+
EXPLICIT_SOCKET_CLOSE: (e) => {
|
|
1215
|
+
if (e.event.code === 4999) {
|
|
1216
|
+
return {
|
|
1217
|
+
target: "@idle.failed",
|
|
1218
|
+
effect: logPermanentClose
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
if (e.event.code === 4001) {
|
|
1222
|
+
return {
|
|
1223
|
+
target: "@auth.backoff",
|
|
1224
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
if (isCustomCloseEvent(e.event)) {
|
|
1228
|
+
return {
|
|
1229
|
+
target: "@connecting.backoff",
|
|
1230
|
+
effect: [
|
|
1231
|
+
increaseBackoffDelayAggressively,
|
|
1232
|
+
logCloseEvent(e.event),
|
|
1233
|
+
() => {
|
|
1234
|
+
const err = new LiveblocksError(e.event.reason, e.event.code);
|
|
1235
|
+
onLiveblocksError.notify(err);
|
|
1236
|
+
}
|
|
1237
|
+
]
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
return {
|
|
1241
|
+
target: "@connecting.backoff",
|
|
1242
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
if (typeof document !== "undefined") {
|
|
1247
|
+
const doc = typeof document !== "undefined" ? document : void 0;
|
|
1248
|
+
const win = typeof window !== "undefined" ? window : void 0;
|
|
1249
|
+
const root = win != null ? win : doc;
|
|
1250
|
+
machine.onEnter("*", (ctx) => {
|
|
1251
|
+
function onBackOnline() {
|
|
1252
|
+
machine.send({ type: "NAVIGATOR_ONLINE" });
|
|
1253
|
+
}
|
|
1254
|
+
function onVisibilityChange() {
|
|
1255
|
+
if ((doc == null ? void 0 : doc.visibilityState) === "visible") {
|
|
1256
|
+
machine.send({ type: "WINDOW_GOT_FOCUS" });
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
win == null ? void 0 : win.addEventListener("online", onBackOnline);
|
|
1260
|
+
root == null ? void 0 : root.addEventListener("visibilitychange", onVisibilityChange);
|
|
1261
|
+
return () => {
|
|
1262
|
+
root == null ? void 0 : root.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1263
|
+
win == null ? void 0 : win.removeEventListener("online", onBackOnline);
|
|
1264
|
+
teardownSocket(ctx.socket);
|
|
1265
|
+
};
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
const cleanups = [];
|
|
1269
|
+
const { statusDidChange, didConnect, didDisconnect, unsubscribe } = defineConnectivityEvents(machine);
|
|
1270
|
+
cleanups.push(unsubscribe);
|
|
1271
|
+
if (enableDebugLogging) {
|
|
1272
|
+
cleanups.push(enableTracing(machine));
|
|
1273
|
+
}
|
|
1274
|
+
machine.start();
|
|
1275
|
+
return {
|
|
1276
|
+
machine,
|
|
1277
|
+
cleanups,
|
|
1278
|
+
// Observable events that will be emitted by this machine
|
|
1279
|
+
events: {
|
|
1280
|
+
statusDidChange,
|
|
1281
|
+
didConnect,
|
|
1282
|
+
didDisconnect,
|
|
1283
|
+
onMessage: onMessage.observable,
|
|
1284
|
+
onLiveblocksError: onLiveblocksError.observable
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
var ManagedSocket = class {
|
|
1289
|
+
constructor(delegates, enableDebugLogging = false) {
|
|
1290
|
+
const { machine, events, cleanups } = createConnectionStateMachine(
|
|
1291
|
+
delegates,
|
|
1292
|
+
enableDebugLogging
|
|
1293
|
+
);
|
|
1294
|
+
this.machine = machine;
|
|
1295
|
+
this.events = events;
|
|
1296
|
+
this.cleanups = cleanups;
|
|
1297
|
+
}
|
|
1298
|
+
getLegacyStatus() {
|
|
1299
|
+
return newToLegacyStatus(this.getStatus());
|
|
1300
|
+
}
|
|
1301
|
+
getStatus() {
|
|
1302
|
+
try {
|
|
1303
|
+
return toNewConnectionStatus(this.machine);
|
|
1304
|
+
} catch (e) {
|
|
1305
|
+
return "initial";
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Returns the current auth token.
|
|
1310
|
+
*/
|
|
1311
|
+
get token() {
|
|
1312
|
+
return this.machine.context.token;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Call this method to try to connect to a WebSocket. This only has an effect
|
|
1316
|
+
* if the machine is idle at the moment, otherwise this is a no-op.
|
|
1317
|
+
*/
|
|
1318
|
+
connect() {
|
|
1319
|
+
this.machine.send({ type: "CONNECT" });
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* If idle, will try to connect. Otherwise, it will attempt to reconnect to
|
|
1323
|
+
* the socket, potentially obtaining a new token first, if needed.
|
|
1324
|
+
*/
|
|
1325
|
+
reconnect() {
|
|
1326
|
+
this.machine.send({ type: "RECONNECT" });
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Call this method to disconnect from the current WebSocket. Is going to be
|
|
1330
|
+
* a no-op if there is no active connection.
|
|
1331
|
+
*/
|
|
1332
|
+
disconnect() {
|
|
1333
|
+
this.machine.send({ type: "DISCONNECT" });
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Call this to stop the machine and run necessary cleanup functions. After
|
|
1337
|
+
* calling destroy(), you can no longer use this instance. Call this before
|
|
1338
|
+
* letting the instance get garbage collected.
|
|
1339
|
+
*/
|
|
1340
|
+
destroy() {
|
|
1341
|
+
this.machine.stop();
|
|
1342
|
+
let cleanup;
|
|
1343
|
+
while (cleanup = this.cleanups.pop()) {
|
|
1344
|
+
cleanup();
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Safely send a message to the current WebSocket connection. Will emit a log
|
|
1349
|
+
* message if this is somehow impossible.
|
|
1350
|
+
*/
|
|
1351
|
+
send(data) {
|
|
1352
|
+
var _a;
|
|
1353
|
+
const socket = (_a = this.machine.context) == null ? void 0 : _a.socket;
|
|
1354
|
+
if (socket === null) {
|
|
1355
|
+
warn("Cannot send: not connected yet", data);
|
|
1356
|
+
} else if (socket.readyState !== 1) {
|
|
1357
|
+
warn("Cannot send: WebSocket no longer open", data);
|
|
1358
|
+
} else {
|
|
1359
|
+
socket.send(data);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* NOTE: Used by the E2E app only, to simulate explicit events.
|
|
1364
|
+
* Not ideal to keep exposed :(
|
|
1365
|
+
*/
|
|
1366
|
+
_privateSendMachineEvent(event) {
|
|
1367
|
+
this.machine.send(event);
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
|
|
348
1371
|
// src/lib/position.ts
|
|
349
1372
|
var MIN_CODE = 32;
|
|
350
1373
|
var MAX_CODE = 126;
|
|
@@ -625,86 +1648,36 @@ var AbstractCrdt = class {
|
|
|
625
1648
|
* mutation to the Live node.
|
|
626
1649
|
*/
|
|
627
1650
|
invalidate() {
|
|
628
|
-
if (this._cachedImmutable !== void 0 || this._cachedTreeNode !== void 0) {
|
|
629
|
-
this._cachedImmutable = void 0;
|
|
630
|
-
this._cachedTreeNode = void 0;
|
|
631
|
-
if (this.parent.type === "HasParent") {
|
|
632
|
-
this.parent.node.invalidate();
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* @internal
|
|
638
|
-
*
|
|
639
|
-
* Return an snapshot of this Live tree for use in DevTools.
|
|
640
|
-
*/
|
|
641
|
-
toTreeNode(key) {
|
|
642
|
-
if (this._cachedTreeNode === void 0 || this._cachedTreeNodeKey !== key) {
|
|
643
|
-
this._cachedTreeNodeKey = key;
|
|
644
|
-
this._cachedTreeNode = this._toTreeNode(key);
|
|
645
|
-
}
|
|
646
|
-
return this._cachedTreeNode;
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Return an immutable snapshot of this Live node and its children.
|
|
650
|
-
*/
|
|
651
|
-
toImmutable() {
|
|
652
|
-
if (this._cachedImmutable === void 0) {
|
|
653
|
-
this._cachedImmutable = this._toImmutable();
|
|
654
|
-
}
|
|
655
|
-
return this._cachedImmutable;
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
// src/lib/utils.ts
|
|
660
|
-
function isPlainObject(blob) {
|
|
661
|
-
return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
|
|
662
|
-
}
|
|
663
|
-
function fromEntries(iterable) {
|
|
664
|
-
const obj = {};
|
|
665
|
-
for (const [key, val] of iterable) {
|
|
666
|
-
obj[key] = val;
|
|
667
|
-
}
|
|
668
|
-
return obj;
|
|
669
|
-
}
|
|
670
|
-
function entries(obj) {
|
|
671
|
-
return Object.entries(obj);
|
|
672
|
-
}
|
|
673
|
-
function tryParseJson(rawMessage) {
|
|
674
|
-
try {
|
|
675
|
-
return JSON.parse(rawMessage);
|
|
676
|
-
} catch (e) {
|
|
677
|
-
return void 0;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
function b64decode(b64value) {
|
|
681
|
-
try {
|
|
682
|
-
const formattedValue = b64value.replace(/-/g, "+").replace(/_/g, "/");
|
|
683
|
-
const decodedValue = decodeURIComponent(
|
|
684
|
-
atob(formattedValue).split("").map(function(c) {
|
|
685
|
-
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
|
686
|
-
}).join("")
|
|
687
|
-
);
|
|
688
|
-
return decodedValue;
|
|
689
|
-
} catch (err) {
|
|
690
|
-
return atob(b64value);
|
|
1651
|
+
if (this._cachedImmutable !== void 0 || this._cachedTreeNode !== void 0) {
|
|
1652
|
+
this._cachedImmutable = void 0;
|
|
1653
|
+
this._cachedTreeNode = void 0;
|
|
1654
|
+
if (this.parent.type === "HasParent") {
|
|
1655
|
+
this.parent.node.invalidate();
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
691
1658
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const key = k;
|
|
702
|
-
if (newObj[key] === void 0) {
|
|
703
|
-
delete newObj[key];
|
|
1659
|
+
/**
|
|
1660
|
+
* @internal
|
|
1661
|
+
*
|
|
1662
|
+
* Return an snapshot of this Live tree for use in DevTools.
|
|
1663
|
+
*/
|
|
1664
|
+
toTreeNode(key) {
|
|
1665
|
+
if (this._cachedTreeNode === void 0 || this._cachedTreeNodeKey !== key) {
|
|
1666
|
+
this._cachedTreeNodeKey = key;
|
|
1667
|
+
this._cachedTreeNode = this._toTreeNode(key);
|
|
704
1668
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1669
|
+
return this._cachedTreeNode;
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Return an immutable snapshot of this Live node and its children.
|
|
1673
|
+
*/
|
|
1674
|
+
toImmutable() {
|
|
1675
|
+
if (this._cachedImmutable === void 0) {
|
|
1676
|
+
this._cachedImmutable = this._toImmutable();
|
|
1677
|
+
}
|
|
1678
|
+
return this._cachedImmutable;
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
708
1681
|
|
|
709
1682
|
// src/protocol/SerializedCrdt.ts
|
|
710
1683
|
var CrdtType = /* @__PURE__ */ ((CrdtType2) => {
|
|
@@ -3239,35 +4212,12 @@ var DerivedRef = class extends ImmutableRef {
|
|
|
3239
4212
|
}
|
|
3240
4213
|
};
|
|
3241
4214
|
|
|
3242
|
-
// src/types/IWebSocket.ts
|
|
3243
|
-
var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
|
|
3244
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
3245
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["INVALID_MESSAGE_FORMAT"] = 4e3] = "INVALID_MESSAGE_FORMAT";
|
|
3246
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
3247
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
3248
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
3249
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
3250
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
3251
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
3252
|
-
return WebsocketCloseCodes2;
|
|
3253
|
-
})(WebsocketCloseCodes || {});
|
|
3254
|
-
|
|
3255
4215
|
// src/room.ts
|
|
3256
|
-
var BACKOFF_RETRY_DELAYS = [250, 500, 1e3, 2e3, 4e3, 8e3, 1e4];
|
|
3257
|
-
var BACKOFF_RETRY_DELAYS_SLOW = [2e3, 3e4, 6e4, 3e5];
|
|
3258
|
-
var HEARTBEAT_INTERVAL = 3e4;
|
|
3259
|
-
var PONG_TIMEOUT = 2e3;
|
|
3260
4216
|
var MAX_MESSAGE_SIZE = 1024 * 1024 - 128;
|
|
3261
4217
|
function makeIdFactory(connectionId) {
|
|
3262
4218
|
let count = 0;
|
|
3263
4219
|
return () => `${connectionId}:${count++}`;
|
|
3264
4220
|
}
|
|
3265
|
-
function log(..._params) {
|
|
3266
|
-
return;
|
|
3267
|
-
}
|
|
3268
|
-
function isConnectionSelfAware(connection) {
|
|
3269
|
-
return connection.status === "open" || connection.status === "connecting";
|
|
3270
|
-
}
|
|
3271
4221
|
function userToTreeNode(key, user) {
|
|
3272
4222
|
return {
|
|
3273
4223
|
type: "User",
|
|
@@ -3277,21 +4227,27 @@ function userToTreeNode(key, user) {
|
|
|
3277
4227
|
};
|
|
3278
4228
|
}
|
|
3279
4229
|
function createRoom(options, config) {
|
|
3280
|
-
var _a;
|
|
4230
|
+
var _a, _b, _c, _d;
|
|
3281
4231
|
const initialPresence = typeof options.initialPresence === "function" ? options.initialPresence(config.roomId) : options.initialPresence;
|
|
3282
4232
|
const initialStorage = typeof options.initialStorage === "function" ? options.initialStorage(config.roomId) : options.initialStorage;
|
|
4233
|
+
const delegates = (_c = config.delegates) != null ? _c : {
|
|
4234
|
+
authenticate: makeAuthDelegateForRoom(
|
|
4235
|
+
config.roomId,
|
|
4236
|
+
config.authentication,
|
|
4237
|
+
(_a = config.polyfills) == null ? void 0 : _a.fetch
|
|
4238
|
+
),
|
|
4239
|
+
createSocket: makeCreateSocketDelegateForRoom(
|
|
4240
|
+
config.liveblocksServer,
|
|
4241
|
+
(_b = config.polyfills) == null ? void 0 : _b.WebSocket
|
|
4242
|
+
)
|
|
4243
|
+
};
|
|
4244
|
+
const managedSocket = new ManagedSocket(
|
|
4245
|
+
delegates,
|
|
4246
|
+
config.enableDebugLogging
|
|
4247
|
+
);
|
|
3283
4248
|
const context = {
|
|
3284
|
-
token: null,
|
|
3285
|
-
lastConnectionId: null,
|
|
3286
|
-
socket: null,
|
|
3287
|
-
numRetries: 0,
|
|
3288
|
-
timers: {
|
|
3289
|
-
flush: void 0,
|
|
3290
|
-
reconnect: void 0,
|
|
3291
|
-
heartbeat: void 0,
|
|
3292
|
-
pongTimeout: void 0
|
|
3293
|
-
},
|
|
3294
4249
|
buffer: {
|
|
4250
|
+
flushTimerID: void 0,
|
|
3295
4251
|
lastFlushedAt: 0,
|
|
3296
4252
|
me: (
|
|
3297
4253
|
// Queue up the initial presence message as a Full Presence™ update
|
|
@@ -3303,7 +4259,7 @@ function createRoom(options, config) {
|
|
|
3303
4259
|
messages: [],
|
|
3304
4260
|
storageOperations: []
|
|
3305
4261
|
},
|
|
3306
|
-
|
|
4262
|
+
sessionInfo: new ValueRef(null),
|
|
3307
4263
|
me: new MeRef(initialPresence),
|
|
3308
4264
|
others: new OthersRef(),
|
|
3309
4265
|
initialStorage,
|
|
@@ -3322,7 +4278,91 @@ function createRoom(options, config) {
|
|
|
3322
4278
|
opStackTraces: process.env.NODE_ENV !== "production" ? /* @__PURE__ */ new Map() : void 0
|
|
3323
4279
|
};
|
|
3324
4280
|
const doNotBatchUpdates = (cb) => cb();
|
|
3325
|
-
const batchUpdates = (
|
|
4281
|
+
const batchUpdates = (_d = config.unstable_batchedUpdates) != null ? _d : doNotBatchUpdates;
|
|
4282
|
+
let lastToken;
|
|
4283
|
+
function onStatusDidChange(newStatus) {
|
|
4284
|
+
var _a2;
|
|
4285
|
+
const token = (_a2 = managedSocket.token) == null ? void 0 : _a2.parsed;
|
|
4286
|
+
if (token !== void 0 && token !== lastToken) {
|
|
4287
|
+
context.sessionInfo.set({
|
|
4288
|
+
id: token.actor,
|
|
4289
|
+
userInfo: token.info,
|
|
4290
|
+
userId: token.id,
|
|
4291
|
+
isReadOnly: isStorageReadOnly(token.scopes)
|
|
4292
|
+
});
|
|
4293
|
+
lastToken = token;
|
|
4294
|
+
}
|
|
4295
|
+
batchUpdates(() => {
|
|
4296
|
+
eventHub.status.notify(newStatus);
|
|
4297
|
+
eventHub.connection.notify(newToLegacyStatus(newStatus));
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
let _connectionLossTimerId;
|
|
4301
|
+
let _hasLostConnection = false;
|
|
4302
|
+
function handleConnectionLossEvent(newStatus) {
|
|
4303
|
+
if (newStatus === "reconnecting") {
|
|
4304
|
+
_connectionLossTimerId = setTimeout(() => {
|
|
4305
|
+
batchUpdates(() => {
|
|
4306
|
+
eventHub.lostConnection.notify("lost");
|
|
4307
|
+
_hasLostConnection = true;
|
|
4308
|
+
context.others.clearOthers();
|
|
4309
|
+
notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
|
|
4310
|
+
});
|
|
4311
|
+
}, config.lostConnectionTimeout);
|
|
4312
|
+
} else {
|
|
4313
|
+
clearTimeout(_connectionLossTimerId);
|
|
4314
|
+
if (_hasLostConnection) {
|
|
4315
|
+
if (newStatus === "disconnected") {
|
|
4316
|
+
batchUpdates(() => {
|
|
4317
|
+
eventHub.lostConnection.notify("failed");
|
|
4318
|
+
});
|
|
4319
|
+
} else {
|
|
4320
|
+
batchUpdates(() => {
|
|
4321
|
+
eventHub.lostConnection.notify("restored");
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
_hasLostConnection = false;
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
function onDidConnect() {
|
|
4329
|
+
const sessionInfo = context.sessionInfo.current;
|
|
4330
|
+
if (sessionInfo === null) {
|
|
4331
|
+
throw new Error("Unexpected missing session info");
|
|
4332
|
+
}
|
|
4333
|
+
context.buffer.me = {
|
|
4334
|
+
type: "full",
|
|
4335
|
+
data: (
|
|
4336
|
+
// Because context.me.current is a readonly object, we'll have to
|
|
4337
|
+
// make a copy here. Otherwise, type errors happen later when
|
|
4338
|
+
// "patching" my presence.
|
|
4339
|
+
__spreadValues({}, context.me.current)
|
|
4340
|
+
)
|
|
4341
|
+
};
|
|
4342
|
+
context.idFactory = makeIdFactory(sessionInfo.id);
|
|
4343
|
+
if (context.root) {
|
|
4344
|
+
context.buffer.messages.push({ type: 200 /* FETCH_STORAGE */ });
|
|
4345
|
+
}
|
|
4346
|
+
tryFlushing();
|
|
4347
|
+
}
|
|
4348
|
+
function onDidDisconnect() {
|
|
4349
|
+
clearTimeout(context.buffer.flushTimerID);
|
|
4350
|
+
}
|
|
4351
|
+
managedSocket.events.onMessage.subscribe(handleServerMessage);
|
|
4352
|
+
managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
|
|
4353
|
+
managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent);
|
|
4354
|
+
managedSocket.events.didConnect.subscribe(onDidConnect);
|
|
4355
|
+
managedSocket.events.didDisconnect.subscribe(onDidDisconnect);
|
|
4356
|
+
managedSocket.events.onLiveblocksError.subscribe((err) => {
|
|
4357
|
+
batchUpdates(() => {
|
|
4358
|
+
if (process.env.NODE_ENV !== "production") {
|
|
4359
|
+
error(
|
|
4360
|
+
`Connection to websocket server closed. Reason: ${err.message} (code: ${err.code}).`
|
|
4361
|
+
);
|
|
4362
|
+
}
|
|
4363
|
+
eventHub.error.notify(err);
|
|
4364
|
+
});
|
|
4365
|
+
});
|
|
3326
4366
|
const pool = {
|
|
3327
4367
|
roomId: config.roomId,
|
|
3328
4368
|
getNode: (id) => context.nodes.get(id),
|
|
@@ -3364,7 +4404,8 @@ function createRoom(options, config) {
|
|
|
3364
4404
|
}
|
|
3365
4405
|
},
|
|
3366
4406
|
assertStorageIsWritable: () => {
|
|
3367
|
-
|
|
4407
|
+
var _a2;
|
|
4408
|
+
if ((_a2 = context.sessionInfo.current) == null ? void 0 : _a2.isReadOnly) {
|
|
3368
4409
|
throw new Error(
|
|
3369
4410
|
"Cannot write to storage with a read only user, please ensure the user has write permissions"
|
|
3370
4411
|
);
|
|
@@ -3372,81 +4413,55 @@ function createRoom(options, config) {
|
|
|
3372
4413
|
}
|
|
3373
4414
|
};
|
|
3374
4415
|
const eventHub = {
|
|
4416
|
+
connection: makeEventSource(),
|
|
4417
|
+
// Old/deprecated API
|
|
4418
|
+
status: makeEventSource(),
|
|
4419
|
+
// New/recommended API
|
|
4420
|
+
lostConnection: makeEventSource(),
|
|
3375
4421
|
customEvent: makeEventSource(),
|
|
3376
4422
|
me: makeEventSource(),
|
|
3377
4423
|
others: makeEventSource(),
|
|
3378
4424
|
error: makeEventSource(),
|
|
3379
|
-
connection: makeEventSource(),
|
|
3380
4425
|
storage: makeEventSource(),
|
|
3381
4426
|
history: makeEventSource(),
|
|
3382
4427
|
storageDidLoad: makeEventSource(),
|
|
3383
4428
|
storageStatus: makeEventSource()
|
|
3384
4429
|
};
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
void auth().then((token) => {
|
|
3394
|
-
if (context.connection.current.status !== "authenticating") {
|
|
3395
|
-
return;
|
|
3396
|
-
}
|
|
3397
|
-
const socket = createWebSocket(token);
|
|
3398
|
-
handleAuthSuccess(token.parsed, socket);
|
|
3399
|
-
context.token = token;
|
|
3400
|
-
}).catch(
|
|
3401
|
-
(er) => authenticationFailure(
|
|
3402
|
-
er instanceof Error ? er : new Error(String(er))
|
|
3403
|
-
)
|
|
3404
|
-
);
|
|
3405
|
-
return void 0;
|
|
3406
|
-
}
|
|
3407
|
-
},
|
|
3408
|
-
send(messageOrMessages) {
|
|
3409
|
-
var _a2, _b;
|
|
3410
|
-
if (context.socket === null) {
|
|
3411
|
-
throw new Error("Can't send message if socket is null");
|
|
3412
|
-
}
|
|
3413
|
-
if (context.socket.readyState === context.socket.OPEN) {
|
|
3414
|
-
const message = JSON.stringify(messageOrMessages);
|
|
3415
|
-
if (config.unstable_fallbackToHTTP) {
|
|
3416
|
-
const size = new TextEncoder().encode(message).length;
|
|
3417
|
-
if (size > MAX_MESSAGE_SIZE && ((_a2 = context.token) == null ? void 0 : _a2.raw) && config.httpSendEndpoint) {
|
|
3418
|
-
if (isTokenExpired(context.token.parsed)) {
|
|
3419
|
-
return reconnect();
|
|
3420
|
-
}
|
|
3421
|
-
void httpSend(
|
|
3422
|
-
message,
|
|
3423
|
-
context.token.raw,
|
|
3424
|
-
config.httpSendEndpoint,
|
|
3425
|
-
(_b = config.polyfills) == null ? void 0 : _b.fetch
|
|
3426
|
-
);
|
|
3427
|
-
warn(
|
|
3428
|
-
"Message was too large for websockets and sent over HTTP instead"
|
|
3429
|
-
);
|
|
3430
|
-
return;
|
|
3431
|
-
}
|
|
4430
|
+
function sendMessages(messageOrMessages) {
|
|
4431
|
+
var _a2, _b2;
|
|
4432
|
+
const message = JSON.stringify(messageOrMessages);
|
|
4433
|
+
if (config.unstable_fallbackToHTTP) {
|
|
4434
|
+
const size = new TextEncoder().encode(message).length;
|
|
4435
|
+
if (size > MAX_MESSAGE_SIZE && ((_a2 = managedSocket.token) == null ? void 0 : _a2.raw) && config.httpSendEndpoint) {
|
|
4436
|
+
if (isTokenExpired(managedSocket.token.parsed)) {
|
|
4437
|
+
return managedSocket.reconnect();
|
|
3432
4438
|
}
|
|
3433
|
-
|
|
4439
|
+
void httpSend(
|
|
4440
|
+
message,
|
|
4441
|
+
managedSocket.token.raw,
|
|
4442
|
+
config.httpSendEndpoint,
|
|
4443
|
+
(_b2 = config.polyfills) == null ? void 0 : _b2.fetch
|
|
4444
|
+
);
|
|
4445
|
+
warn(
|
|
4446
|
+
"Message was too large for websockets and sent over HTTP instead"
|
|
4447
|
+
);
|
|
4448
|
+
return;
|
|
3434
4449
|
}
|
|
3435
|
-
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
schedulePongTimeout: () => setTimeout(pongTimeout, PONG_TIMEOUT)
|
|
3439
|
-
};
|
|
4450
|
+
}
|
|
4451
|
+
managedSocket.send(message);
|
|
4452
|
+
}
|
|
3440
4453
|
const self = new DerivedRef(
|
|
3441
|
-
context.
|
|
4454
|
+
context.sessionInfo,
|
|
3442
4455
|
context.me,
|
|
3443
|
-
(
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
4456
|
+
(info, me) => {
|
|
4457
|
+
return info !== null ? {
|
|
4458
|
+
connectionId: info.id,
|
|
4459
|
+
id: info.userId,
|
|
4460
|
+
info: info.userInfo,
|
|
4461
|
+
presence: me,
|
|
4462
|
+
isReadOnly: info.isReadOnly
|
|
4463
|
+
} : null;
|
|
4464
|
+
}
|
|
3450
4465
|
);
|
|
3451
4466
|
const selfAsTreeNode = new DerivedRef(
|
|
3452
4467
|
self,
|
|
@@ -3515,11 +4530,9 @@ function createRoom(options, config) {
|
|
|
3515
4530
|
});
|
|
3516
4531
|
}
|
|
3517
4532
|
function getConnectionId() {
|
|
3518
|
-
const
|
|
3519
|
-
if (
|
|
3520
|
-
return
|
|
3521
|
-
} else if (context.lastConnectionId !== null) {
|
|
3522
|
-
return context.lastConnectionId;
|
|
4533
|
+
const info = context.sessionInfo.current;
|
|
4534
|
+
if (info) {
|
|
4535
|
+
return info.id;
|
|
3523
4536
|
}
|
|
3524
4537
|
throw new Error(
|
|
3525
4538
|
"Internal. Tried to get connection id but connection was never open"
|
|
@@ -3642,23 +4655,6 @@ function createRoom(options, config) {
|
|
|
3642
4655
|
}
|
|
3643
4656
|
}
|
|
3644
4657
|
}
|
|
3645
|
-
function handleConnect() {
|
|
3646
|
-
var _a2, _b;
|
|
3647
|
-
if (context.connection.current.status !== "closed" && context.connection.current.status !== "unavailable") {
|
|
3648
|
-
return;
|
|
3649
|
-
}
|
|
3650
|
-
const auth = prepareAuthEndpoint(
|
|
3651
|
-
config.roomId,
|
|
3652
|
-
config.authentication,
|
|
3653
|
-
(_a2 = config.polyfills) == null ? void 0 : _a2.fetch
|
|
3654
|
-
);
|
|
3655
|
-
const createWebSocket = prepareCreateWebSocket(
|
|
3656
|
-
config.liveblocksServer,
|
|
3657
|
-
(_b = config.polyfills) == null ? void 0 : _b.WebSocket
|
|
3658
|
-
);
|
|
3659
|
-
updateConnection({ status: "authenticating" }, batchUpdates);
|
|
3660
|
-
effects.authenticateAndConnect(auth, createWebSocket);
|
|
3661
|
-
}
|
|
3662
4658
|
function updatePresence(patch, options2) {
|
|
3663
4659
|
const oldValues = {};
|
|
3664
4660
|
if (context.buffer.me === null) {
|
|
@@ -3700,40 +4696,6 @@ function createRoom(options, config) {
|
|
|
3700
4696
|
function isStorageReadOnly(scopes) {
|
|
3701
4697
|
return scopes.includes("room:read" /* Read */) && scopes.includes("room:presence:write" /* PresenceWrite */) && !scopes.includes("room:write" /* Write */);
|
|
3702
4698
|
}
|
|
3703
|
-
function handleAuthSuccess(token, socket) {
|
|
3704
|
-
socket.addEventListener("message", handleRawSocketMessage);
|
|
3705
|
-
socket.addEventListener("open", handleSocketOpen);
|
|
3706
|
-
socket.addEventListener("close", handleExplicitClose);
|
|
3707
|
-
socket.addEventListener("error", handleSocketError);
|
|
3708
|
-
updateConnection(
|
|
3709
|
-
{
|
|
3710
|
-
status: "connecting",
|
|
3711
|
-
id: token.actor,
|
|
3712
|
-
userInfo: token.info,
|
|
3713
|
-
userId: token.id,
|
|
3714
|
-
isReadOnly: isStorageReadOnly(token.scopes)
|
|
3715
|
-
},
|
|
3716
|
-
batchUpdates
|
|
3717
|
-
);
|
|
3718
|
-
context.idFactory = makeIdFactory(token.actor);
|
|
3719
|
-
context.socket = socket;
|
|
3720
|
-
}
|
|
3721
|
-
function authenticationFailure(error2) {
|
|
3722
|
-
if (process.env.NODE_ENV !== "production") {
|
|
3723
|
-
error("Call to authentication endpoint failed", error2);
|
|
3724
|
-
}
|
|
3725
|
-
context.token = null;
|
|
3726
|
-
updateConnection({ status: "unavailable" }, batchUpdates);
|
|
3727
|
-
context.numRetries++;
|
|
3728
|
-
clearTimeout(context.timers.reconnect);
|
|
3729
|
-
context.timers.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
3730
|
-
}
|
|
3731
|
-
function handleWindowGotFocus() {
|
|
3732
|
-
if (context.connection.current.status === "open") {
|
|
3733
|
-
log("Heartbeat after visibility change");
|
|
3734
|
-
heartbeat();
|
|
3735
|
-
}
|
|
3736
|
-
}
|
|
3737
4699
|
function onUpdatePresenceMessage(message) {
|
|
3738
4700
|
if (message.targetActor !== void 0) {
|
|
3739
4701
|
const oldUser = context.others.getUser(message.actor);
|
|
@@ -3783,12 +4745,6 @@ function createRoom(options, config) {
|
|
|
3783
4745
|
}
|
|
3784
4746
|
return { type: "reset" };
|
|
3785
4747
|
}
|
|
3786
|
-
function handleNavigatorBackOnline() {
|
|
3787
|
-
if (context.connection.current.status === "unavailable") {
|
|
3788
|
-
log("Try to reconnect after connectivity change");
|
|
3789
|
-
reconnect();
|
|
3790
|
-
}
|
|
3791
|
-
}
|
|
3792
4748
|
function canUndo() {
|
|
3793
4749
|
return context.undoStack.length > 0;
|
|
3794
4750
|
}
|
|
@@ -3844,17 +4800,7 @@ function createRoom(options, config) {
|
|
|
3844
4800
|
ops: result.ops
|
|
3845
4801
|
});
|
|
3846
4802
|
notify(result.updates, batchedUpdatesWrapper);
|
|
3847
|
-
|
|
3848
|
-
}
|
|
3849
|
-
function handleRawSocketMessage(event) {
|
|
3850
|
-
if (event.data === "pong") {
|
|
3851
|
-
transition({ type: "RECEIVE_PONG" });
|
|
3852
|
-
} else {
|
|
3853
|
-
handleServerMessage(event);
|
|
3854
|
-
}
|
|
3855
|
-
}
|
|
3856
|
-
function handlePong() {
|
|
3857
|
-
clearTimeout(context.timers.pongTimeout);
|
|
4803
|
+
sendMessages(messages);
|
|
3858
4804
|
}
|
|
3859
4805
|
function handleServerMessage(event) {
|
|
3860
4806
|
if (typeof event.data !== "string") {
|
|
@@ -3908,8 +4854,8 @@ function createRoom(options, config) {
|
|
|
3908
4854
|
const unacknowledgedOps = new Map(context.unacknowledgedOps);
|
|
3909
4855
|
createOrUpdateRootFromMessage(message, doNotBatchUpdates);
|
|
3910
4856
|
applyAndSendOps(unacknowledgedOps, doNotBatchUpdates);
|
|
3911
|
-
if (
|
|
3912
|
-
|
|
4857
|
+
if (_resolveInitialStatePromise !== null) {
|
|
4858
|
+
_resolveInitialStatePromise();
|
|
3913
4859
|
}
|
|
3914
4860
|
notifyStorageStatus();
|
|
3915
4861
|
eventHub.storageDidLoad.notify();
|
|
@@ -3957,140 +4903,6 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
3957
4903
|
notify(updates, doNotBatchUpdates);
|
|
3958
4904
|
});
|
|
3959
4905
|
}
|
|
3960
|
-
function handleExplicitClose(event) {
|
|
3961
|
-
context.socket = null;
|
|
3962
|
-
clearTimeout(context.timers.flush);
|
|
3963
|
-
clearTimeout(context.timers.reconnect);
|
|
3964
|
-
clearInterval(context.timers.heartbeat);
|
|
3965
|
-
clearTimeout(context.timers.pongTimeout);
|
|
3966
|
-
context.others.clearOthers();
|
|
3967
|
-
batchUpdates(() => {
|
|
3968
|
-
notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
|
|
3969
|
-
if (event.code >= 4e3 && event.code <= 4100) {
|
|
3970
|
-
updateConnection({ status: "failed" }, doNotBatchUpdates);
|
|
3971
|
-
const error2 = new LiveblocksError(event.reason, event.code);
|
|
3972
|
-
eventHub.error.notify(error2);
|
|
3973
|
-
const delay = getRetryDelay(true);
|
|
3974
|
-
context.numRetries++;
|
|
3975
|
-
if (process.env.NODE_ENV !== "production") {
|
|
3976
|
-
error(
|
|
3977
|
-
`Connection to websocket server closed. Reason: ${error2.message} (code: ${error2.code}). Retrying in ${delay}ms.`
|
|
3978
|
-
);
|
|
3979
|
-
}
|
|
3980
|
-
updateConnection({ status: "unavailable" }, doNotBatchUpdates);
|
|
3981
|
-
clearTimeout(context.timers.reconnect);
|
|
3982
|
-
context.timers.reconnect = effects.scheduleReconnect(delay);
|
|
3983
|
-
} else if (event.code === 4999 /* CLOSE_WITHOUT_RETRY */) {
|
|
3984
|
-
updateConnection({ status: "closed" }, doNotBatchUpdates);
|
|
3985
|
-
} else {
|
|
3986
|
-
const delay = getRetryDelay();
|
|
3987
|
-
context.numRetries++;
|
|
3988
|
-
if (process.env.NODE_ENV !== "production") {
|
|
3989
|
-
warn(
|
|
3990
|
-
`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`
|
|
3991
|
-
);
|
|
3992
|
-
}
|
|
3993
|
-
updateConnection({ status: "unavailable" }, doNotBatchUpdates);
|
|
3994
|
-
clearTimeout(context.timers.reconnect);
|
|
3995
|
-
context.timers.reconnect = effects.scheduleReconnect(delay);
|
|
3996
|
-
}
|
|
3997
|
-
});
|
|
3998
|
-
}
|
|
3999
|
-
function updateConnection(connection, batchedUpdatesWrapper) {
|
|
4000
|
-
context.connection.set(connection);
|
|
4001
|
-
batchedUpdatesWrapper(() => {
|
|
4002
|
-
eventHub.connection.notify(connection.status);
|
|
4003
|
-
});
|
|
4004
|
-
}
|
|
4005
|
-
function getRetryDelay(slow = false) {
|
|
4006
|
-
if (slow) {
|
|
4007
|
-
return BACKOFF_RETRY_DELAYS_SLOW[context.numRetries < BACKOFF_RETRY_DELAYS_SLOW.length ? context.numRetries : BACKOFF_RETRY_DELAYS_SLOW.length - 1];
|
|
4008
|
-
}
|
|
4009
|
-
return BACKOFF_RETRY_DELAYS[context.numRetries < BACKOFF_RETRY_DELAYS.length ? context.numRetries : BACKOFF_RETRY_DELAYS.length - 1];
|
|
4010
|
-
}
|
|
4011
|
-
function handleSocketError() {
|
|
4012
|
-
}
|
|
4013
|
-
function handleSocketOpen() {
|
|
4014
|
-
clearInterval(context.timers.heartbeat);
|
|
4015
|
-
context.timers.heartbeat = effects.startHeartbeatInterval();
|
|
4016
|
-
if (context.connection.current.status === "connecting") {
|
|
4017
|
-
updateConnection(
|
|
4018
|
-
__spreadProps(__spreadValues({}, context.connection.current), { status: "open" }),
|
|
4019
|
-
batchUpdates
|
|
4020
|
-
);
|
|
4021
|
-
context.numRetries = 0;
|
|
4022
|
-
if (context.lastConnectionId !== void 0) {
|
|
4023
|
-
context.buffer.me = {
|
|
4024
|
-
type: "full",
|
|
4025
|
-
data: (
|
|
4026
|
-
// Because state.me.current is a readonly object, we'll have to
|
|
4027
|
-
// make a copy here. Otherwise, type errors happen later when
|
|
4028
|
-
// "patching" my presence.
|
|
4029
|
-
__spreadValues({}, context.me.current)
|
|
4030
|
-
)
|
|
4031
|
-
};
|
|
4032
|
-
tryFlushing();
|
|
4033
|
-
}
|
|
4034
|
-
context.lastConnectionId = context.connection.current.id;
|
|
4035
|
-
if (context.root) {
|
|
4036
|
-
context.buffer.messages.push({ type: 200 /* FETCH_STORAGE */ });
|
|
4037
|
-
}
|
|
4038
|
-
tryFlushing();
|
|
4039
|
-
} else {
|
|
4040
|
-
}
|
|
4041
|
-
}
|
|
4042
|
-
function heartbeat() {
|
|
4043
|
-
if (context.socket === null) {
|
|
4044
|
-
return;
|
|
4045
|
-
}
|
|
4046
|
-
clearTimeout(context.timers.pongTimeout);
|
|
4047
|
-
context.timers.pongTimeout = effects.schedulePongTimeout();
|
|
4048
|
-
if (context.socket.readyState === context.socket.OPEN) {
|
|
4049
|
-
context.socket.send("ping");
|
|
4050
|
-
}
|
|
4051
|
-
}
|
|
4052
|
-
function pongTimeout() {
|
|
4053
|
-
log("Pong timeout. Trying to reconnect.");
|
|
4054
|
-
reconnect();
|
|
4055
|
-
}
|
|
4056
|
-
function handleDisconnect() {
|
|
4057
|
-
if (context.socket) {
|
|
4058
|
-
context.socket.removeEventListener("open", handleSocketOpen);
|
|
4059
|
-
context.socket.removeEventListener("message", handleRawSocketMessage);
|
|
4060
|
-
context.socket.removeEventListener("close", handleExplicitClose);
|
|
4061
|
-
context.socket.removeEventListener("error", handleSocketError);
|
|
4062
|
-
context.socket.close();
|
|
4063
|
-
context.socket = null;
|
|
4064
|
-
}
|
|
4065
|
-
clearTimeout(context.timers.flush);
|
|
4066
|
-
clearTimeout(context.timers.reconnect);
|
|
4067
|
-
clearInterval(context.timers.heartbeat);
|
|
4068
|
-
clearTimeout(context.timers.pongTimeout);
|
|
4069
|
-
batchUpdates(() => {
|
|
4070
|
-
updateConnection({ status: "closed" }, doNotBatchUpdates);
|
|
4071
|
-
context.others.clearOthers();
|
|
4072
|
-
notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
|
|
4073
|
-
});
|
|
4074
|
-
for (const eventSource2 of Object.values(eventHub)) {
|
|
4075
|
-
eventSource2.clear();
|
|
4076
|
-
}
|
|
4077
|
-
}
|
|
4078
|
-
function reconnect() {
|
|
4079
|
-
if (context.socket) {
|
|
4080
|
-
context.socket.removeEventListener("open", handleSocketOpen);
|
|
4081
|
-
context.socket.removeEventListener("message", handleRawSocketMessage);
|
|
4082
|
-
context.socket.removeEventListener("close", handleExplicitClose);
|
|
4083
|
-
context.socket.removeEventListener("error", handleSocketError);
|
|
4084
|
-
context.socket.close();
|
|
4085
|
-
context.socket = null;
|
|
4086
|
-
}
|
|
4087
|
-
clearTimeout(context.timers.flush);
|
|
4088
|
-
clearTimeout(context.timers.reconnect);
|
|
4089
|
-
clearInterval(context.timers.heartbeat);
|
|
4090
|
-
clearTimeout(context.timers.pongTimeout);
|
|
4091
|
-
updateConnection({ status: "unavailable" }, batchUpdates);
|
|
4092
|
-
handleConnect();
|
|
4093
|
-
}
|
|
4094
4906
|
function tryFlushing() {
|
|
4095
4907
|
const storageOps = context.buffer.storageOperations;
|
|
4096
4908
|
if (storageOps.length > 0) {
|
|
@@ -4099,7 +4911,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4099
4911
|
}
|
|
4100
4912
|
notifyStorageStatus();
|
|
4101
4913
|
}
|
|
4102
|
-
if (
|
|
4914
|
+
if (managedSocket.getStatus() !== "connected") {
|
|
4103
4915
|
context.buffer.storageOperations = [];
|
|
4104
4916
|
return;
|
|
4105
4917
|
}
|
|
@@ -4110,16 +4922,17 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4110
4922
|
if (messagesToFlush.length === 0) {
|
|
4111
4923
|
return;
|
|
4112
4924
|
}
|
|
4113
|
-
|
|
4925
|
+
sendMessages(messagesToFlush);
|
|
4114
4926
|
context.buffer = {
|
|
4927
|
+
flushTimerID: void 0,
|
|
4115
4928
|
lastFlushedAt: now,
|
|
4116
4929
|
messages: [],
|
|
4117
4930
|
storageOperations: [],
|
|
4118
4931
|
me: null
|
|
4119
4932
|
};
|
|
4120
4933
|
} else {
|
|
4121
|
-
clearTimeout(context.
|
|
4122
|
-
context.
|
|
4934
|
+
clearTimeout(context.buffer.flushTimerID);
|
|
4935
|
+
context.buffer.flushTimerID = setTimeout(
|
|
4123
4936
|
tryFlushing,
|
|
4124
4937
|
config.throttleDelay - elapsedMillis
|
|
4125
4938
|
);
|
|
@@ -4156,7 +4969,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4156
4969
|
function broadcastEvent(event, options2 = {
|
|
4157
4970
|
shouldQueueEventIfNotReady: false
|
|
4158
4971
|
}) {
|
|
4159
|
-
if (
|
|
4972
|
+
if (managedSocket.getStatus() !== "connected" && !options2.shouldQueueEventIfNotReady) {
|
|
4160
4973
|
return;
|
|
4161
4974
|
}
|
|
4162
4975
|
context.buffer.messages.push({
|
|
@@ -4170,13 +4983,13 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4170
4983
|
tryFlushing();
|
|
4171
4984
|
}
|
|
4172
4985
|
let _getInitialStatePromise = null;
|
|
4173
|
-
let
|
|
4986
|
+
let _resolveInitialStatePromise = null;
|
|
4174
4987
|
function startLoadingStorage() {
|
|
4175
4988
|
if (_getInitialStatePromise === null) {
|
|
4176
4989
|
context.buffer.messages.push({ type: 200 /* FETCH_STORAGE */ });
|
|
4177
4990
|
tryFlushing();
|
|
4178
4991
|
_getInitialStatePromise = new Promise(
|
|
4179
|
-
(resolve) =>
|
|
4992
|
+
(resolve) => _resolveInitialStatePromise = resolve
|
|
4180
4993
|
);
|
|
4181
4994
|
notifyStorageStatus();
|
|
4182
4995
|
}
|
|
@@ -4293,11 +5106,6 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4293
5106
|
_addToRealUndoStack(historyOps, batchUpdates);
|
|
4294
5107
|
}
|
|
4295
5108
|
}
|
|
4296
|
-
function handleImplicitClose() {
|
|
4297
|
-
if (context.socket) {
|
|
4298
|
-
context.socket = null;
|
|
4299
|
-
}
|
|
4300
|
-
}
|
|
4301
5109
|
function getStorageStatus() {
|
|
4302
5110
|
if (_getInitialStatePromise === null) {
|
|
4303
5111
|
return "not-loaded";
|
|
@@ -4320,38 +5128,20 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4320
5128
|
(others) => others.map((other, index) => userToTreeNode(`Other ${index}`, other))
|
|
4321
5129
|
);
|
|
4322
5130
|
const events = {
|
|
5131
|
+
connection: eventHub.connection.observable,
|
|
5132
|
+
// Old/deprecated API
|
|
5133
|
+
status: eventHub.status.observable,
|
|
5134
|
+
// New/recommended API
|
|
5135
|
+
lostConnection: eventHub.lostConnection.observable,
|
|
4323
5136
|
customEvent: eventHub.customEvent.observable,
|
|
4324
5137
|
others: eventHub.others.observable,
|
|
4325
5138
|
me: eventHub.me.observable,
|
|
4326
5139
|
error: eventHub.error.observable,
|
|
4327
|
-
connection: eventHub.connection.observable,
|
|
4328
5140
|
storage: eventHub.storage.observable,
|
|
4329
5141
|
history: eventHub.history.observable,
|
|
4330
5142
|
storageDidLoad: eventHub.storageDidLoad.observable,
|
|
4331
5143
|
storageStatus: eventHub.storageStatus.observable
|
|
4332
5144
|
};
|
|
4333
|
-
function transition(event) {
|
|
4334
|
-
switch (event.type) {
|
|
4335
|
-
case "CONNECT":
|
|
4336
|
-
return handleConnect();
|
|
4337
|
-
case "DISCONNECT":
|
|
4338
|
-
return handleDisconnect();
|
|
4339
|
-
case "RECEIVE_PONG":
|
|
4340
|
-
return handlePong();
|
|
4341
|
-
case "AUTH_SUCCESS":
|
|
4342
|
-
return handleAuthSuccess(event.token, event.socket);
|
|
4343
|
-
case "WINDOW_GOT_FOCUS":
|
|
4344
|
-
return handleWindowGotFocus();
|
|
4345
|
-
case "NAVIGATOR_ONLINE":
|
|
4346
|
-
return handleNavigatorBackOnline();
|
|
4347
|
-
case "IMPLICIT_CLOSE":
|
|
4348
|
-
return handleImplicitClose();
|
|
4349
|
-
case "EXPLICIT_CLOSE":
|
|
4350
|
-
return handleExplicitClose(event.closeEvent);
|
|
4351
|
-
default:
|
|
4352
|
-
return assertNever(event, "Invalid event");
|
|
4353
|
-
}
|
|
4354
|
-
}
|
|
4355
5145
|
return {
|
|
4356
5146
|
/* NOTE: Exposing __internal here only to allow testing implementation details in unit tests */
|
|
4357
5147
|
__internal: {
|
|
@@ -4359,10 +5149,6 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4359
5149
|
return context.buffer;
|
|
4360
5150
|
},
|
|
4361
5151
|
// prettier-ignore
|
|
4362
|
-
get numRetries() {
|
|
4363
|
-
return context.numRetries;
|
|
4364
|
-
},
|
|
4365
|
-
// prettier-ignore
|
|
4366
5152
|
get undoStack() {
|
|
4367
5153
|
return context.undoStack;
|
|
4368
5154
|
},
|
|
@@ -4376,27 +5162,17 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4376
5162
|
getOthers_forDevTools: () => others_forDevTools.current,
|
|
4377
5163
|
// prettier-ignore
|
|
4378
5164
|
send: {
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
navigatorOnline: () => transition({ type: "NAVIGATOR_ONLINE" }),
|
|
4383
|
-
windowGotFocus: () => transition({ type: "WINDOW_GOT_FOCUS" }),
|
|
4384
|
-
pong: () => transition({ type: "RECEIVE_PONG" }),
|
|
4385
|
-
connect: () => transition({ type: "CONNECT" }),
|
|
4386
|
-
disconnect: () => transition({ type: "DISCONNECT" }),
|
|
4387
|
-
/**
|
|
4388
|
-
* This one looks differently from the rest, because receiving messages
|
|
4389
|
-
* is handled orthorgonally from all other possible events above,
|
|
4390
|
-
* because it does not matter what the connectivity state of the
|
|
4391
|
-
* machine is, so there won't be an explicit state machine transition
|
|
4392
|
-
* needed for this event.
|
|
4393
|
-
*/
|
|
4394
|
-
incomingMessage: handleServerMessage
|
|
5165
|
+
// These exist only for our E2E testing app
|
|
5166
|
+
explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
|
|
5167
|
+
implicitClose: () => managedSocket._privateSendMachineEvent({ type: "PONG_TIMEOUT" })
|
|
4395
5168
|
}
|
|
4396
5169
|
},
|
|
4397
5170
|
id: config.roomId,
|
|
4398
5171
|
subscribe: makeClassicSubscribeFn(events),
|
|
4399
|
-
|
|
5172
|
+
connect: () => managedSocket.connect(),
|
|
5173
|
+
reconnect: () => managedSocket.reconnect(),
|
|
5174
|
+
disconnect: () => managedSocket.disconnect(),
|
|
5175
|
+
destroy: () => managedSocket.destroy(),
|
|
4400
5176
|
// Presence
|
|
4401
5177
|
updatePresence,
|
|
4402
5178
|
broadcastEvent,
|
|
@@ -4415,8 +5191,9 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4415
5191
|
getStorageStatus,
|
|
4416
5192
|
events,
|
|
4417
5193
|
// Core
|
|
4418
|
-
|
|
4419
|
-
|
|
5194
|
+
getStatus: () => managedSocket.getStatus(),
|
|
5195
|
+
getConnectionState: () => managedSocket.getLegacyStatus(),
|
|
5196
|
+
isSelfAware: () => context.sessionInfo.current !== null,
|
|
4420
5197
|
getSelf: () => self.current,
|
|
4421
5198
|
// Presence
|
|
4422
5199
|
getPresence: () => context.me.current,
|
|
@@ -4468,6 +5245,12 @@ function makeClassicSubscribeFn(events) {
|
|
|
4468
5245
|
return events.connection.subscribe(
|
|
4469
5246
|
callback
|
|
4470
5247
|
);
|
|
5248
|
+
case "status":
|
|
5249
|
+
return events.status.subscribe(callback);
|
|
5250
|
+
case "lost-connection":
|
|
5251
|
+
return events.lostConnection.subscribe(
|
|
5252
|
+
callback
|
|
5253
|
+
);
|
|
4471
5254
|
case "history":
|
|
4472
5255
|
return events.history.subscribe(callback);
|
|
4473
5256
|
case "storage-status":
|
|
@@ -4503,20 +5286,14 @@ function makeClassicSubscribeFn(events) {
|
|
|
4503
5286
|
function isRoomEventName(value) {
|
|
4504
5287
|
return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "connection" || value === "history" || value === "storage-status";
|
|
4505
5288
|
}
|
|
4506
|
-
|
|
4507
|
-
constructor(message, code) {
|
|
4508
|
-
super(message);
|
|
4509
|
-
this.code = code;
|
|
4510
|
-
}
|
|
4511
|
-
};
|
|
4512
|
-
function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
|
|
4513
|
-
if (typeof window === "undefined" && WebSocketPolyfill === void 0) {
|
|
4514
|
-
throw new Error(
|
|
4515
|
-
"To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
|
|
4516
|
-
);
|
|
4517
|
-
}
|
|
4518
|
-
const ws = WebSocketPolyfill || WebSocket;
|
|
5289
|
+
function makeCreateSocketDelegateForRoom(liveblocksServer, WebSocketPolyfill) {
|
|
4519
5290
|
return (richToken) => {
|
|
5291
|
+
const ws = WebSocketPolyfill != null ? WebSocketPolyfill : typeof WebSocket === "undefined" ? void 0 : WebSocket;
|
|
5292
|
+
if (ws === void 0) {
|
|
5293
|
+
throw new StopRetrying(
|
|
5294
|
+
"To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
4520
5297
|
const token = richToken.raw;
|
|
4521
5298
|
return new ws(
|
|
4522
5299
|
`${liveblocksServer}/?token=${token}&version=${// prettier-ignore
|
|
@@ -4524,7 +5301,7 @@ function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
|
|
|
4524
5301
|
// @ts-ignore (__PACKAGE_VERSION__ will be injected by the build script)
|
|
4525
5302
|
true ? (
|
|
4526
5303
|
/* istanbul ignore next */
|
|
4527
|
-
"1.0
|
|
5304
|
+
"1.1.0-beta2"
|
|
4528
5305
|
) : "dev"}`
|
|
4529
5306
|
);
|
|
4530
5307
|
};
|
|
@@ -4543,45 +5320,44 @@ function httpSend(message, token, endpoint, fetchPolyfill) {
|
|
|
4543
5320
|
});
|
|
4544
5321
|
});
|
|
4545
5322
|
}
|
|
4546
|
-
function
|
|
5323
|
+
function makeAuthDelegateForRoom(roomId, authentication, fetchPolyfill) {
|
|
5324
|
+
const fetcher = fetchPolyfill != null ? fetchPolyfill : typeof window === "undefined" ? void 0 : window.fetch;
|
|
4547
5325
|
if (authentication.type === "public") {
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
fetch,
|
|
4556
|
-
authentication.url,
|
|
4557
|
-
{
|
|
5326
|
+
return () => __async(this, null, function* () {
|
|
5327
|
+
if (fetcher === void 0) {
|
|
5328
|
+
throw new StopRetrying(
|
|
5329
|
+
"To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill."
|
|
5330
|
+
);
|
|
5331
|
+
}
|
|
5332
|
+
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
4558
5333
|
room: roomId,
|
|
4559
5334
|
publicApiKey: authentication.publicApiKey
|
|
5335
|
+
}).then(({ token }) => parseRoomAuthToken(token));
|
|
5336
|
+
});
|
|
5337
|
+
} else if (authentication.type === "private") {
|
|
5338
|
+
return () => __async(this, null, function* () {
|
|
5339
|
+
if (fetcher === void 0) {
|
|
5340
|
+
throw new StopRetrying(
|
|
5341
|
+
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
5342
|
+
);
|
|
4560
5343
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
4567
|
-
);
|
|
4568
|
-
}
|
|
4569
|
-
return () => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
4570
|
-
room: roomId
|
|
4571
|
-
}).then(({ token }) => parseRoomAuthToken(token));
|
|
4572
|
-
}
|
|
4573
|
-
if (authentication.type === "custom") {
|
|
5344
|
+
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
5345
|
+
room: roomId
|
|
5346
|
+
}).then(({ token }) => parseRoomAuthToken(token));
|
|
5347
|
+
});
|
|
5348
|
+
} else if (authentication.type === "custom") {
|
|
4574
5349
|
return () => __async(this, null, function* () {
|
|
4575
5350
|
const response = yield authentication.callback(roomId);
|
|
4576
5351
|
if (!response || !response.token) {
|
|
4577
5352
|
throw new Error(
|
|
4578
|
-
'
|
|
5353
|
+
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
4579
5354
|
);
|
|
4580
5355
|
}
|
|
4581
5356
|
return parseRoomAuthToken(response.token);
|
|
4582
5357
|
});
|
|
5358
|
+
} else {
|
|
5359
|
+
throw new Error("Internal error. Unexpected authentication type");
|
|
4583
5360
|
}
|
|
4584
|
-
throw new Error("Internal error. Unexpected authentication type");
|
|
4585
5361
|
}
|
|
4586
5362
|
function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
4587
5363
|
return __async(this, null, function* () {
|
|
@@ -4595,22 +5371,25 @@ function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
|
4595
5371
|
body: JSON.stringify(body)
|
|
4596
5372
|
});
|
|
4597
5373
|
if (!res.ok) {
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
5374
|
+
const reason = `${(yield res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
|
|
5375
|
+
if (res.status === 401 || res.status === 403) {
|
|
5376
|
+
throw new StopRetrying(`Unauthorized: ${reason}`);
|
|
5377
|
+
} else {
|
|
5378
|
+
throw new Error(`Failed to authenticate: ${reason}`);
|
|
5379
|
+
}
|
|
4601
5380
|
}
|
|
4602
5381
|
let data;
|
|
4603
5382
|
try {
|
|
4604
5383
|
data = yield res.json();
|
|
4605
5384
|
} catch (er) {
|
|
4606
|
-
throw new
|
|
5385
|
+
throw new Error(
|
|
4607
5386
|
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
|
|
4608
5387
|
er
|
|
4609
5388
|
)}`
|
|
4610
5389
|
);
|
|
4611
5390
|
}
|
|
4612
5391
|
if (!isPlainObject(data) || typeof data.token !== "string") {
|
|
4613
|
-
throw new
|
|
5392
|
+
throw new Error(
|
|
4614
5393
|
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
4615
5394
|
data
|
|
4616
5395
|
)}`
|
|
@@ -4620,11 +5399,6 @@ function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
|
4620
5399
|
return { token };
|
|
4621
5400
|
});
|
|
4622
5401
|
}
|
|
4623
|
-
var AuthenticationError = class extends Error {
|
|
4624
|
-
constructor(message) {
|
|
4625
|
-
super(message);
|
|
4626
|
-
}
|
|
4627
|
-
};
|
|
4628
5402
|
|
|
4629
5403
|
// src/client.ts
|
|
4630
5404
|
var MIN_THROTTLE = 16;
|
|
@@ -4643,7 +5417,7 @@ function createClient(options) {
|
|
|
4643
5417
|
return room ? room : null;
|
|
4644
5418
|
}
|
|
4645
5419
|
function enter(roomId, options2) {
|
|
4646
|
-
var _a, _b, _c;
|
|
5420
|
+
var _a, _b, _c, _d;
|
|
4647
5421
|
const existingRoom = rooms.get(roomId);
|
|
4648
5422
|
if (existingRoom !== void 0) {
|
|
4649
5423
|
return existingRoom;
|
|
@@ -4660,7 +5434,10 @@ function createClient(options) {
|
|
|
4660
5434
|
{
|
|
4661
5435
|
roomId,
|
|
4662
5436
|
throttleDelay,
|
|
5437
|
+
lostConnectionTimeout: (_b = clientOptions.lostConnectionTimeout) != null ? _b : 5e3,
|
|
4663
5438
|
polyfills: clientOptions.polyfills,
|
|
5439
|
+
delegates: clientOptions.mockedDelegates,
|
|
5440
|
+
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
4664
5441
|
unstable_batchedUpdates: options2 == null ? void 0 : options2.unstable_batchedUpdates,
|
|
4665
5442
|
liveblocksServer: getServerFromClientOptions(clientOptions),
|
|
4666
5443
|
authentication: prepareAuthentication(clientOptions, roomId),
|
|
@@ -4674,17 +5451,17 @@ function createClient(options) {
|
|
|
4674
5451
|
rooms.set(roomId, newRoom);
|
|
4675
5452
|
setupDevTools(() => Array.from(rooms.keys()));
|
|
4676
5453
|
linkDevTools(roomId, newRoom);
|
|
4677
|
-
const shouldConnect = (
|
|
5454
|
+
const shouldConnect = (_c = options2.shouldInitiallyConnect) != null ? _c : true;
|
|
4678
5455
|
if (shouldConnect) {
|
|
4679
5456
|
if (typeof atob === "undefined") {
|
|
4680
|
-
if (((
|
|
5457
|
+
if (((_d = clientOptions.polyfills) == null ? void 0 : _d.atob) === void 0) {
|
|
4681
5458
|
throw new Error(
|
|
4682
5459
|
"You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
|
|
4683
5460
|
);
|
|
4684
5461
|
}
|
|
4685
5462
|
global.atob = clientOptions.polyfills.atob;
|
|
4686
5463
|
}
|
|
4687
|
-
newRoom.
|
|
5464
|
+
newRoom.connect();
|
|
4688
5465
|
}
|
|
4689
5466
|
return newRoom;
|
|
4690
5467
|
}
|
|
@@ -4692,27 +5469,10 @@ function createClient(options) {
|
|
|
4692
5469
|
unlinkDevTools(roomId);
|
|
4693
5470
|
const room = rooms.get(roomId);
|
|
4694
5471
|
if (room !== void 0) {
|
|
4695
|
-
room.
|
|
5472
|
+
room.destroy();
|
|
4696
5473
|
rooms.delete(roomId);
|
|
4697
5474
|
}
|
|
4698
5475
|
}
|
|
4699
|
-
if (typeof window !== "undefined" && // istanbul ignore next: React Native environment doesn't implement window.addEventListener
|
|
4700
|
-
typeof window.addEventListener !== "undefined") {
|
|
4701
|
-
window.addEventListener("online", () => {
|
|
4702
|
-
for (const [, room] of rooms) {
|
|
4703
|
-
room.__internal.send.navigatorOnline();
|
|
4704
|
-
}
|
|
4705
|
-
});
|
|
4706
|
-
}
|
|
4707
|
-
if (typeof document !== "undefined") {
|
|
4708
|
-
document.addEventListener("visibilitychange", () => {
|
|
4709
|
-
if (document.visibilityState === "visible") {
|
|
4710
|
-
for (const [, room] of rooms) {
|
|
4711
|
-
room.__internal.send.windowGotFocus();
|
|
4712
|
-
}
|
|
4713
|
-
}
|
|
4714
|
-
});
|
|
4715
|
-
}
|
|
4716
5476
|
return {
|
|
4717
5477
|
getRoom,
|
|
4718
5478
|
enter,
|
|
@@ -5131,6 +5891,20 @@ function shallow(a, b) {
|
|
|
5131
5891
|
return shallowObj(a, b);
|
|
5132
5892
|
}
|
|
5133
5893
|
|
|
5894
|
+
// src/types/IWebSocket.ts
|
|
5895
|
+
var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
|
|
5896
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
5897
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["INVALID_MESSAGE_FORMAT"] = 4e3] = "INVALID_MESSAGE_FORMAT";
|
|
5898
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
5899
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
5900
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
5901
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
5902
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
5903
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
5904
|
+
return WebsocketCloseCodes2;
|
|
5905
|
+
})(WebsocketCloseCodes || {});
|
|
5906
|
+
|
|
5907
|
+
|
|
5134
5908
|
|
|
5135
5909
|
|
|
5136
5910
|
|
|
@@ -5166,4 +5940,4 @@ function shallow(a, b) {
|
|
|
5166
5940
|
|
|
5167
5941
|
|
|
5168
5942
|
|
|
5169
|
-
exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.OpCode = OpCode; exports.ServerMsgCode = ServerMsgCode; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asArrayWithLegacyMethods = asArrayWithLegacyMethods; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.b64decode = b64decode; exports.createClient = createClient; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.errorIf = errorIf; exports.freeze = freeze; exports.isAppOnlyAuthToken = isAppOnlyAuthToken; exports.isAuthToken = isAuthToken; exports.isChildCrdt = isChildCrdt; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isPlainObject = isPlainObject; exports.isRoomAuthToken = isRoomAuthToken; exports.isRootCrdt = isRootCrdt; exports.legacy_patchImmutableObject = legacy_patchImmutableObject; exports.lsonToJson = lsonToJson; exports.makePosition = makePosition; exports.nn = nn; exports.patchLiveObjectKey = patchLiveObjectKey; exports.shallow = shallow; exports.throwUsageError = throwUsageError; exports.tryParseJson = tryParseJson;
|
|
5943
|
+
exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.OpCode = OpCode; exports.ServerMsgCode = ServerMsgCode; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.asArrayWithLegacyMethods = asArrayWithLegacyMethods; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.b64decode = b64decode; exports.createClient = createClient; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.errorIf = errorIf; exports.freeze = freeze; exports.isAppOnlyAuthToken = isAppOnlyAuthToken; exports.isAuthToken = isAuthToken; exports.isChildCrdt = isChildCrdt; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isPlainObject = isPlainObject; exports.isRoomAuthToken = isRoomAuthToken; exports.isRootCrdt = isRootCrdt; exports.legacy_patchImmutableObject = legacy_patchImmutableObject; exports.lsonToJson = lsonToJson; exports.makePosition = makePosition; exports.nn = nn; exports.patchLiveObjectKey = patchLiveObjectKey; exports.shallow = shallow; exports.throwUsageError = throwUsageError; exports.tryParseJson = tryParseJson; exports.withTimeout = withTimeout;
|