@liveblocks/core 2.15.1 → 2.16.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +434 -308
- package/dist/index.d.ts +434 -308
- package/dist/index.js +973 -807
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +875 -709
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/version.ts
|
|
8
8
|
var PKG_NAME = "@liveblocks/core";
|
|
9
|
-
var PKG_VERSION = "2.
|
|
9
|
+
var PKG_VERSION = "2.16.0-rc1";
|
|
10
10
|
var PKG_FORMAT = "esm";
|
|
11
11
|
|
|
12
12
|
// src/dupe-detection.ts
|
|
@@ -166,13 +166,18 @@ function wrapWithTitle(method) {
|
|
|
166
166
|
var warnWithTitle = wrapWithTitle("warn");
|
|
167
167
|
var errorWithTitle = wrapWithTitle("error");
|
|
168
168
|
|
|
169
|
+
// src/lib/guards.ts
|
|
170
|
+
function isPlainObject(blob) {
|
|
171
|
+
return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
|
|
172
|
+
}
|
|
173
|
+
function isStartsWithOperator(blob) {
|
|
174
|
+
return isPlainObject(blob) && typeof blob.startsWith === "string";
|
|
175
|
+
}
|
|
176
|
+
|
|
169
177
|
// src/lib/utils.ts
|
|
170
178
|
function raise(msg) {
|
|
171
179
|
throw new Error(msg);
|
|
172
180
|
}
|
|
173
|
-
function isPlainObject(blob) {
|
|
174
|
-
return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
|
|
175
|
-
}
|
|
176
181
|
function entries(obj) {
|
|
177
182
|
return Object.entries(obj);
|
|
178
183
|
}
|
|
@@ -254,13 +259,48 @@ function memoizeOnSuccess(factoryFn) {
|
|
|
254
259
|
}
|
|
255
260
|
|
|
256
261
|
// src/lib/autoRetry.ts
|
|
257
|
-
var HttpError = class extends Error {
|
|
258
|
-
|
|
262
|
+
var HttpError = class _HttpError extends Error {
|
|
263
|
+
response;
|
|
264
|
+
details;
|
|
265
|
+
constructor(message, response, details) {
|
|
259
266
|
super(message);
|
|
260
|
-
this.
|
|
261
|
-
this.
|
|
267
|
+
this.name = "HttpError";
|
|
268
|
+
this.response = response;
|
|
262
269
|
this.details = details;
|
|
263
270
|
}
|
|
271
|
+
static async fromResponse(response) {
|
|
272
|
+
let bodyAsText;
|
|
273
|
+
try {
|
|
274
|
+
bodyAsText = await response.text();
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
277
|
+
const bodyAsJson = bodyAsText ? tryParseJson(bodyAsText) : void 0;
|
|
278
|
+
let bodyAsJsonObject;
|
|
279
|
+
if (isPlainObject(bodyAsJson)) {
|
|
280
|
+
bodyAsJsonObject = bodyAsJson;
|
|
281
|
+
}
|
|
282
|
+
let message = "";
|
|
283
|
+
message ||= typeof bodyAsJsonObject?.message === "string" ? bodyAsJsonObject.message : "";
|
|
284
|
+
message ||= typeof bodyAsJsonObject?.error === "string" ? bodyAsJsonObject.error : "";
|
|
285
|
+
if (bodyAsJson === void 0) {
|
|
286
|
+
message ||= bodyAsText || "";
|
|
287
|
+
}
|
|
288
|
+
message ||= response.statusText;
|
|
289
|
+
let path;
|
|
290
|
+
try {
|
|
291
|
+
path = new URL(response.url).pathname;
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
message += path !== void 0 ? ` (got status ${response.status} from ${path})` : ` (got status ${response.status})`;
|
|
295
|
+
const details = bodyAsJsonObject;
|
|
296
|
+
return new _HttpError(message, response, details);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Convenience accessor for response.status.
|
|
300
|
+
*/
|
|
301
|
+
get status() {
|
|
302
|
+
return this.response.status;
|
|
303
|
+
}
|
|
264
304
|
};
|
|
265
305
|
var DONT_RETRY_4XX = (x) => x instanceof HttpError && x.status >= 400 && x.status < 500;
|
|
266
306
|
async function autoRetry(promiseFn, maxTries, backoff, shouldStopRetrying = DONT_RETRY_4XX) {
|
|
@@ -326,7 +366,12 @@ function makeEventSource() {
|
|
|
326
366
|
}).finally(() => unsub?.());
|
|
327
367
|
}
|
|
328
368
|
function notify(event) {
|
|
329
|
-
|
|
369
|
+
let called = false;
|
|
370
|
+
for (const callback of _observers) {
|
|
371
|
+
callback(event);
|
|
372
|
+
called = true;
|
|
373
|
+
}
|
|
374
|
+
return called;
|
|
330
375
|
}
|
|
331
376
|
function count() {
|
|
332
377
|
return _observers.size;
|
|
@@ -367,8 +412,9 @@ function makeBufferableEventSource() {
|
|
|
367
412
|
function notifyOrBuffer(event) {
|
|
368
413
|
if (_buffer !== null) {
|
|
369
414
|
_buffer.push(event);
|
|
415
|
+
return false;
|
|
370
416
|
} else {
|
|
371
|
-
eventSource2.notify(event);
|
|
417
|
+
return eventSource2.notify(event);
|
|
372
418
|
}
|
|
373
419
|
}
|
|
374
420
|
return {
|
|
@@ -385,164 +431,448 @@ function makeBufferableEventSource() {
|
|
|
385
431
|
};
|
|
386
432
|
}
|
|
387
433
|
|
|
388
|
-
// src/lib/
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const sortedObject = Object.keys(object).sort().reduce(
|
|
394
|
-
(sortedObject2, key) => {
|
|
395
|
-
sortedObject2[key] = object[key];
|
|
396
|
-
return sortedObject2;
|
|
397
|
-
},
|
|
398
|
-
{}
|
|
399
|
-
);
|
|
400
|
-
return JSON.stringify(sortedObject, ...args);
|
|
401
|
-
}
|
|
434
|
+
// src/lib/freeze.ts
|
|
435
|
+
var freeze = process.env.NODE_ENV === "production" ? (
|
|
436
|
+
/* istanbul ignore next */
|
|
437
|
+
(x) => x
|
|
438
|
+
) : Object.freeze;
|
|
402
439
|
|
|
403
|
-
// src/lib/
|
|
404
|
-
var
|
|
405
|
-
var
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const { promise, resolve, reject } = Promise_withResolvers();
|
|
413
|
-
this.promise = promise;
|
|
414
|
-
this.resolve = resolve;
|
|
415
|
-
this.reject = reject;
|
|
440
|
+
// src/lib/signals.ts
|
|
441
|
+
var kSinks = Symbol("kSinks");
|
|
442
|
+
var kTrigger = Symbol("kTrigger");
|
|
443
|
+
var signalsToTrigger = null;
|
|
444
|
+
var trackedReads = null;
|
|
445
|
+
function batch(callback) {
|
|
446
|
+
if (signalsToTrigger !== null) {
|
|
447
|
+
callback();
|
|
448
|
+
return;
|
|
416
449
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
constructor(callback, options) {
|
|
426
|
-
this.#callback = callback;
|
|
427
|
-
this.#size = options.size ?? DEFAULT_SIZE;
|
|
428
|
-
this.#delay = options.delay;
|
|
450
|
+
signalsToTrigger = /* @__PURE__ */ new Set();
|
|
451
|
+
try {
|
|
452
|
+
callback();
|
|
453
|
+
} finally {
|
|
454
|
+
for (const signal of signalsToTrigger) {
|
|
455
|
+
signal[kTrigger]();
|
|
456
|
+
}
|
|
457
|
+
signalsToTrigger = null;
|
|
429
458
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
459
|
+
}
|
|
460
|
+
function enqueueTrigger(signal) {
|
|
461
|
+
if (!signalsToTrigger) raise("Expected to be in an active batch");
|
|
462
|
+
signalsToTrigger.add(signal);
|
|
463
|
+
}
|
|
464
|
+
function merge(target, patch) {
|
|
465
|
+
let updated = false;
|
|
466
|
+
const newValue = { ...target };
|
|
467
|
+
Object.keys(patch).forEach((k) => {
|
|
468
|
+
const key = k;
|
|
469
|
+
const val = patch[key];
|
|
470
|
+
if (newValue[key] !== val) {
|
|
471
|
+
if (val === void 0) {
|
|
472
|
+
delete newValue[key];
|
|
473
|
+
} else {
|
|
474
|
+
newValue[key] = val;
|
|
475
|
+
}
|
|
476
|
+
updated = true;
|
|
434
477
|
}
|
|
478
|
+
});
|
|
479
|
+
return updated ? newValue : target;
|
|
480
|
+
}
|
|
481
|
+
var AbstractSignal = class {
|
|
482
|
+
/** @internal */
|
|
483
|
+
equals;
|
|
484
|
+
#eventSource;
|
|
485
|
+
/** @internal */
|
|
486
|
+
[kSinks];
|
|
487
|
+
constructor(equals) {
|
|
488
|
+
this.equals = equals ?? Object.is;
|
|
489
|
+
this.#eventSource = makeEventSource();
|
|
490
|
+
this[kSinks] = /* @__PURE__ */ new Set();
|
|
491
|
+
this.get = this.get.bind(this);
|
|
492
|
+
this.subscribe = this.subscribe.bind(this);
|
|
493
|
+
this.subscribeOnce = this.subscribeOnce.bind(this);
|
|
435
494
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
495
|
+
[Symbol.dispose]() {
|
|
496
|
+
this.#eventSource[Symbol.dispose]();
|
|
497
|
+
this.#eventSource = "(disposed)";
|
|
498
|
+
this.equals = "(disposed)";
|
|
499
|
+
}
|
|
500
|
+
get hasWatchers() {
|
|
501
|
+
if (this.#eventSource.count() > 0) return true;
|
|
502
|
+
for (const sink of this[kSinks]) {
|
|
503
|
+
if (sink.hasWatchers) {
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
442
506
|
}
|
|
507
|
+
return false;
|
|
443
508
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
509
|
+
[kTrigger]() {
|
|
510
|
+
this.#eventSource.notify();
|
|
511
|
+
for (const sink of this[kSinks]) {
|
|
512
|
+
enqueueTrigger(sink);
|
|
447
513
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
this.error = false;
|
|
453
|
-
calls.forEach((call, index) => {
|
|
454
|
-
const result = results?.[index];
|
|
455
|
-
if (!Array.isArray(results)) {
|
|
456
|
-
call.reject(new Error("Callback must return an array."));
|
|
457
|
-
} else if (calls.length !== results.length) {
|
|
458
|
-
call.reject(
|
|
459
|
-
new Error(
|
|
460
|
-
`Callback must return an array of the same length as the number of provided items. Expected ${calls.length}, but got ${results.length}.`
|
|
461
|
-
)
|
|
462
|
-
);
|
|
463
|
-
} else if (result instanceof Error) {
|
|
464
|
-
call.reject(result);
|
|
465
|
-
} else {
|
|
466
|
-
call.resolve(result);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
} catch (error3) {
|
|
470
|
-
this.error = true;
|
|
471
|
-
calls.forEach((call) => {
|
|
472
|
-
call.reject(error3);
|
|
473
|
-
});
|
|
514
|
+
}
|
|
515
|
+
subscribe(callback) {
|
|
516
|
+
if (this.#eventSource.count() === 0) {
|
|
517
|
+
this.get();
|
|
474
518
|
}
|
|
519
|
+
return this.#eventSource.subscribe(callback);
|
|
475
520
|
}
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
521
|
+
subscribeOnce(callback) {
|
|
522
|
+
const unsub = this.subscribe(() => {
|
|
523
|
+
unsub();
|
|
524
|
+
return callback();
|
|
525
|
+
});
|
|
526
|
+
return unsub;
|
|
527
|
+
}
|
|
528
|
+
waitUntil() {
|
|
529
|
+
throw new Error("waitUntil not supported on Signals");
|
|
530
|
+
}
|
|
531
|
+
markSinksDirty() {
|
|
532
|
+
for (const sink of this[kSinks]) {
|
|
533
|
+
sink.markDirty();
|
|
482
534
|
}
|
|
483
|
-
const call = new BatchCall(input);
|
|
484
|
-
this.#queue.push(call);
|
|
485
|
-
this.#schedule();
|
|
486
|
-
return call.promise;
|
|
487
535
|
}
|
|
488
|
-
|
|
489
|
-
this
|
|
490
|
-
|
|
491
|
-
|
|
536
|
+
addSink(sink) {
|
|
537
|
+
this[kSinks].add(sink);
|
|
538
|
+
}
|
|
539
|
+
removeSink(sink) {
|
|
540
|
+
this[kSinks].delete(sink);
|
|
541
|
+
}
|
|
542
|
+
asReadonly() {
|
|
543
|
+
return this;
|
|
492
544
|
}
|
|
493
545
|
};
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
546
|
+
var Signal = class extends AbstractSignal {
|
|
547
|
+
#value;
|
|
548
|
+
constructor(value, equals) {
|
|
549
|
+
super(equals);
|
|
550
|
+
this.#value = freeze(value);
|
|
499
551
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
552
|
+
[Symbol.dispose]() {
|
|
553
|
+
super[Symbol.dispose]();
|
|
554
|
+
this.#value = "(disposed)";
|
|
503
555
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
cache.delete(getCacheKey(input));
|
|
508
|
-
}
|
|
509
|
-
} else {
|
|
510
|
-
cache.clear();
|
|
511
|
-
}
|
|
512
|
-
eventSource2.notify();
|
|
556
|
+
get() {
|
|
557
|
+
trackedReads?.add(this);
|
|
558
|
+
return this.#value;
|
|
513
559
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
isLoading: false,
|
|
526
|
-
error: error3
|
|
527
|
-
});
|
|
528
|
-
}
|
|
560
|
+
set(newValue) {
|
|
561
|
+
batch(() => {
|
|
562
|
+
if (typeof newValue === "function") {
|
|
563
|
+
newValue = newValue(this.#value);
|
|
564
|
+
}
|
|
565
|
+
if (!this.equals(this.#value, newValue)) {
|
|
566
|
+
this.#value = freeze(newValue);
|
|
567
|
+
this.markSinksDirty();
|
|
568
|
+
enqueueTrigger(this);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
529
571
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
572
|
+
};
|
|
573
|
+
var PatchableSignal = class extends Signal {
|
|
574
|
+
constructor(data) {
|
|
575
|
+
super(freeze(compactObject(data)));
|
|
533
576
|
}
|
|
534
|
-
|
|
535
|
-
|
|
577
|
+
set() {
|
|
578
|
+
throw new Error("Don't call .set() directly, use .patch()");
|
|
536
579
|
}
|
|
537
|
-
|
|
538
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Patches the current object.
|
|
582
|
+
*/
|
|
583
|
+
patch(patch) {
|
|
584
|
+
super.set((old) => merge(old, patch));
|
|
539
585
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
586
|
+
};
|
|
587
|
+
var INITIAL = Symbol();
|
|
588
|
+
var DerivedSignal = class _DerivedSignal extends AbstractSignal {
|
|
589
|
+
#prevValue;
|
|
590
|
+
#dirty;
|
|
591
|
+
// When true, the value in #value may not be up-to-date and needs re-checking
|
|
592
|
+
#sources;
|
|
593
|
+
#deps;
|
|
594
|
+
#transform;
|
|
595
|
+
// prettier-ignore
|
|
596
|
+
static from(...args) {
|
|
597
|
+
const last = args.pop();
|
|
598
|
+
if (typeof last !== "function")
|
|
599
|
+
raise("Invalid .from() call, last argument expected to be a function");
|
|
600
|
+
if (typeof args[args.length - 1] === "function") {
|
|
601
|
+
const equals = last;
|
|
602
|
+
const transform = args.pop();
|
|
603
|
+
return new _DerivedSignal(args, transform, equals);
|
|
604
|
+
} else {
|
|
605
|
+
const transform = last;
|
|
606
|
+
return new _DerivedSignal(args, transform);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
constructor(deps, transform, equals) {
|
|
610
|
+
super(equals);
|
|
611
|
+
this.#dirty = true;
|
|
612
|
+
this.#prevValue = INITIAL;
|
|
613
|
+
this.#deps = deps;
|
|
614
|
+
this.#sources = /* @__PURE__ */ new Set();
|
|
615
|
+
this.#transform = transform;
|
|
616
|
+
}
|
|
617
|
+
[Symbol.dispose]() {
|
|
618
|
+
for (const src of this.#sources) {
|
|
619
|
+
src.removeSink(this);
|
|
620
|
+
}
|
|
621
|
+
this.#prevValue = "(disposed)";
|
|
622
|
+
this.#sources = "(disposed)";
|
|
623
|
+
this.#deps = "(disposed)";
|
|
624
|
+
this.#transform = "(disposed)";
|
|
625
|
+
}
|
|
626
|
+
get isDirty() {
|
|
627
|
+
return this.#dirty;
|
|
628
|
+
}
|
|
629
|
+
#recompute() {
|
|
630
|
+
const oldTrackedReads = trackedReads;
|
|
631
|
+
let derived;
|
|
632
|
+
trackedReads = /* @__PURE__ */ new Set();
|
|
633
|
+
try {
|
|
634
|
+
derived = this.#transform(...this.#deps.map((p) => p.get()));
|
|
635
|
+
} finally {
|
|
636
|
+
const oldSources = this.#sources;
|
|
637
|
+
this.#sources = /* @__PURE__ */ new Set();
|
|
638
|
+
for (const sig of trackedReads) {
|
|
639
|
+
this.#sources.add(sig);
|
|
640
|
+
oldSources.delete(sig);
|
|
641
|
+
}
|
|
642
|
+
for (const oldSource of oldSources) {
|
|
643
|
+
oldSource.removeSink(this);
|
|
644
|
+
}
|
|
645
|
+
for (const newSource of this.#sources) {
|
|
646
|
+
newSource.addSink(this);
|
|
647
|
+
}
|
|
648
|
+
trackedReads = oldTrackedReads;
|
|
649
|
+
}
|
|
650
|
+
this.#dirty = false;
|
|
651
|
+
if (!this.equals(this.#prevValue, derived)) {
|
|
652
|
+
this.#prevValue = derived;
|
|
653
|
+
return true;
|
|
654
|
+
}
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
markDirty() {
|
|
658
|
+
if (!this.#dirty) {
|
|
659
|
+
this.#dirty = true;
|
|
660
|
+
this.markSinksDirty();
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
get() {
|
|
664
|
+
if (this.#dirty) {
|
|
665
|
+
this.#recompute();
|
|
666
|
+
}
|
|
667
|
+
trackedReads?.add(this);
|
|
668
|
+
return this.#prevValue;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Called by the Signal system if one or more of the dependent signals have
|
|
672
|
+
* changed. In the case of a DerivedSignal, we'll only want to re-evaluate
|
|
673
|
+
* the actual value if it's being watched, or any of their sinks are being
|
|
674
|
+
* watched actively.
|
|
675
|
+
*/
|
|
676
|
+
[kTrigger]() {
|
|
677
|
+
if (!this.hasWatchers) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const updated = this.#recompute();
|
|
681
|
+
if (updated) {
|
|
682
|
+
super[kTrigger]();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
var MutableSignal = class extends AbstractSignal {
|
|
687
|
+
#state;
|
|
688
|
+
constructor(initialState) {
|
|
689
|
+
super();
|
|
690
|
+
this.#state = initialState;
|
|
691
|
+
}
|
|
692
|
+
[Symbol.dispose]() {
|
|
693
|
+
super[Symbol.dispose]();
|
|
694
|
+
this.#state = "(disposed)";
|
|
695
|
+
}
|
|
696
|
+
get() {
|
|
697
|
+
trackedReads?.add(this);
|
|
698
|
+
return this.#state;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Invokes a callback function that is allowed to mutate the given state
|
|
702
|
+
* value. Do not change the value outside of the callback.
|
|
703
|
+
*
|
|
704
|
+
* If the callback explicitly returns `false`, it's assumed that the state
|
|
705
|
+
* was not changed.
|
|
706
|
+
*/
|
|
707
|
+
mutate(callback) {
|
|
708
|
+
batch(() => {
|
|
709
|
+
const result = callback ? callback(this.#state) : true;
|
|
710
|
+
if (result !== null && typeof result === "object" && "then" in result) {
|
|
711
|
+
raise("MutableSignal.mutate() does not support async callbacks");
|
|
712
|
+
}
|
|
713
|
+
if (result !== false) {
|
|
714
|
+
this.markSinksDirty();
|
|
715
|
+
enqueueTrigger(this);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// src/lib/stringify.ts
|
|
722
|
+
function replacer(_key, value) {
|
|
723
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).sort().reduce((sorted, key) => {
|
|
724
|
+
sorted[key] = value[key];
|
|
725
|
+
return sorted;
|
|
726
|
+
}, {}) : value;
|
|
727
|
+
}
|
|
728
|
+
function stringify(value) {
|
|
729
|
+
return JSON.stringify(value, replacer);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// src/lib/batch.ts
|
|
733
|
+
var DEFAULT_SIZE = 50;
|
|
734
|
+
var BatchCall = class {
|
|
735
|
+
input;
|
|
736
|
+
resolve;
|
|
737
|
+
reject;
|
|
738
|
+
promise;
|
|
739
|
+
constructor(input) {
|
|
740
|
+
this.input = input;
|
|
741
|
+
const { promise, resolve, reject } = Promise_withResolvers();
|
|
742
|
+
this.promise = promise;
|
|
743
|
+
this.resolve = resolve;
|
|
744
|
+
this.reject = reject;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
var Batch = class {
|
|
748
|
+
#queue = [];
|
|
749
|
+
#callback;
|
|
750
|
+
#size;
|
|
751
|
+
#delay;
|
|
752
|
+
#delayTimeoutId;
|
|
753
|
+
error = false;
|
|
754
|
+
constructor(callback, options) {
|
|
755
|
+
this.#callback = callback;
|
|
756
|
+
this.#size = options.size ?? DEFAULT_SIZE;
|
|
757
|
+
this.#delay = options.delay;
|
|
758
|
+
}
|
|
759
|
+
#clearDelayTimeout() {
|
|
760
|
+
if (this.#delayTimeoutId !== void 0) {
|
|
761
|
+
clearTimeout(this.#delayTimeoutId);
|
|
762
|
+
this.#delayTimeoutId = void 0;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
#schedule() {
|
|
766
|
+
if (this.#queue.length === this.#size) {
|
|
767
|
+
void this.#flush();
|
|
768
|
+
} else if (this.#queue.length === 1) {
|
|
769
|
+
this.#clearDelayTimeout();
|
|
770
|
+
this.#delayTimeoutId = setTimeout(() => void this.#flush(), this.#delay);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async #flush() {
|
|
774
|
+
if (this.#queue.length === 0) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const calls = this.#queue.splice(0);
|
|
778
|
+
const inputs = calls.map((call) => call.input);
|
|
779
|
+
try {
|
|
780
|
+
const results = await this.#callback(inputs);
|
|
781
|
+
this.error = false;
|
|
782
|
+
calls.forEach((call, index) => {
|
|
783
|
+
const result = results?.[index];
|
|
784
|
+
if (!Array.isArray(results)) {
|
|
785
|
+
call.reject(new Error("Callback must return an array."));
|
|
786
|
+
} else if (calls.length !== results.length) {
|
|
787
|
+
call.reject(
|
|
788
|
+
new Error(
|
|
789
|
+
`Callback must return an array of the same length as the number of provided items. Expected ${calls.length}, but got ${results.length}.`
|
|
790
|
+
)
|
|
791
|
+
);
|
|
792
|
+
} else if (result instanceof Error) {
|
|
793
|
+
call.reject(result);
|
|
794
|
+
} else {
|
|
795
|
+
call.resolve(result);
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
} catch (error3) {
|
|
799
|
+
this.error = true;
|
|
800
|
+
calls.forEach((call) => {
|
|
801
|
+
call.reject(error3);
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
get(input) {
|
|
806
|
+
const existingCall = this.#queue.find(
|
|
807
|
+
(call2) => stringify(call2.input) === stringify(input)
|
|
808
|
+
);
|
|
809
|
+
if (existingCall) {
|
|
810
|
+
return existingCall.promise;
|
|
811
|
+
}
|
|
812
|
+
const call = new BatchCall(input);
|
|
813
|
+
this.#queue.push(call);
|
|
814
|
+
this.#schedule();
|
|
815
|
+
return call.promise;
|
|
816
|
+
}
|
|
817
|
+
clear() {
|
|
818
|
+
this.#queue = [];
|
|
819
|
+
this.error = false;
|
|
820
|
+
this.#clearDelayTimeout();
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
function createBatchStore(batch2) {
|
|
824
|
+
const signal = new MutableSignal(/* @__PURE__ */ new Map());
|
|
825
|
+
function getCacheKey(args) {
|
|
826
|
+
return stringify(args);
|
|
827
|
+
}
|
|
828
|
+
function update(cacheKey, state) {
|
|
829
|
+
signal.mutate((cache) => {
|
|
830
|
+
cache.set(cacheKey, state);
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function invalidate(inputs) {
|
|
834
|
+
signal.mutate((cache) => {
|
|
835
|
+
if (Array.isArray(inputs)) {
|
|
836
|
+
for (const input of inputs) {
|
|
837
|
+
cache.delete(getCacheKey(input));
|
|
838
|
+
}
|
|
839
|
+
} else {
|
|
840
|
+
cache.clear();
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
async function enqueue(input) {
|
|
845
|
+
const cacheKey = getCacheKey(input);
|
|
846
|
+
const cache = signal.get();
|
|
847
|
+
if (cache.has(cacheKey)) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
update(cacheKey, { isLoading: true });
|
|
852
|
+
const result = await batch2.get(input);
|
|
853
|
+
update(cacheKey, { isLoading: false, data: result });
|
|
854
|
+
} catch (error3) {
|
|
855
|
+
update(cacheKey, {
|
|
856
|
+
isLoading: false,
|
|
857
|
+
error: error3
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function getItemState(input) {
|
|
862
|
+
const cacheKey = getCacheKey(input);
|
|
863
|
+
const cache = signal.get();
|
|
864
|
+
return cache.get(cacheKey);
|
|
865
|
+
}
|
|
866
|
+
function _cacheKeys() {
|
|
867
|
+
const cache = signal.get();
|
|
868
|
+
return [...cache.keys()];
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
subscribe: signal.subscribe,
|
|
872
|
+
enqueue,
|
|
873
|
+
getItemState,
|
|
544
874
|
invalidate,
|
|
545
|
-
|
|
875
|
+
batch: batch2,
|
|
546
876
|
_cacheKeys
|
|
547
877
|
};
|
|
548
878
|
}
|
|
@@ -583,6 +913,36 @@ function createInboxNotificationId() {
|
|
|
583
913
|
return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
|
|
584
914
|
}
|
|
585
915
|
|
|
916
|
+
// src/lib/DefaultMap.ts
|
|
917
|
+
var DefaultMap = class extends Map {
|
|
918
|
+
#defaultFn;
|
|
919
|
+
/**
|
|
920
|
+
* If the default function is not provided to the constructor, it has to be
|
|
921
|
+
* provided in each .getOrCreate() call individually.
|
|
922
|
+
*/
|
|
923
|
+
constructor(defaultFn, entries2) {
|
|
924
|
+
super(entries2);
|
|
925
|
+
this.#defaultFn = defaultFn;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Gets the value at the given key, or creates it.
|
|
929
|
+
*
|
|
930
|
+
* Difference from normal Map: if the key does not exist, it will be created
|
|
931
|
+
* on the fly using the factory function, and that value will get returned
|
|
932
|
+
* instead of `undefined`.
|
|
933
|
+
*/
|
|
934
|
+
getOrCreate(key, defaultFn) {
|
|
935
|
+
if (super.has(key)) {
|
|
936
|
+
return super.get(key);
|
|
937
|
+
} else {
|
|
938
|
+
const fn = defaultFn ?? this.#defaultFn ?? raise("DefaultMap used without a factory function");
|
|
939
|
+
const value = fn(key);
|
|
940
|
+
this.set(key, value);
|
|
941
|
+
return value;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
|
|
586
946
|
// src/lib/objectToQuery.ts
|
|
587
947
|
var identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
588
948
|
function objectToQuery(obj) {
|
|
@@ -597,10 +957,12 @@ function objectToQuery(obj) {
|
|
|
597
957
|
}
|
|
598
958
|
if (isSimpleValue(value)) {
|
|
599
959
|
keyValuePairs.push([key, value]);
|
|
600
|
-
} else if (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
960
|
+
} else if (isPlainObject(value)) {
|
|
961
|
+
if (isStartsWithOperator(value)) {
|
|
962
|
+
keyValuePairsWithOperator.push([key, value]);
|
|
963
|
+
} else {
|
|
964
|
+
indexedKeys.push([key, value]);
|
|
965
|
+
}
|
|
604
966
|
}
|
|
605
967
|
});
|
|
606
968
|
filterList = [
|
|
@@ -617,7 +979,7 @@ function objectToQuery(obj) {
|
|
|
617
979
|
}
|
|
618
980
|
if (isSimpleValue(nestedValue)) {
|
|
619
981
|
nKeyValuePairs.push([formatFilterKey(key, nestedKey), nestedValue]);
|
|
620
|
-
} else if (
|
|
982
|
+
} else if (isStartsWithOperator(nestedValue)) {
|
|
621
983
|
nKeyValuePairsWithOperator.push([
|
|
622
984
|
formatFilterKey(key, nestedKey),
|
|
623
985
|
nestedValue
|
|
@@ -630,9 +992,7 @@ function objectToQuery(obj) {
|
|
|
630
992
|
...getFiltersFromKeyValuePairsWithOperator(nKeyValuePairsWithOperator)
|
|
631
993
|
];
|
|
632
994
|
});
|
|
633
|
-
return filterList.map(
|
|
634
|
-
({ key, operator, value }) => formatFilter(key, operator, formatFilterValue(value))
|
|
635
|
-
).join(" AND ");
|
|
995
|
+
return filterList.map(({ key, operator, value }) => `${key}${operator}${quote(value)}`).join(" ");
|
|
636
996
|
}
|
|
637
997
|
var getFiltersFromKeyValuePairs = (keyValuePairs) => {
|
|
638
998
|
const filters = [];
|
|
@@ -659,38 +1019,20 @@ var getFiltersFromKeyValuePairsWithOperator = (keyValuePairsWithOperator) => {
|
|
|
659
1019
|
return filters;
|
|
660
1020
|
};
|
|
661
1021
|
var isSimpleValue = (value) => {
|
|
662
|
-
|
|
663
|
-
return true;
|
|
664
|
-
}
|
|
665
|
-
return false;
|
|
1022
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
|
|
666
1023
|
};
|
|
667
|
-
var
|
|
668
|
-
if (
|
|
669
|
-
return
|
|
670
|
-
}
|
|
671
|
-
return false;
|
|
672
|
-
};
|
|
673
|
-
var formatFilter = (key, operator, value) => {
|
|
674
|
-
return `${key}${operator}${value}`;
|
|
675
|
-
};
|
|
676
|
-
var formatFilterKey = (key, nestedKey) => {
|
|
677
|
-
if (nestedKey) {
|
|
678
|
-
return `${key}[${JSON.stringify(nestedKey)}]`;
|
|
1024
|
+
var formatFilterKey = (key, nestedKey) => {
|
|
1025
|
+
if (nestedKey) {
|
|
1026
|
+
return `${key}[${quote(nestedKey)}]`;
|
|
679
1027
|
}
|
|
680
1028
|
return key;
|
|
681
1029
|
};
|
|
682
|
-
var formatFilterValue = (value) => {
|
|
683
|
-
if (typeof value === "string") {
|
|
684
|
-
if (isStringEmpty(value)) {
|
|
685
|
-
throw new Error("Value cannot be empty");
|
|
686
|
-
}
|
|
687
|
-
return JSON.stringify(value);
|
|
688
|
-
}
|
|
689
|
-
return value.toString();
|
|
690
|
-
};
|
|
691
1030
|
var isStringEmpty = (value) => {
|
|
692
1031
|
return !value || value.toString().trim() === "";
|
|
693
1032
|
};
|
|
1033
|
+
function quote(value) {
|
|
1034
|
+
return typeof value !== "string" || value.includes("'") ? JSON.stringify(value) : `'${value}'`;
|
|
1035
|
+
}
|
|
694
1036
|
|
|
695
1037
|
// src/lib/url.ts
|
|
696
1038
|
function toURLSearchParams(params) {
|
|
@@ -1083,40 +1425,31 @@ function createApiClient({
|
|
|
1083
1425
|
}
|
|
1084
1426
|
}
|
|
1085
1427
|
}
|
|
1086
|
-
const
|
|
1428
|
+
const attachmentUrlsBatchStoresByRoom = new DefaultMap((roomId) => {
|
|
1429
|
+
const batch2 = new Batch(
|
|
1430
|
+
async (batchedAttachmentIds) => {
|
|
1431
|
+
const attachmentIds = batchedAttachmentIds.flat();
|
|
1432
|
+
const { urls } = await httpClient.post(
|
|
1433
|
+
url`/v2/c/rooms/${roomId}/attachments/presigned-urls`,
|
|
1434
|
+
await authManager.getAuthValue({
|
|
1435
|
+
requestedScope: "comments:read",
|
|
1436
|
+
roomId
|
|
1437
|
+
}),
|
|
1438
|
+
{ attachmentIds }
|
|
1439
|
+
);
|
|
1440
|
+
return urls.map(
|
|
1441
|
+
(url2) => url2 ?? new Error("There was an error while getting this attachment's URL")
|
|
1442
|
+
);
|
|
1443
|
+
},
|
|
1444
|
+
{ delay: 50 }
|
|
1445
|
+
);
|
|
1446
|
+
return createBatchStore(batch2);
|
|
1447
|
+
});
|
|
1087
1448
|
function getOrCreateAttachmentUrlsStore(roomId) {
|
|
1088
|
-
|
|
1089
|
-
if (store === void 0) {
|
|
1090
|
-
const batch2 = new Batch(
|
|
1091
|
-
async (batchedAttachmentIds) => {
|
|
1092
|
-
const attachmentIds = batchedAttachmentIds.flat();
|
|
1093
|
-
const { urls } = await httpClient.post(
|
|
1094
|
-
url`/v2/c/rooms/${roomId}/attachments/presigned-urls`,
|
|
1095
|
-
await authManager.getAuthValue({
|
|
1096
|
-
requestedScope: "comments:read",
|
|
1097
|
-
roomId
|
|
1098
|
-
}),
|
|
1099
|
-
{
|
|
1100
|
-
attachmentIds
|
|
1101
|
-
}
|
|
1102
|
-
);
|
|
1103
|
-
return urls.map(
|
|
1104
|
-
(url2) => url2 ?? new Error(
|
|
1105
|
-
"There was an error while getting this attachment's URL"
|
|
1106
|
-
)
|
|
1107
|
-
);
|
|
1108
|
-
},
|
|
1109
|
-
{
|
|
1110
|
-
delay: 50
|
|
1111
|
-
}
|
|
1112
|
-
);
|
|
1113
|
-
store = createBatchStore(batch2);
|
|
1114
|
-
getAttachmentUrlsBatchStoreByRoom.set(roomId, store);
|
|
1115
|
-
}
|
|
1116
|
-
return store;
|
|
1449
|
+
return attachmentUrlsBatchStoresByRoom.getOrCreate(roomId);
|
|
1117
1450
|
}
|
|
1118
1451
|
function getAttachmentUrl(options) {
|
|
1119
|
-
const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).
|
|
1452
|
+
const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).batch;
|
|
1120
1453
|
return batch2.get(options.attachmentId);
|
|
1121
1454
|
}
|
|
1122
1455
|
async function getNotificationSettings(options) {
|
|
@@ -1142,33 +1475,25 @@ function createApiClient({
|
|
|
1142
1475
|
options.settings
|
|
1143
1476
|
);
|
|
1144
1477
|
}
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
{
|
|
1163
|
-
delay: 50
|
|
1164
|
-
}
|
|
1165
|
-
);
|
|
1166
|
-
markInboxNotificationsAsReadBatchByRoom.set(roomId, batch2);
|
|
1167
|
-
}
|
|
1168
|
-
return batch2;
|
|
1169
|
-
}
|
|
1478
|
+
const markAsReadBatchesByRoom = new DefaultMap(
|
|
1479
|
+
(roomId) => new Batch(
|
|
1480
|
+
async (batchedInboxNotificationIds) => {
|
|
1481
|
+
const inboxNotificationIds = batchedInboxNotificationIds.flat();
|
|
1482
|
+
await httpClient.post(
|
|
1483
|
+
url`/v2/c/rooms/${roomId}/inbox-notifications/read`,
|
|
1484
|
+
await authManager.getAuthValue({
|
|
1485
|
+
requestedScope: "comments:read",
|
|
1486
|
+
roomId
|
|
1487
|
+
}),
|
|
1488
|
+
{ inboxNotificationIds }
|
|
1489
|
+
);
|
|
1490
|
+
return inboxNotificationIds;
|
|
1491
|
+
},
|
|
1492
|
+
{ delay: 50 }
|
|
1493
|
+
)
|
|
1494
|
+
);
|
|
1170
1495
|
async function markRoomInboxNotificationAsRead(options) {
|
|
1171
|
-
const batch2 =
|
|
1496
|
+
const batch2 = markAsReadBatchesByRoom.getOrCreate(options.roomId);
|
|
1172
1497
|
return batch2.get(options.inboxNotificationId);
|
|
1173
1498
|
}
|
|
1174
1499
|
async function createTextMention(options) {
|
|
@@ -1532,14 +1857,7 @@ var HttpClient = class {
|
|
|
1532
1857
|
async #fetch(endpoint, authValue, options, params) {
|
|
1533
1858
|
const response = await this.#rawFetch(endpoint, authValue, options, params);
|
|
1534
1859
|
if (!response.ok) {
|
|
1535
|
-
|
|
1536
|
-
try {
|
|
1537
|
-
const errorBody = await response.json();
|
|
1538
|
-
error3 = new HttpError(errorBody.message, response.status, errorBody);
|
|
1539
|
-
} catch {
|
|
1540
|
-
error3 = new HttpError(response.statusText, response.status);
|
|
1541
|
-
}
|
|
1542
|
-
throw error3;
|
|
1860
|
+
throw await HttpError.fromResponse(response);
|
|
1543
1861
|
}
|
|
1544
1862
|
let body;
|
|
1545
1863
|
try {
|
|
@@ -2166,6 +2484,7 @@ function toNewConnectionStatus(machine) {
|
|
|
2166
2484
|
return machine.context.successCount > 0 ? "reconnecting" : "connecting";
|
|
2167
2485
|
case "@idle.failed":
|
|
2168
2486
|
return "disconnected";
|
|
2487
|
+
// istanbul ignore next
|
|
2169
2488
|
default:
|
|
2170
2489
|
return assertNever(state, "Unknown state");
|
|
2171
2490
|
}
|
|
@@ -2182,13 +2501,6 @@ var StopRetrying = class extends Error {
|
|
|
2182
2501
|
super(reason);
|
|
2183
2502
|
}
|
|
2184
2503
|
};
|
|
2185
|
-
var LiveblocksError = class extends Error {
|
|
2186
|
-
/** @internal */
|
|
2187
|
-
constructor(message, code) {
|
|
2188
|
-
super(message);
|
|
2189
|
-
this.code = code;
|
|
2190
|
-
}
|
|
2191
|
-
};
|
|
2192
2504
|
function nextBackoffDelay(currentDelay, delays) {
|
|
2193
2505
|
return delays.find((delay) => delay > currentDelay) ?? delays[delays.length - 1];
|
|
2194
2506
|
}
|
|
@@ -2298,11 +2610,10 @@ var assign = (patch) => (ctx) => ctx.patch(patch);
|
|
|
2298
2610
|
function createConnectionStateMachine(delegates, options) {
|
|
2299
2611
|
const onMessage = makeBufferableEventSource();
|
|
2300
2612
|
onMessage.pause();
|
|
2301
|
-
const
|
|
2302
|
-
function fireErrorEvent(
|
|
2613
|
+
const onConnectionError = makeEventSource();
|
|
2614
|
+
function fireErrorEvent(message, code) {
|
|
2303
2615
|
return () => {
|
|
2304
|
-
|
|
2305
|
-
onLiveblocksError.notify(err);
|
|
2616
|
+
onConnectionError.notify({ message, code });
|
|
2306
2617
|
};
|
|
2307
2618
|
}
|
|
2308
2619
|
const initialContext = {
|
|
@@ -2660,7 +2971,7 @@ function createConnectionStateMachine(delegates, options) {
|
|
|
2660
2971
|
didConnect,
|
|
2661
2972
|
didDisconnect,
|
|
2662
2973
|
onMessage: onMessage.observable,
|
|
2663
|
-
|
|
2974
|
+
onConnectionError: onConnectionError.observable
|
|
2664
2975
|
}
|
|
2665
2976
|
};
|
|
2666
2977
|
}
|
|
@@ -3022,458 +3333,205 @@ function sendToPanel(message, options) {
|
|
|
3022
3333
|
}
|
|
3023
3334
|
window.postMessage(fullMsg, "*");
|
|
3024
3335
|
}
|
|
3025
|
-
var eventSource = makeEventSource();
|
|
3026
|
-
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
3027
|
-
window.addEventListener("message", (event) => {
|
|
3028
|
-
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
3029
|
-
eventSource.notify(event.data);
|
|
3030
|
-
} else {
|
|
3031
|
-
}
|
|
3032
|
-
});
|
|
3033
|
-
}
|
|
3034
|
-
var onMessageFromPanel = eventSource.observable;
|
|
3035
|
-
|
|
3036
|
-
// src/devtools/index.ts
|
|
3037
|
-
var VERSION = PKG_VERSION || "dev";
|
|
3038
|
-
var _devtoolsSetupHasRun = false;
|
|
3039
|
-
function setupDevTools(getAllRooms) {
|
|
3040
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3041
|
-
return;
|
|
3042
|
-
}
|
|
3043
|
-
if (_devtoolsSetupHasRun) {
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
_devtoolsSetupHasRun = true;
|
|
3047
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
3048
|
-
switch (msg.msg) {
|
|
3049
|
-
case "connect": {
|
|
3050
|
-
activateBridge(true);
|
|
3051
|
-
for (const roomId of getAllRooms()) {
|
|
3052
|
-
sendToPanel({
|
|
3053
|
-
msg: "room::available",
|
|
3054
|
-
roomId,
|
|
3055
|
-
clientVersion: VERSION
|
|
3056
|
-
});
|
|
3057
|
-
}
|
|
3058
|
-
break;
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
});
|
|
3062
|
-
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
3063
|
-
}
|
|
3064
|
-
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
3065
|
-
function stopSyncStream(roomId) {
|
|
3066
|
-
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
3067
|
-
unsubsByRoomId.delete(roomId);
|
|
3068
|
-
for (const unsub of unsubs) {
|
|
3069
|
-
unsub();
|
|
3070
|
-
}
|
|
3071
|
-
}
|
|
3072
|
-
function startSyncStream(room) {
|
|
3073
|
-
stopSyncStream(room.id);
|
|
3074
|
-
fullSync(room);
|
|
3075
|
-
unsubsByRoomId.set(room.id, [
|
|
3076
|
-
// When the connection status changes
|
|
3077
|
-
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
3078
|
-
// When storage initializes, send the update
|
|
3079
|
-
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
3080
|
-
// Any time storage updates, send the new storage root
|
|
3081
|
-
room.events.storageBatch.subscribe(() => partialSyncStorage(room)),
|
|
3082
|
-
// Any time "me" or "others" updates, send the new values accordingly
|
|
3083
|
-
room.events.self.subscribe(() => partialSyncMe(room)),
|
|
3084
|
-
room.events.others.subscribe(() => partialSyncOthers(room)),
|
|
3085
|
-
// Any time ydoc is updated, forward the update
|
|
3086
|
-
room.events.ydoc.subscribe((update) => syncYdocUpdate(room, update)),
|
|
3087
|
-
// Any time a custom room event is received, forward it
|
|
3088
|
-
room.events.customEvent.subscribe(
|
|
3089
|
-
(eventData) => forwardEvent(room, eventData)
|
|
3090
|
-
)
|
|
3091
|
-
]);
|
|
3092
|
-
}
|
|
3093
|
-
function syncYdocUpdate(room, update) {
|
|
3094
|
-
sendToPanel({
|
|
3095
|
-
msg: "room::sync::ydoc",
|
|
3096
|
-
roomId: room.id,
|
|
3097
|
-
update
|
|
3098
|
-
});
|
|
3099
|
-
}
|
|
3100
|
-
var loadedAt = Date.now();
|
|
3101
|
-
var eventCounter = 0;
|
|
3102
|
-
function nextEventId() {
|
|
3103
|
-
return `event-${loadedAt}-${eventCounter++}`;
|
|
3104
|
-
}
|
|
3105
|
-
function forwardEvent(room, eventData) {
|
|
3106
|
-
sendToPanel({
|
|
3107
|
-
msg: "room::events::custom-event",
|
|
3108
|
-
roomId: room.id,
|
|
3109
|
-
event: {
|
|
3110
|
-
type: "CustomEvent",
|
|
3111
|
-
id: nextEventId(),
|
|
3112
|
-
key: "Event",
|
|
3113
|
-
connectionId: eventData.connectionId,
|
|
3114
|
-
payload: eventData.event
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
}
|
|
3118
|
-
function partialSyncConnection(room) {
|
|
3119
|
-
sendToPanel({
|
|
3120
|
-
msg: "room::sync::partial",
|
|
3121
|
-
roomId: room.id,
|
|
3122
|
-
status: room.getStatus()
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
function partialSyncStorage(room) {
|
|
3126
|
-
const root = room.getStorageSnapshot();
|
|
3127
|
-
if (root) {
|
|
3128
|
-
sendToPanel({
|
|
3129
|
-
msg: "room::sync::partial",
|
|
3130
|
-
roomId: room.id,
|
|
3131
|
-
storage: root.toTreeNode("root").payload
|
|
3132
|
-
});
|
|
3133
|
-
}
|
|
3134
|
-
}
|
|
3135
|
-
function partialSyncMe(room) {
|
|
3136
|
-
const me = room[kInternal].getSelf_forDevTools();
|
|
3137
|
-
if (me) {
|
|
3138
|
-
sendToPanel({
|
|
3139
|
-
msg: "room::sync::partial",
|
|
3140
|
-
roomId: room.id,
|
|
3141
|
-
me
|
|
3142
|
-
});
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
function partialSyncOthers(room) {
|
|
3146
|
-
const others = room[kInternal].getOthers_forDevTools();
|
|
3147
|
-
if (others) {
|
|
3148
|
-
sendToPanel({
|
|
3149
|
-
msg: "room::sync::partial",
|
|
3150
|
-
roomId: room.id,
|
|
3151
|
-
others
|
|
3152
|
-
});
|
|
3153
|
-
}
|
|
3154
|
-
}
|
|
3155
|
-
function fullSync(room) {
|
|
3156
|
-
const root = room.getStorageSnapshot();
|
|
3157
|
-
const me = room[kInternal].getSelf_forDevTools();
|
|
3158
|
-
const others = room[kInternal].getOthers_forDevTools();
|
|
3159
|
-
room.fetchYDoc("");
|
|
3160
|
-
sendToPanel({
|
|
3161
|
-
msg: "room::sync::full",
|
|
3162
|
-
roomId: room.id,
|
|
3163
|
-
status: room.getStatus(),
|
|
3164
|
-
storage: root?.toTreeNode("root").payload ?? null,
|
|
3165
|
-
me,
|
|
3166
|
-
others
|
|
3167
|
-
});
|
|
3168
|
-
}
|
|
3169
|
-
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
3170
|
-
function stopRoomChannelListener(roomId) {
|
|
3171
|
-
const listener = roomChannelListeners.get(roomId);
|
|
3172
|
-
roomChannelListeners.delete(roomId);
|
|
3173
|
-
if (listener) {
|
|
3174
|
-
listener();
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
function linkDevTools(roomId, room) {
|
|
3178
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3179
|
-
return;
|
|
3180
|
-
}
|
|
3181
|
-
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
3182
|
-
stopRoomChannelListener(roomId);
|
|
3183
|
-
roomChannelListeners.set(
|
|
3184
|
-
roomId,
|
|
3185
|
-
// Returns the unsubscribe callback, that we store in the
|
|
3186
|
-
// roomChannelListeners registry
|
|
3187
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
3188
|
-
switch (msg.msg) {
|
|
3189
|
-
case "room::subscribe": {
|
|
3190
|
-
if (msg.roomId === roomId) {
|
|
3191
|
-
startSyncStream(room);
|
|
3192
|
-
}
|
|
3193
|
-
break;
|
|
3194
|
-
}
|
|
3195
|
-
case "room::unsubscribe": {
|
|
3196
|
-
if (msg.roomId === roomId) {
|
|
3197
|
-
stopSyncStream(roomId);
|
|
3198
|
-
}
|
|
3199
|
-
break;
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
})
|
|
3203
|
-
);
|
|
3204
|
-
}
|
|
3205
|
-
function unlinkDevTools(roomId) {
|
|
3206
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3207
|
-
return;
|
|
3208
|
-
}
|
|
3209
|
-
stopSyncStream(roomId);
|
|
3210
|
-
stopRoomChannelListener(roomId);
|
|
3211
|
-
sendToPanel({
|
|
3212
|
-
msg: "room::unavailable",
|
|
3213
|
-
roomId
|
|
3214
|
-
});
|
|
3215
|
-
}
|
|
3216
|
-
|
|
3217
|
-
// src/lib/freeze.ts
|
|
3218
|
-
var freeze = process.env.NODE_ENV === "production" ? (
|
|
3219
|
-
/* istanbul ignore next */
|
|
3220
|
-
(x) => x
|
|
3221
|
-
) : Object.freeze;
|
|
3222
|
-
|
|
3223
|
-
// src/lib/signals.ts
|
|
3224
|
-
var kSinks = Symbol("kSinks");
|
|
3225
|
-
var kTrigger = Symbol("kTrigger");
|
|
3226
|
-
var signalsToTrigger = null;
|
|
3227
|
-
function batch(callback) {
|
|
3228
|
-
if (signalsToTrigger !== null) {
|
|
3229
|
-
callback();
|
|
3230
|
-
return;
|
|
3231
|
-
}
|
|
3232
|
-
signalsToTrigger = /* @__PURE__ */ new Set();
|
|
3233
|
-
try {
|
|
3234
|
-
callback();
|
|
3235
|
-
} finally {
|
|
3236
|
-
for (const signal of signalsToTrigger) {
|
|
3237
|
-
signal[kTrigger]();
|
|
3238
|
-
}
|
|
3239
|
-
signalsToTrigger = null;
|
|
3240
|
-
}
|
|
3241
|
-
}
|
|
3242
|
-
function enqueueTrigger(signal) {
|
|
3243
|
-
if (!signalsToTrigger) raise("Expected to be in an active batch");
|
|
3244
|
-
signalsToTrigger.add(signal);
|
|
3245
|
-
}
|
|
3246
|
-
function merge(target, patch) {
|
|
3247
|
-
let updated = false;
|
|
3248
|
-
const newValue = { ...target };
|
|
3249
|
-
Object.keys(patch).forEach((k) => {
|
|
3250
|
-
const key = k;
|
|
3251
|
-
const val = patch[key];
|
|
3252
|
-
if (newValue[key] !== val) {
|
|
3253
|
-
if (val === void 0) {
|
|
3254
|
-
delete newValue[key];
|
|
3255
|
-
} else {
|
|
3256
|
-
newValue[key] = val;
|
|
3257
|
-
}
|
|
3258
|
-
updated = true;
|
|
3259
|
-
}
|
|
3260
|
-
});
|
|
3261
|
-
return updated ? newValue : target;
|
|
3262
|
-
}
|
|
3263
|
-
var AbstractSignal = class {
|
|
3264
|
-
/** @internal */
|
|
3265
|
-
equals;
|
|
3266
|
-
#eventSource;
|
|
3267
|
-
/** @internal */
|
|
3268
|
-
[kSinks];
|
|
3269
|
-
constructor(equals) {
|
|
3270
|
-
this.equals = equals ?? Object.is;
|
|
3271
|
-
this.#eventSource = makeEventSource();
|
|
3272
|
-
this[kSinks] = /* @__PURE__ */ new Set();
|
|
3273
|
-
this.get = this.get.bind(this);
|
|
3274
|
-
this.subscribe = this.subscribe.bind(this);
|
|
3275
|
-
this.subscribeOnce = this.subscribeOnce.bind(this);
|
|
3276
|
-
}
|
|
3277
|
-
[Symbol.dispose]() {
|
|
3278
|
-
this.#eventSource[Symbol.dispose]();
|
|
3279
|
-
this.#eventSource = "(disposed)";
|
|
3280
|
-
this.equals = "(disposed)";
|
|
3281
|
-
}
|
|
3282
|
-
get hasWatchers() {
|
|
3283
|
-
if (this.#eventSource.count() > 0) return true;
|
|
3284
|
-
for (const sink of this[kSinks]) {
|
|
3285
|
-
if (sink.hasWatchers) {
|
|
3286
|
-
return true;
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
return false;
|
|
3290
|
-
}
|
|
3291
|
-
[kTrigger]() {
|
|
3292
|
-
this.#eventSource.notify();
|
|
3293
|
-
for (const sink of this[kSinks]) {
|
|
3294
|
-
enqueueTrigger(sink);
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
subscribe(callback) {
|
|
3298
|
-
return this.#eventSource.subscribe(callback);
|
|
3299
|
-
}
|
|
3300
|
-
subscribeOnce(callback) {
|
|
3301
|
-
const unsub = this.subscribe(() => {
|
|
3302
|
-
unsub();
|
|
3303
|
-
return callback();
|
|
3304
|
-
});
|
|
3305
|
-
return unsub;
|
|
3306
|
-
}
|
|
3307
|
-
waitUntil() {
|
|
3308
|
-
throw new Error("waitUntil not supported on Signals");
|
|
3309
|
-
}
|
|
3310
|
-
markSinksDirty() {
|
|
3311
|
-
for (const sink of this[kSinks]) {
|
|
3312
|
-
sink.markDirty();
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
addSink(sink) {
|
|
3316
|
-
this[kSinks].add(sink);
|
|
3317
|
-
}
|
|
3318
|
-
removeSink(sink) {
|
|
3319
|
-
this[kSinks].delete(sink);
|
|
3320
|
-
}
|
|
3321
|
-
asReadonly() {
|
|
3322
|
-
return this;
|
|
3323
|
-
}
|
|
3324
|
-
};
|
|
3325
|
-
var Signal = class extends AbstractSignal {
|
|
3326
|
-
#value;
|
|
3327
|
-
constructor(value, equals) {
|
|
3328
|
-
super(equals);
|
|
3329
|
-
this.#value = freeze(value);
|
|
3330
|
-
}
|
|
3331
|
-
[Symbol.dispose]() {
|
|
3332
|
-
super[Symbol.dispose]();
|
|
3333
|
-
this.#value = "(disposed)";
|
|
3334
|
-
}
|
|
3335
|
-
get() {
|
|
3336
|
-
return this.#value;
|
|
3337
|
-
}
|
|
3338
|
-
set(newValue) {
|
|
3339
|
-
batch(() => {
|
|
3340
|
-
if (typeof newValue === "function") {
|
|
3341
|
-
newValue = newValue(this.#value);
|
|
3342
|
-
}
|
|
3343
|
-
if (!this.equals(this.#value, newValue)) {
|
|
3344
|
-
this.#value = freeze(newValue);
|
|
3345
|
-
this.markSinksDirty();
|
|
3346
|
-
enqueueTrigger(this);
|
|
3347
|
-
}
|
|
3348
|
-
});
|
|
3349
|
-
}
|
|
3350
|
-
};
|
|
3351
|
-
var PatchableSignal = class extends Signal {
|
|
3352
|
-
constructor(data) {
|
|
3353
|
-
super(freeze(compactObject(data)));
|
|
3354
|
-
}
|
|
3355
|
-
set() {
|
|
3356
|
-
throw new Error("Don't call .set() directly, use .patch()");
|
|
3357
|
-
}
|
|
3358
|
-
/**
|
|
3359
|
-
* Patches the current object.
|
|
3360
|
-
*/
|
|
3361
|
-
patch(patch) {
|
|
3362
|
-
super.set((old) => merge(old, patch));
|
|
3363
|
-
}
|
|
3364
|
-
};
|
|
3365
|
-
var INITIAL = Symbol();
|
|
3366
|
-
var DerivedSignal = class _DerivedSignal extends AbstractSignal {
|
|
3367
|
-
#prevValue;
|
|
3368
|
-
#dirty;
|
|
3369
|
-
// When true, the value in #value may not be up-to-date and needs re-checking
|
|
3370
|
-
#parents;
|
|
3371
|
-
#transform;
|
|
3372
|
-
// prettier-ignore
|
|
3373
|
-
static from(...args) {
|
|
3374
|
-
const last = args.pop();
|
|
3375
|
-
if (typeof last !== "function")
|
|
3376
|
-
raise("Invalid .from() call, last argument expected to be a function");
|
|
3377
|
-
if (typeof args[args.length - 1] === "function") {
|
|
3378
|
-
const equals = last;
|
|
3379
|
-
const transform = args.pop();
|
|
3380
|
-
return new _DerivedSignal(args, transform, equals);
|
|
3381
|
-
} else {
|
|
3382
|
-
const transform = last;
|
|
3383
|
-
return new _DerivedSignal(args, transform);
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
constructor(parents, transform, equals) {
|
|
3387
|
-
super(equals);
|
|
3388
|
-
this.#dirty = true;
|
|
3389
|
-
this.#prevValue = INITIAL;
|
|
3390
|
-
this.#parents = parents;
|
|
3391
|
-
this.#transform = transform;
|
|
3392
|
-
for (const parent of parents) {
|
|
3393
|
-
parent.addSink(this);
|
|
3394
|
-
}
|
|
3395
|
-
}
|
|
3396
|
-
[Symbol.dispose]() {
|
|
3397
|
-
for (const parent of this.#parents) {
|
|
3398
|
-
parent.removeSink(this);
|
|
3399
|
-
}
|
|
3400
|
-
this.#prevValue = "(disposed)";
|
|
3401
|
-
this.#parents = "(disposed)";
|
|
3402
|
-
this.#transform = "(disposed)";
|
|
3403
|
-
}
|
|
3404
|
-
get isDirty() {
|
|
3405
|
-
return this.#dirty;
|
|
3406
|
-
}
|
|
3407
|
-
#recompute() {
|
|
3408
|
-
const derived = this.#transform(...this.#parents.map((p) => p.get()));
|
|
3409
|
-
this.#dirty = false;
|
|
3410
|
-
if (!this.equals(this.#prevValue, derived)) {
|
|
3411
|
-
this.#prevValue = derived;
|
|
3412
|
-
return true;
|
|
3336
|
+
var eventSource = makeEventSource();
|
|
3337
|
+
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
3338
|
+
window.addEventListener("message", (event) => {
|
|
3339
|
+
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
3340
|
+
eventSource.notify(event.data);
|
|
3341
|
+
} else {
|
|
3413
3342
|
}
|
|
3414
|
-
|
|
3343
|
+
});
|
|
3344
|
+
}
|
|
3345
|
+
var onMessageFromPanel = eventSource.observable;
|
|
3346
|
+
|
|
3347
|
+
// src/devtools/index.ts
|
|
3348
|
+
var VERSION = PKG_VERSION || "dev";
|
|
3349
|
+
var _devtoolsSetupHasRun = false;
|
|
3350
|
+
function setupDevTools(getAllRooms) {
|
|
3351
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3352
|
+
return;
|
|
3415
3353
|
}
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
this.#dirty = true;
|
|
3419
|
-
this.markSinksDirty();
|
|
3420
|
-
}
|
|
3354
|
+
if (_devtoolsSetupHasRun) {
|
|
3355
|
+
return;
|
|
3421
3356
|
}
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3357
|
+
_devtoolsSetupHasRun = true;
|
|
3358
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
3359
|
+
switch (msg.msg) {
|
|
3360
|
+
// When a devtool panel sends an explicit "connect" message back to this
|
|
3361
|
+
// live running client (in response to the "wake-up-devtools" message,
|
|
3362
|
+
// or when the devtool panel is opened for the first time), it means that it's okay to
|
|
3363
|
+
// start emitting messages.
|
|
3364
|
+
// Before this explicit acknowledgement, any call to sendToPanel() will
|
|
3365
|
+
// be a no-op.
|
|
3366
|
+
case "connect": {
|
|
3367
|
+
activateBridge(true);
|
|
3368
|
+
for (const roomId of getAllRooms()) {
|
|
3369
|
+
sendToPanel({
|
|
3370
|
+
msg: "room::available",
|
|
3371
|
+
roomId,
|
|
3372
|
+
clientVersion: VERSION
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
break;
|
|
3376
|
+
}
|
|
3425
3377
|
}
|
|
3426
|
-
|
|
3378
|
+
});
|
|
3379
|
+
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
3380
|
+
}
|
|
3381
|
+
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
3382
|
+
function stopSyncStream(roomId) {
|
|
3383
|
+
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
3384
|
+
unsubsByRoomId.delete(roomId);
|
|
3385
|
+
for (const unsub of unsubs) {
|
|
3386
|
+
unsub();
|
|
3427
3387
|
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3388
|
+
}
|
|
3389
|
+
function startSyncStream(room) {
|
|
3390
|
+
stopSyncStream(room.id);
|
|
3391
|
+
fullSync(room);
|
|
3392
|
+
unsubsByRoomId.set(room.id, [
|
|
3393
|
+
// When the connection status changes
|
|
3394
|
+
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
3395
|
+
// When storage initializes, send the update
|
|
3396
|
+
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
3397
|
+
// Any time storage updates, send the new storage root
|
|
3398
|
+
room.events.storageBatch.subscribe(() => partialSyncStorage(room)),
|
|
3399
|
+
// Any time "me" or "others" updates, send the new values accordingly
|
|
3400
|
+
room.events.self.subscribe(() => partialSyncMe(room)),
|
|
3401
|
+
room.events.others.subscribe(() => partialSyncOthers(room)),
|
|
3402
|
+
// Any time ydoc is updated, forward the update
|
|
3403
|
+
room.events.ydoc.subscribe((update) => syncYdocUpdate(room, update)),
|
|
3404
|
+
// Any time a custom room event is received, forward it
|
|
3405
|
+
room.events.customEvent.subscribe(
|
|
3406
|
+
(eventData) => forwardEvent(room, eventData)
|
|
3407
|
+
)
|
|
3408
|
+
]);
|
|
3409
|
+
}
|
|
3410
|
+
function syncYdocUpdate(room, update) {
|
|
3411
|
+
sendToPanel({
|
|
3412
|
+
msg: "room::sync::ydoc",
|
|
3413
|
+
roomId: room.id,
|
|
3414
|
+
update
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
var loadedAt = Date.now();
|
|
3418
|
+
var eventCounter = 0;
|
|
3419
|
+
function nextEventId() {
|
|
3420
|
+
return `event-${loadedAt}-${eventCounter++}`;
|
|
3421
|
+
}
|
|
3422
|
+
function forwardEvent(room, eventData) {
|
|
3423
|
+
sendToPanel({
|
|
3424
|
+
msg: "room::events::custom-event",
|
|
3425
|
+
roomId: room.id,
|
|
3426
|
+
event: {
|
|
3427
|
+
type: "CustomEvent",
|
|
3428
|
+
id: nextEventId(),
|
|
3429
|
+
key: "Event",
|
|
3430
|
+
connectionId: eventData.connectionId,
|
|
3431
|
+
payload: eventData.event
|
|
3441
3432
|
}
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
function partialSyncConnection(room) {
|
|
3436
|
+
sendToPanel({
|
|
3437
|
+
msg: "room::sync::partial",
|
|
3438
|
+
roomId: room.id,
|
|
3439
|
+
status: room.getStatus()
|
|
3440
|
+
});
|
|
3441
|
+
}
|
|
3442
|
+
function partialSyncStorage(room) {
|
|
3443
|
+
const root = room.getStorageSnapshot();
|
|
3444
|
+
if (root) {
|
|
3445
|
+
sendToPanel({
|
|
3446
|
+
msg: "room::sync::partial",
|
|
3447
|
+
roomId: room.id,
|
|
3448
|
+
storage: root.toTreeNode("root").payload
|
|
3449
|
+
});
|
|
3442
3450
|
}
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3451
|
+
}
|
|
3452
|
+
function partialSyncMe(room) {
|
|
3453
|
+
const me = room[kInternal].getSelf_forDevTools();
|
|
3454
|
+
if (me) {
|
|
3455
|
+
sendToPanel({
|
|
3456
|
+
msg: "room::sync::partial",
|
|
3457
|
+
roomId: room.id,
|
|
3458
|
+
me
|
|
3459
|
+
});
|
|
3449
3460
|
}
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3461
|
+
}
|
|
3462
|
+
function partialSyncOthers(room) {
|
|
3463
|
+
const others = room[kInternal].getOthers_forDevTools();
|
|
3464
|
+
if (others) {
|
|
3465
|
+
sendToPanel({
|
|
3466
|
+
msg: "room::sync::partial",
|
|
3467
|
+
roomId: room.id,
|
|
3468
|
+
others
|
|
3469
|
+
});
|
|
3453
3470
|
}
|
|
3454
|
-
|
|
3455
|
-
|
|
3471
|
+
}
|
|
3472
|
+
function fullSync(room) {
|
|
3473
|
+
const root = room.getStorageSnapshot();
|
|
3474
|
+
const me = room[kInternal].getSelf_forDevTools();
|
|
3475
|
+
const others = room[kInternal].getOthers_forDevTools();
|
|
3476
|
+
room.fetchYDoc("");
|
|
3477
|
+
sendToPanel({
|
|
3478
|
+
msg: "room::sync::full",
|
|
3479
|
+
roomId: room.id,
|
|
3480
|
+
status: room.getStatus(),
|
|
3481
|
+
storage: root?.toTreeNode("root").payload ?? null,
|
|
3482
|
+
me,
|
|
3483
|
+
others
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
3487
|
+
function stopRoomChannelListener(roomId) {
|
|
3488
|
+
const listener = roomChannelListeners.get(roomId);
|
|
3489
|
+
roomChannelListeners.delete(roomId);
|
|
3490
|
+
if (listener) {
|
|
3491
|
+
listener();
|
|
3456
3492
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3493
|
+
}
|
|
3494
|
+
function linkDevTools(roomId, room) {
|
|
3495
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
3498
|
+
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
3499
|
+
stopRoomChannelListener(roomId);
|
|
3500
|
+
roomChannelListeners.set(
|
|
3501
|
+
roomId,
|
|
3502
|
+
// Returns the unsubscribe callback, that we store in the
|
|
3503
|
+
// roomChannelListeners registry
|
|
3504
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
3505
|
+
switch (msg.msg) {
|
|
3506
|
+
// Sent by the devtool panel when it wants to receive the sync stream
|
|
3507
|
+
// for a room
|
|
3508
|
+
case "room::subscribe": {
|
|
3509
|
+
if (msg.roomId === roomId) {
|
|
3510
|
+
startSyncStream(room);
|
|
3511
|
+
}
|
|
3512
|
+
break;
|
|
3513
|
+
}
|
|
3514
|
+
case "room::unsubscribe": {
|
|
3515
|
+
if (msg.roomId === roomId) {
|
|
3516
|
+
stopSyncStream(roomId);
|
|
3517
|
+
}
|
|
3518
|
+
break;
|
|
3519
|
+
}
|
|
3473
3520
|
}
|
|
3474
|
-
})
|
|
3521
|
+
})
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3524
|
+
function unlinkDevTools(roomId) {
|
|
3525
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
3526
|
+
return;
|
|
3475
3527
|
}
|
|
3476
|
-
|
|
3528
|
+
stopSyncStream(roomId);
|
|
3529
|
+
stopRoomChannelListener(roomId);
|
|
3530
|
+
sendToPanel({
|
|
3531
|
+
msg: "room::unavailable",
|
|
3532
|
+
roomId
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3477
3535
|
|
|
3478
3536
|
// src/lib/position.ts
|
|
3479
3537
|
var MIN_CODE = 32;
|
|
@@ -6177,6 +6235,83 @@ var ManagedOthers = class {
|
|
|
6177
6235
|
}
|
|
6178
6236
|
};
|
|
6179
6237
|
|
|
6238
|
+
// src/types/LiveblocksError.ts
|
|
6239
|
+
var LiveblocksError = class _LiveblocksError extends Error {
|
|
6240
|
+
context;
|
|
6241
|
+
constructor(message, context, cause) {
|
|
6242
|
+
super(message, { cause });
|
|
6243
|
+
this.context = context;
|
|
6244
|
+
this.name = "LiveblocksError";
|
|
6245
|
+
}
|
|
6246
|
+
/** Convenience accessor for error.context.roomId (if available) */
|
|
6247
|
+
get roomId() {
|
|
6248
|
+
return this.context.roomId;
|
|
6249
|
+
}
|
|
6250
|
+
/** @deprecated Prefer using `context.code` instead, to enable type narrowing */
|
|
6251
|
+
get code() {
|
|
6252
|
+
return this.context.code;
|
|
6253
|
+
}
|
|
6254
|
+
/**
|
|
6255
|
+
* Creates a LiveblocksError from a generic error, by attaching Liveblocks
|
|
6256
|
+
* contextual information like room ID, thread ID, etc.
|
|
6257
|
+
*/
|
|
6258
|
+
static from(context, cause) {
|
|
6259
|
+
return new _LiveblocksError(
|
|
6260
|
+
defaultMessageFromContext(context),
|
|
6261
|
+
context,
|
|
6262
|
+
cause
|
|
6263
|
+
);
|
|
6264
|
+
}
|
|
6265
|
+
};
|
|
6266
|
+
function defaultMessageFromContext(context) {
|
|
6267
|
+
switch (context.type) {
|
|
6268
|
+
case "ROOM_CONNECTION_ERROR": {
|
|
6269
|
+
switch (context.code) {
|
|
6270
|
+
case 4001:
|
|
6271
|
+
return "Not allowed to connect to the room";
|
|
6272
|
+
case 4005:
|
|
6273
|
+
return "Room is already full";
|
|
6274
|
+
case 4006:
|
|
6275
|
+
return "Kicked out of the room, because the room ID changed";
|
|
6276
|
+
default:
|
|
6277
|
+
return "Could not connect to the room";
|
|
6278
|
+
}
|
|
6279
|
+
}
|
|
6280
|
+
case "CREATE_THREAD_ERROR":
|
|
6281
|
+
return "Could not create new thread";
|
|
6282
|
+
case "DELETE_THREAD_ERROR":
|
|
6283
|
+
return "Could not delete thread";
|
|
6284
|
+
case "EDIT_THREAD_METADATA_ERROR":
|
|
6285
|
+
return "Could not edit thread metadata";
|
|
6286
|
+
case "MARK_THREAD_AS_RESOLVED_ERROR":
|
|
6287
|
+
return "Could not mark thread as resolved";
|
|
6288
|
+
case "MARK_THREAD_AS_UNRESOLVED_ERROR":
|
|
6289
|
+
return "Could not mark thread as unresolved";
|
|
6290
|
+
case "CREATE_COMMENT_ERROR":
|
|
6291
|
+
return "Could not create new comment";
|
|
6292
|
+
case "EDIT_COMMENT_ERROR":
|
|
6293
|
+
return "Could not edit comment";
|
|
6294
|
+
case "DELETE_COMMENT_ERROR":
|
|
6295
|
+
return "Could not delete comment";
|
|
6296
|
+
case "ADD_REACTION_ERROR":
|
|
6297
|
+
return "Could not add reaction";
|
|
6298
|
+
case "REMOVE_REACTION_ERROR":
|
|
6299
|
+
return "Could not remove reaction";
|
|
6300
|
+
case "MARK_INBOX_NOTIFICATION_AS_READ_ERROR":
|
|
6301
|
+
return "Could not mark inbox notification as read";
|
|
6302
|
+
case "DELETE_INBOX_NOTIFICATION_ERROR":
|
|
6303
|
+
return "Could not delete inbox notification";
|
|
6304
|
+
case "MARK_ALL_INBOX_NOTIFICATIONS_AS_READ_ERROR":
|
|
6305
|
+
return "Could not mark all inbox notifications as read";
|
|
6306
|
+
case "DELETE_ALL_INBOX_NOTIFICATIONS_ERROR":
|
|
6307
|
+
return "Could not delete all inbox notifications";
|
|
6308
|
+
case "UPDATE_NOTIFICATION_SETTINGS_ERROR":
|
|
6309
|
+
return "Could not update notification settings";
|
|
6310
|
+
default:
|
|
6311
|
+
return assertNever(context, "Unhandled case");
|
|
6312
|
+
}
|
|
6313
|
+
}
|
|
6314
|
+
|
|
6180
6315
|
// src/room.ts
|
|
6181
6316
|
var MAX_SOCKET_MESSAGE_SIZE = 1024 * 1024 - 1024;
|
|
6182
6317
|
function makeIdFactory(connectionId) {
|
|
@@ -6341,13 +6476,17 @@ function createRoom(options, config) {
|
|
|
6341
6476
|
managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent);
|
|
6342
6477
|
managedSocket.events.didConnect.subscribe(onDidConnect);
|
|
6343
6478
|
managedSocket.events.didDisconnect.subscribe(onDidDisconnect);
|
|
6344
|
-
managedSocket.events.
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6479
|
+
managedSocket.events.onConnectionError.subscribe(({ message, code }) => {
|
|
6480
|
+
const type = "ROOM_CONNECTION_ERROR";
|
|
6481
|
+
const err = new LiveblocksError(message, { type, code, roomId });
|
|
6482
|
+
const didNotify = config.errorEventSource.notify(err);
|
|
6483
|
+
if (!didNotify) {
|
|
6484
|
+
if (process.env.NODE_ENV !== "production") {
|
|
6485
|
+
error2(
|
|
6486
|
+
`Connection to websocket server closed. Reason: ${message} (code: ${code}).`
|
|
6487
|
+
);
|
|
6488
|
+
}
|
|
6349
6489
|
}
|
|
6350
|
-
eventHub.error.notify(err);
|
|
6351
6490
|
});
|
|
6352
6491
|
const pool = {
|
|
6353
6492
|
roomId: config.roomId,
|
|
@@ -6410,7 +6549,6 @@ function createRoom(options, config) {
|
|
|
6410
6549
|
self: makeEventSource(),
|
|
6411
6550
|
myPresence: makeEventSource(),
|
|
6412
6551
|
others: makeEventSource(),
|
|
6413
|
-
error: makeEventSource(),
|
|
6414
6552
|
storageBatch: makeEventSource(),
|
|
6415
6553
|
history: makeEventSource(),
|
|
6416
6554
|
storageDidLoad: makeEventSource(),
|
|
@@ -6889,6 +7027,7 @@ function createRoom(options, config) {
|
|
|
6889
7027
|
processInitialStorage(message);
|
|
6890
7028
|
break;
|
|
6891
7029
|
}
|
|
7030
|
+
// Write event
|
|
6892
7031
|
case 201 /* UPDATE_STORAGE */: {
|
|
6893
7032
|
const applyResult = applyOps(message.ops, false);
|
|
6894
7033
|
for (const [key, value] of applyResult.updates.storageUpdates) {
|
|
@@ -6899,6 +7038,11 @@ function createRoom(options, config) {
|
|
|
6899
7038
|
}
|
|
6900
7039
|
break;
|
|
6901
7040
|
}
|
|
7041
|
+
// Receiving a RejectedOps message in the client means that the server is no
|
|
7042
|
+
// longer in sync with the client. Trying to synchronize the client again by
|
|
7043
|
+
// rolling back particular Ops may be hard/impossible. It's fine to not try and
|
|
7044
|
+
// accept the out-of-sync reality and throw an error. We look at this kind of bug
|
|
7045
|
+
// as a developer-owned bug. In production, these errors are not expected to happen.
|
|
6902
7046
|
case 299 /* REJECT_STORAGE_OP */: {
|
|
6903
7047
|
errorWithTitle(
|
|
6904
7048
|
"Storage mutation rejection error",
|
|
@@ -7242,7 +7386,6 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
7242
7386
|
others: eventHub.others.observable,
|
|
7243
7387
|
self: eventHub.self.observable,
|
|
7244
7388
|
myPresence: eventHub.myPresence.observable,
|
|
7245
|
-
error: eventHub.error.observable,
|
|
7246
7389
|
/** @deprecated */
|
|
7247
7390
|
storage: eventHub.storageBatch.observable,
|
|
7248
7391
|
storageBatch: eventHub.storageBatch.observable,
|
|
@@ -7433,7 +7576,11 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
7433
7576
|
attachmentUrlsStore: httpClient.getOrCreateAttachmentUrlsStore(roomId)
|
|
7434
7577
|
},
|
|
7435
7578
|
id: config.roomId,
|
|
7436
|
-
subscribe: makeClassicSubscribeFn(
|
|
7579
|
+
subscribe: makeClassicSubscribeFn(
|
|
7580
|
+
config.roomId,
|
|
7581
|
+
events,
|
|
7582
|
+
config.errorEventSource
|
|
7583
|
+
),
|
|
7437
7584
|
connect: () => managedSocket.connect(),
|
|
7438
7585
|
reconnect: () => managedSocket.reconnect(),
|
|
7439
7586
|
disconnect: () => managedSocket.disconnect(),
|
|
@@ -7502,7 +7649,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
7502
7649
|
{ enumerable: false }
|
|
7503
7650
|
);
|
|
7504
7651
|
}
|
|
7505
|
-
function makeClassicSubscribeFn(events) {
|
|
7652
|
+
function makeClassicSubscribeFn(roomId, events, errorEvents) {
|
|
7506
7653
|
function subscribeToLiveStructureDeeply(node, callback) {
|
|
7507
7654
|
return events.storageBatch.subscribe((updates) => {
|
|
7508
7655
|
const relatedUpdates = updates.filter(
|
|
@@ -7542,8 +7689,13 @@ function makeClassicSubscribeFn(events) {
|
|
|
7542
7689
|
return cb(others, internalEvent);
|
|
7543
7690
|
});
|
|
7544
7691
|
}
|
|
7545
|
-
case "error":
|
|
7546
|
-
return
|
|
7692
|
+
case "error": {
|
|
7693
|
+
return errorEvents.subscribe((err) => {
|
|
7694
|
+
if (err.roomId === roomId) {
|
|
7695
|
+
return callback(err);
|
|
7696
|
+
}
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7547
7699
|
case "status":
|
|
7548
7700
|
return events.status.subscribe(callback);
|
|
7549
7701
|
case "lost-connection":
|
|
@@ -7560,6 +7712,7 @@ function makeClassicSubscribeFn(events) {
|
|
|
7560
7712
|
return events.comments.subscribe(
|
|
7561
7713
|
callback
|
|
7562
7714
|
);
|
|
7715
|
+
// istanbul ignore next
|
|
7563
7716
|
default:
|
|
7564
7717
|
return assertNever(
|
|
7565
7718
|
first,
|
|
@@ -7714,6 +7867,7 @@ function createClient(options) {
|
|
|
7714
7867
|
},
|
|
7715
7868
|
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
7716
7869
|
baseUrl,
|
|
7870
|
+
errorEventSource: liveblocksErrorSource,
|
|
7717
7871
|
unstable_fallbackToHTTP: !!clientOptions.unstable_fallbackToHTTP,
|
|
7718
7872
|
unstable_streamData: !!clientOptions.unstable_streamData,
|
|
7719
7873
|
roomHttpClient: httpClient,
|
|
@@ -7796,6 +7950,7 @@ function createClient(options) {
|
|
|
7796
7950
|
}
|
|
7797
7951
|
const syncStatusSources = [];
|
|
7798
7952
|
const syncStatusSignal = new Signal("synchronized");
|
|
7953
|
+
const liveblocksErrorSource = makeEventSource();
|
|
7799
7954
|
function getSyncStatus() {
|
|
7800
7955
|
const status = syncStatusSignal.get();
|
|
7801
7956
|
return status === "synchronizing" ? status : "synchronized";
|
|
@@ -7855,6 +8010,7 @@ function createClient(options) {
|
|
|
7855
8010
|
},
|
|
7856
8011
|
getSyncStatus,
|
|
7857
8012
|
events: {
|
|
8013
|
+
error: liveblocksErrorSource,
|
|
7858
8014
|
syncStatus: syncStatusSignal
|
|
7859
8015
|
},
|
|
7860
8016
|
// Internal
|
|
@@ -7870,7 +8026,14 @@ function createClient(options) {
|
|
|
7870
8026
|
httpClient,
|
|
7871
8027
|
// Type-level helper only, it's effectively only an identity-function at runtime
|
|
7872
8028
|
as: () => client,
|
|
7873
|
-
createSyncSource
|
|
8029
|
+
createSyncSource,
|
|
8030
|
+
emitError: (context, cause) => {
|
|
8031
|
+
const error3 = LiveblocksError.from(context, cause);
|
|
8032
|
+
const didNotify = liveblocksErrorSource.notify(error3);
|
|
8033
|
+
if (!didNotify) {
|
|
8034
|
+
error2(error3.message);
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
7874
8037
|
}
|
|
7875
8038
|
},
|
|
7876
8039
|
kInternal,
|
|
@@ -8842,11 +9005,13 @@ export {
|
|
|
8842
9005
|
ClientMsgCode,
|
|
8843
9006
|
CommentsApiError,
|
|
8844
9007
|
CrdtType,
|
|
9008
|
+
DefaultMap,
|
|
8845
9009
|
DerivedSignal,
|
|
8846
9010
|
HttpError,
|
|
8847
9011
|
LiveList,
|
|
8848
9012
|
LiveMap,
|
|
8849
9013
|
LiveObject,
|
|
9014
|
+
LiveblocksError,
|
|
8850
9015
|
MutableSignal,
|
|
8851
9016
|
NotificationsApiError,
|
|
8852
9017
|
OpCode,
|
|
@@ -8896,6 +9061,7 @@ export {
|
|
|
8896
9061
|
isLiveNode,
|
|
8897
9062
|
isPlainObject,
|
|
8898
9063
|
isRootCrdt,
|
|
9064
|
+
isStartsWithOperator,
|
|
8899
9065
|
kInternal,
|
|
8900
9066
|
legacy_patchImmutableObject,
|
|
8901
9067
|
lsonToJson,
|