@liveblocks/core 2.15.1 → 2.16.0-toolbars1

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.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.15.1";
9
+ var PKG_VERSION = "2.16.0-toolbars1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -170,9 +170,6 @@ var errorWithTitle = wrapWithTitle("error");
170
170
  function raise(msg) {
171
171
  throw new Error(msg);
172
172
  }
173
- function isPlainObject(blob) {
174
- return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
175
- }
176
173
  function entries(obj) {
177
174
  return Object.entries(obj);
178
175
  }
@@ -385,19 +382,319 @@ function makeBufferableEventSource() {
385
382
  };
386
383
  }
387
384
 
385
+ // src/lib/freeze.ts
386
+ var freeze = process.env.NODE_ENV === "production" ? (
387
+ /* istanbul ignore next */
388
+ (x) => x
389
+ ) : Object.freeze;
390
+
391
+ // src/lib/signals.ts
392
+ var kSinks = Symbol("kSinks");
393
+ var kTrigger = Symbol("kTrigger");
394
+ var signalsToTrigger = null;
395
+ var trackedReads = null;
396
+ function batch(callback) {
397
+ if (signalsToTrigger !== null) {
398
+ callback();
399
+ return;
400
+ }
401
+ signalsToTrigger = /* @__PURE__ */ new Set();
402
+ try {
403
+ callback();
404
+ } finally {
405
+ for (const signal of signalsToTrigger) {
406
+ signal[kTrigger]();
407
+ }
408
+ signalsToTrigger = null;
409
+ }
410
+ }
411
+ function enqueueTrigger(signal) {
412
+ if (!signalsToTrigger) raise("Expected to be in an active batch");
413
+ signalsToTrigger.add(signal);
414
+ }
415
+ function merge(target, patch) {
416
+ let updated = false;
417
+ const newValue = { ...target };
418
+ Object.keys(patch).forEach((k) => {
419
+ const key = k;
420
+ const val = patch[key];
421
+ if (newValue[key] !== val) {
422
+ if (val === void 0) {
423
+ delete newValue[key];
424
+ } else {
425
+ newValue[key] = val;
426
+ }
427
+ updated = true;
428
+ }
429
+ });
430
+ return updated ? newValue : target;
431
+ }
432
+ var AbstractSignal = class {
433
+ /** @internal */
434
+ equals;
435
+ #eventSource;
436
+ /** @internal */
437
+ [kSinks];
438
+ constructor(equals) {
439
+ this.equals = equals ?? Object.is;
440
+ this.#eventSource = makeEventSource();
441
+ this[kSinks] = /* @__PURE__ */ new Set();
442
+ this.get = this.get.bind(this);
443
+ this.subscribe = this.subscribe.bind(this);
444
+ this.subscribeOnce = this.subscribeOnce.bind(this);
445
+ }
446
+ [Symbol.dispose]() {
447
+ this.#eventSource[Symbol.dispose]();
448
+ this.#eventSource = "(disposed)";
449
+ this.equals = "(disposed)";
450
+ }
451
+ get hasWatchers() {
452
+ if (this.#eventSource.count() > 0) return true;
453
+ for (const sink of this[kSinks]) {
454
+ if (sink.hasWatchers) {
455
+ return true;
456
+ }
457
+ }
458
+ return false;
459
+ }
460
+ [kTrigger]() {
461
+ this.#eventSource.notify();
462
+ for (const sink of this[kSinks]) {
463
+ enqueueTrigger(sink);
464
+ }
465
+ }
466
+ subscribe(callback) {
467
+ if (this.#eventSource.count() === 0) {
468
+ this.get();
469
+ }
470
+ return this.#eventSource.subscribe(callback);
471
+ }
472
+ subscribeOnce(callback) {
473
+ const unsub = this.subscribe(() => {
474
+ unsub();
475
+ return callback();
476
+ });
477
+ return unsub;
478
+ }
479
+ waitUntil() {
480
+ throw new Error("waitUntil not supported on Signals");
481
+ }
482
+ markSinksDirty() {
483
+ for (const sink of this[kSinks]) {
484
+ sink.markDirty();
485
+ }
486
+ }
487
+ addSink(sink) {
488
+ this[kSinks].add(sink);
489
+ }
490
+ removeSink(sink) {
491
+ this[kSinks].delete(sink);
492
+ }
493
+ asReadonly() {
494
+ return this;
495
+ }
496
+ };
497
+ var Signal = class extends AbstractSignal {
498
+ #value;
499
+ constructor(value, equals) {
500
+ super(equals);
501
+ this.#value = freeze(value);
502
+ }
503
+ [Symbol.dispose]() {
504
+ super[Symbol.dispose]();
505
+ this.#value = "(disposed)";
506
+ }
507
+ get() {
508
+ trackedReads?.add(this);
509
+ return this.#value;
510
+ }
511
+ set(newValue) {
512
+ batch(() => {
513
+ if (typeof newValue === "function") {
514
+ newValue = newValue(this.#value);
515
+ }
516
+ if (!this.equals(this.#value, newValue)) {
517
+ this.#value = freeze(newValue);
518
+ this.markSinksDirty();
519
+ enqueueTrigger(this);
520
+ }
521
+ });
522
+ }
523
+ };
524
+ var PatchableSignal = class extends Signal {
525
+ constructor(data) {
526
+ super(freeze(compactObject(data)));
527
+ }
528
+ set() {
529
+ throw new Error("Don't call .set() directly, use .patch()");
530
+ }
531
+ /**
532
+ * Patches the current object.
533
+ */
534
+ patch(patch) {
535
+ super.set((old) => merge(old, patch));
536
+ }
537
+ };
538
+ var INITIAL = Symbol();
539
+ var DerivedSignal = class _DerivedSignal extends AbstractSignal {
540
+ #prevValue;
541
+ #dirty;
542
+ // When true, the value in #value may not be up-to-date and needs re-checking
543
+ #sources;
544
+ #deps;
545
+ #transform;
546
+ // prettier-ignore
547
+ static from(...args) {
548
+ const last = args.pop();
549
+ if (typeof last !== "function")
550
+ raise("Invalid .from() call, last argument expected to be a function");
551
+ if (typeof args[args.length - 1] === "function") {
552
+ const equals = last;
553
+ const transform = args.pop();
554
+ return new _DerivedSignal(args, transform, equals);
555
+ } else {
556
+ const transform = last;
557
+ return new _DerivedSignal(args, transform);
558
+ }
559
+ }
560
+ constructor(deps, transform, equals) {
561
+ super(equals);
562
+ this.#dirty = true;
563
+ this.#prevValue = INITIAL;
564
+ this.#deps = deps;
565
+ this.#sources = /* @__PURE__ */ new Set();
566
+ this.#transform = transform;
567
+ }
568
+ [Symbol.dispose]() {
569
+ for (const src of this.#sources) {
570
+ src.removeSink(this);
571
+ }
572
+ this.#prevValue = "(disposed)";
573
+ this.#sources = "(disposed)";
574
+ this.#deps = "(disposed)";
575
+ this.#transform = "(disposed)";
576
+ }
577
+ get isDirty() {
578
+ return this.#dirty;
579
+ }
580
+ #recompute() {
581
+ const oldTrackedReads = trackedReads;
582
+ let derived;
583
+ trackedReads = /* @__PURE__ */ new Set();
584
+ try {
585
+ derived = this.#transform(...this.#deps.map((p) => p.get()));
586
+ } finally {
587
+ const oldSources = this.#sources;
588
+ this.#sources = /* @__PURE__ */ new Set();
589
+ for (const sig of trackedReads) {
590
+ this.#sources.add(sig);
591
+ oldSources.delete(sig);
592
+ }
593
+ for (const oldSource of oldSources) {
594
+ oldSource.removeSink(this);
595
+ }
596
+ for (const newSource of this.#sources) {
597
+ newSource.addSink(this);
598
+ }
599
+ trackedReads = oldTrackedReads;
600
+ }
601
+ this.#dirty = false;
602
+ if (!this.equals(this.#prevValue, derived)) {
603
+ this.#prevValue = derived;
604
+ return true;
605
+ }
606
+ return false;
607
+ }
608
+ markDirty() {
609
+ if (!this.#dirty) {
610
+ this.#dirty = true;
611
+ this.markSinksDirty();
612
+ }
613
+ }
614
+ get() {
615
+ if (this.#dirty) {
616
+ this.#recompute();
617
+ }
618
+ trackedReads?.add(this);
619
+ return this.#prevValue;
620
+ }
621
+ /**
622
+ * Called by the Signal system if one or more of the dependent signals have
623
+ * changed. In the case of a DerivedSignal, we'll only want to re-evaluate
624
+ * the actual value if it's being watched, or any of their sinks are being
625
+ * watched actively.
626
+ */
627
+ [kTrigger]() {
628
+ if (!this.hasWatchers) {
629
+ return;
630
+ }
631
+ const updated = this.#recompute();
632
+ if (updated) {
633
+ super[kTrigger]();
634
+ }
635
+ }
636
+ };
637
+ var MutableSignal = class extends AbstractSignal {
638
+ #state;
639
+ constructor(initialState) {
640
+ super();
641
+ this.#state = initialState;
642
+ }
643
+ [Symbol.dispose]() {
644
+ super[Symbol.dispose]();
645
+ this.#state = "(disposed)";
646
+ }
647
+ get() {
648
+ trackedReads?.add(this);
649
+ return this.#state;
650
+ }
651
+ /**
652
+ * Invokes a callback function that is allowed to mutate the given state
653
+ * value. Do not change the value outside of the callback.
654
+ *
655
+ * If the callback explicitly returns `false`, it's assumed that the state
656
+ * was not changed.
657
+ */
658
+ mutate(callback) {
659
+ batch(() => {
660
+ const result = callback ? callback(this.#state) : true;
661
+ if (result !== null && typeof result === "object" && "then" in result) {
662
+ raise("MutableSignal.mutate() does not support async callbacks");
663
+ }
664
+ if (result !== false) {
665
+ this.markSinksDirty();
666
+ enqueueTrigger(this);
667
+ }
668
+ });
669
+ }
670
+ };
671
+
388
672
  // src/lib/stringify.ts
389
- function stringify(object, ...args) {
390
- if (typeof object !== "object" || object === null || Array.isArray(object)) {
391
- return JSON.stringify(object, ...args);
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);
673
+ var EXPLICIT_UNDEFINED_PLACEHOLDER = "_explicit_undefined";
674
+ function replacer(_key, value) {
675
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).sort().reduce((sorted, key) => {
676
+ sorted[key] = value[key];
677
+ return sorted;
678
+ }, {}) : value === void 0 ? EXPLICIT_UNDEFINED_PLACEHOLDER : value;
679
+ }
680
+ function reviver(key, value) {
681
+ if (!key && value === EXPLICIT_UNDEFINED_PLACEHOLDER) {
682
+ return void 0;
683
+ }
684
+ if (value && typeof value === "object") {
685
+ for (const k in value) {
686
+ if (value[k] === EXPLICIT_UNDEFINED_PLACEHOLDER) {
687
+ Object.defineProperty(value, k, { value: void 0 });
688
+ }
689
+ }
690
+ }
691
+ return value;
692
+ }
693
+ function stringify(value) {
694
+ return JSON.stringify(value, replacer);
695
+ }
696
+ function unstringify(value) {
697
+ return JSON.parse(value, reviver);
401
698
  }
402
699
 
403
700
  // src/lib/batch.ts
@@ -492,57 +789,58 @@ var Batch = class {
492
789
  }
493
790
  };
494
791
  function createBatchStore(batch2) {
495
- const cache = /* @__PURE__ */ new Map();
496
- const eventSource2 = makeEventSource();
792
+ const signal = new MutableSignal(/* @__PURE__ */ new Map());
497
793
  function getCacheKey(args) {
498
794
  return stringify(args);
499
795
  }
500
- function setStateAndNotify(cacheKey, state) {
501
- cache.set(cacheKey, state);
502
- eventSource2.notify();
796
+ function update(cacheKey, state) {
797
+ signal.mutate((cache) => {
798
+ cache.set(cacheKey, state);
799
+ });
503
800
  }
504
801
  function invalidate(inputs) {
505
- if (Array.isArray(inputs)) {
506
- for (const input of inputs) {
507
- cache.delete(getCacheKey(input));
802
+ signal.mutate((cache) => {
803
+ if (Array.isArray(inputs)) {
804
+ for (const input of inputs) {
805
+ cache.delete(getCacheKey(input));
806
+ }
807
+ } else {
808
+ cache.clear();
508
809
  }
509
- } else {
510
- cache.clear();
511
- }
512
- eventSource2.notify();
810
+ });
513
811
  }
514
- async function get(input) {
812
+ async function enqueue(input) {
515
813
  const cacheKey = getCacheKey(input);
814
+ const cache = signal.get();
516
815
  if (cache.has(cacheKey)) {
517
816
  return;
518
817
  }
519
818
  try {
520
- setStateAndNotify(cacheKey, { isLoading: true });
819
+ update(cacheKey, { isLoading: true });
521
820
  const result = await batch2.get(input);
522
- setStateAndNotify(cacheKey, { isLoading: false, data: result });
821
+ update(cacheKey, { isLoading: false, data: result });
523
822
  } catch (error3) {
524
- setStateAndNotify(cacheKey, {
823
+ update(cacheKey, {
525
824
  isLoading: false,
526
825
  error: error3
527
826
  });
528
827
  }
529
828
  }
530
- function getState(input) {
829
+ function getItemState(input) {
531
830
  const cacheKey = getCacheKey(input);
831
+ const cache = signal.get();
532
832
  return cache.get(cacheKey);
533
833
  }
534
834
  function _cacheKeys() {
835
+ const cache = signal.get();
535
836
  return [...cache.keys()];
536
837
  }
537
- function getBatch() {
538
- return batch2;
539
- }
540
838
  return {
541
- ...eventSource2.observable,
542
- get,
543
- getState,
839
+ subscribe: signal.subscribe,
840
+ enqueue,
841
+ getItemState,
544
842
  invalidate,
545
- getBatch,
843
+ batch: batch2,
546
844
  _cacheKeys
547
845
  };
548
846
  }
@@ -583,6 +881,44 @@ function createInboxNotificationId() {
583
881
  return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
584
882
  }
585
883
 
884
+ // src/lib/DefaultMap.ts
885
+ var DefaultMap = class extends Map {
886
+ #defaultFn;
887
+ /**
888
+ * If the default function is not provided to the constructor, it has to be
889
+ * provided in each .getOrCreate() call individually.
890
+ */
891
+ constructor(defaultFn, entries2) {
892
+ super(entries2);
893
+ this.#defaultFn = defaultFn;
894
+ }
895
+ /**
896
+ * Gets the value at the given key, or creates it.
897
+ *
898
+ * Difference from normal Map: if the key does not exist, it will be created
899
+ * on the fly using the factory function, and that value will get returned
900
+ * instead of `undefined`.
901
+ */
902
+ getOrCreate(key, defaultFn) {
903
+ if (super.has(key)) {
904
+ return super.get(key);
905
+ } else {
906
+ const fn = defaultFn ?? this.#defaultFn ?? raise("DefaultMap used without a factory function");
907
+ const value = fn(key);
908
+ this.set(key, value);
909
+ return value;
910
+ }
911
+ }
912
+ };
913
+
914
+ // src/lib/guards.ts
915
+ function isPlainObject(blob) {
916
+ return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
917
+ }
918
+ function isStartsWithOperator(blob) {
919
+ return isPlainObject(blob) && typeof blob.startsWith === "string";
920
+ }
921
+
586
922
  // src/lib/objectToQuery.ts
587
923
  var identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
588
924
  function objectToQuery(obj) {
@@ -597,10 +933,12 @@ function objectToQuery(obj) {
597
933
  }
598
934
  if (isSimpleValue(value)) {
599
935
  keyValuePairs.push([key, value]);
600
- } else if (isValueWithOperator(value)) {
601
- keyValuePairsWithOperator.push([key, value]);
602
- } else if (typeof value === "object" && !("startsWith" in value)) {
603
- indexedKeys.push([key, value]);
936
+ } else if (isPlainObject(value)) {
937
+ if (isStartsWithOperator(value)) {
938
+ keyValuePairsWithOperator.push([key, value]);
939
+ } else {
940
+ indexedKeys.push([key, value]);
941
+ }
604
942
  }
605
943
  });
606
944
  filterList = [
@@ -617,7 +955,7 @@ function objectToQuery(obj) {
617
955
  }
618
956
  if (isSimpleValue(nestedValue)) {
619
957
  nKeyValuePairs.push([formatFilterKey(key, nestedKey), nestedValue]);
620
- } else if (isValueWithOperator(nestedValue)) {
958
+ } else if (isStartsWithOperator(nestedValue)) {
621
959
  nKeyValuePairsWithOperator.push([
622
960
  formatFilterKey(key, nestedKey),
623
961
  nestedValue
@@ -659,16 +997,7 @@ var getFiltersFromKeyValuePairsWithOperator = (keyValuePairsWithOperator) => {
659
997
  return filters;
660
998
  };
661
999
  var isSimpleValue = (value) => {
662
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
663
- return true;
664
- }
665
- return false;
666
- };
667
- var isValueWithOperator = (value) => {
668
- if (typeof value === "object" && value !== null && "startsWith" in value) {
669
- return true;
670
- }
671
- return false;
1000
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
672
1001
  };
673
1002
  var formatFilter = (key, operator, value) => {
674
1003
  return `${key}${operator}${value}`;
@@ -1083,40 +1412,31 @@ function createApiClient({
1083
1412
  }
1084
1413
  }
1085
1414
  }
1086
- const getAttachmentUrlsBatchStoreByRoom = /* @__PURE__ */ new Map();
1415
+ const attachmentUrlsBatchStoresByRoom = new DefaultMap((roomId) => {
1416
+ const batch2 = new Batch(
1417
+ async (batchedAttachmentIds) => {
1418
+ const attachmentIds = batchedAttachmentIds.flat();
1419
+ const { urls } = await httpClient.post(
1420
+ url`/v2/c/rooms/${roomId}/attachments/presigned-urls`,
1421
+ await authManager.getAuthValue({
1422
+ requestedScope: "comments:read",
1423
+ roomId
1424
+ }),
1425
+ { attachmentIds }
1426
+ );
1427
+ return urls.map(
1428
+ (url2) => url2 ?? new Error("There was an error while getting this attachment's URL")
1429
+ );
1430
+ },
1431
+ { delay: 50 }
1432
+ );
1433
+ return createBatchStore(batch2);
1434
+ });
1087
1435
  function getOrCreateAttachmentUrlsStore(roomId) {
1088
- let store = getAttachmentUrlsBatchStoreByRoom.get(roomId);
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;
1436
+ return attachmentUrlsBatchStoresByRoom.getOrCreate(roomId);
1117
1437
  }
1118
1438
  function getAttachmentUrl(options) {
1119
- const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).getBatch();
1439
+ const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).batch;
1120
1440
  return batch2.get(options.attachmentId);
1121
1441
  }
1122
1442
  async function getNotificationSettings(options) {
@@ -1142,33 +1462,25 @@ function createApiClient({
1142
1462
  options.settings
1143
1463
  );
1144
1464
  }
1145
- const markInboxNotificationsAsReadBatchByRoom = /* @__PURE__ */ new Map();
1146
- function getOrCreateMarkInboxNotificationsAsReadBatch(roomId) {
1147
- let batch2 = markInboxNotificationsAsReadBatchByRoom.get(roomId);
1148
- if (batch2 === void 0) {
1149
- batch2 = new Batch(
1150
- async (batchedInboxNotificationIds) => {
1151
- const inboxNotificationIds = batchedInboxNotificationIds.flat();
1152
- await httpClient.post(
1153
- url`/v2/c/rooms/${roomId}/inbox-notifications/read`,
1154
- await authManager.getAuthValue({
1155
- requestedScope: "comments:read",
1156
- roomId
1157
- }),
1158
- { inboxNotificationIds }
1159
- );
1160
- return inboxNotificationIds;
1161
- },
1162
- {
1163
- delay: 50
1164
- }
1165
- );
1166
- markInboxNotificationsAsReadBatchByRoom.set(roomId, batch2);
1167
- }
1168
- return batch2;
1169
- }
1465
+ const markAsReadBatchesByRoom = new DefaultMap(
1466
+ (roomId) => new Batch(
1467
+ async (batchedInboxNotificationIds) => {
1468
+ const inboxNotificationIds = batchedInboxNotificationIds.flat();
1469
+ await httpClient.post(
1470
+ url`/v2/c/rooms/${roomId}/inbox-notifications/read`,
1471
+ await authManager.getAuthValue({
1472
+ requestedScope: "comments:read",
1473
+ roomId
1474
+ }),
1475
+ { inboxNotificationIds }
1476
+ );
1477
+ return inboxNotificationIds;
1478
+ },
1479
+ { delay: 50 }
1480
+ )
1481
+ );
1170
1482
  async function markRoomInboxNotificationAsRead(options) {
1171
- const batch2 = getOrCreateMarkInboxNotificationsAsReadBatch(options.roomId);
1483
+ const batch2 = markAsReadBatchesByRoom.getOrCreate(options.roomId);
1172
1484
  return batch2.get(options.inboxNotificationId);
1173
1485
  }
1174
1486
  async function createTextMention(options) {
@@ -2166,6 +2478,7 @@ function toNewConnectionStatus(machine) {
2166
2478
  return machine.context.successCount > 0 ? "reconnecting" : "connecting";
2167
2479
  case "@idle.failed":
2168
2480
  return "disconnected";
2481
+ // istanbul ignore next
2169
2482
  default:
2170
2483
  return assertNever(state, "Unknown state");
2171
2484
  }
@@ -3046,6 +3359,12 @@ function setupDevTools(getAllRooms) {
3046
3359
  _devtoolsSetupHasRun = true;
3047
3360
  onMessageFromPanel.subscribe((msg) => {
3048
3361
  switch (msg.msg) {
3362
+ // When a devtool panel sends an explicit "connect" message back to this
3363
+ // live running client (in response to the "wake-up-devtools" message,
3364
+ // or when the devtool panel is opened for the first time), it means that it's okay to
3365
+ // start emitting messages.
3366
+ // Before this explicit acknowledgement, any call to sendToPanel() will
3367
+ // be a no-op.
3049
3368
  case "connect": {
3050
3369
  activateBridge(true);
3051
3370
  for (const roomId of getAllRooms()) {
@@ -3186,6 +3505,8 @@ function linkDevTools(roomId, room) {
3186
3505
  // roomChannelListeners registry
3187
3506
  onMessageFromPanel.subscribe((msg) => {
3188
3507
  switch (msg.msg) {
3508
+ // Sent by the devtool panel when it wants to receive the sync stream
3509
+ // for a room
3189
3510
  case "room::subscribe": {
3190
3511
  if (msg.roomId === roomId) {
3191
3512
  startSyncStream(room);
@@ -3214,267 +3535,6 @@ function unlinkDevTools(roomId) {
3214
3535
  });
3215
3536
  }
3216
3537
 
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;
3413
- }
3414
- return false;
3415
- }
3416
- markDirty() {
3417
- if (!this.#dirty) {
3418
- this.#dirty = true;
3419
- this.markSinksDirty();
3420
- }
3421
- }
3422
- get() {
3423
- if (this.#dirty) {
3424
- this.#recompute();
3425
- }
3426
- return this.#prevValue;
3427
- }
3428
- /**
3429
- * Called by the Signal system if one or more of the dependent signals have
3430
- * changed. In the case of a DerivedSignal, we'll only want to re-evaluate
3431
- * the actual value if it's being watched, or any of their sinks are being
3432
- * watched actively.
3433
- */
3434
- [kTrigger]() {
3435
- if (!this.hasWatchers) {
3436
- return;
3437
- }
3438
- const updated = this.#recompute();
3439
- if (updated) {
3440
- super[kTrigger]();
3441
- }
3442
- }
3443
- };
3444
- var MutableSignal = class extends AbstractSignal {
3445
- #state;
3446
- constructor(initialState) {
3447
- super();
3448
- this.#state = initialState;
3449
- }
3450
- [Symbol.dispose]() {
3451
- super[Symbol.dispose]();
3452
- this.#state = "(disposed)";
3453
- }
3454
- get() {
3455
- return this.#state;
3456
- }
3457
- /**
3458
- * Invokes a callback function that is allowed to mutate the given state
3459
- * value. Do not change the value outside of the callback.
3460
- *
3461
- * If the callback explicitly returns `false`, it's assumed that the state
3462
- * was not changed.
3463
- */
3464
- mutate(callback) {
3465
- batch(() => {
3466
- const result = callback ? callback(this.#state) : true;
3467
- if (result !== null && typeof result === "object" && "then" in result) {
3468
- raise("MutableSignal.mutate() does not support async callbacks");
3469
- }
3470
- if (result !== false) {
3471
- this.markSinksDirty();
3472
- enqueueTrigger(this);
3473
- }
3474
- });
3475
- }
3476
- };
3477
-
3478
3538
  // src/lib/position.ts
3479
3539
  var MIN_CODE = 32;
3480
3540
  var MAX_CODE = 126;
@@ -6889,6 +6949,7 @@ function createRoom(options, config) {
6889
6949
  processInitialStorage(message);
6890
6950
  break;
6891
6951
  }
6952
+ // Write event
6892
6953
  case 201 /* UPDATE_STORAGE */: {
6893
6954
  const applyResult = applyOps(message.ops, false);
6894
6955
  for (const [key, value] of applyResult.updates.storageUpdates) {
@@ -6899,6 +6960,11 @@ function createRoom(options, config) {
6899
6960
  }
6900
6961
  break;
6901
6962
  }
6963
+ // Receiving a RejectedOps message in the client means that the server is no
6964
+ // longer in sync with the client. Trying to synchronize the client again by
6965
+ // rolling back particular Ops may be hard/impossible. It's fine to not try and
6966
+ // accept the out-of-sync reality and throw an error. We look at this kind of bug
6967
+ // as a developer-owned bug. In production, these errors are not expected to happen.
6902
6968
  case 299 /* REJECT_STORAGE_OP */: {
6903
6969
  errorWithTitle(
6904
6970
  "Storage mutation rejection error",
@@ -7560,6 +7626,7 @@ function makeClassicSubscribeFn(events) {
7560
7626
  return events.comments.subscribe(
7561
7627
  callback
7562
7628
  );
7629
+ // istanbul ignore next
7563
7630
  default:
7564
7631
  return assertNever(
7565
7632
  first,
@@ -8842,6 +8909,7 @@ export {
8842
8909
  ClientMsgCode,
8843
8910
  CommentsApiError,
8844
8911
  CrdtType,
8912
+ DefaultMap,
8845
8913
  DerivedSignal,
8846
8914
  HttpError,
8847
8915
  LiveList,
@@ -8896,6 +8964,7 @@ export {
8896
8964
  isLiveNode,
8897
8965
  isPlainObject,
8898
8966
  isRootCrdt,
8967
+ isStartsWithOperator,
8899
8968
  kInternal,
8900
8969
  legacy_patchImmutableObject,
8901
8970
  lsonToJson,
@@ -8917,6 +8986,7 @@ export {
8917
8986
  toAbsoluteUrl,
8918
8987
  toPlainLson,
8919
8988
  tryParseJson,
8989
+ unstringify,
8920
8990
  url,
8921
8991
  urljoin,
8922
8992
  wait,