@liveblocks/core 2.11.0 → 2.12.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.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.11.0";
9
+ var PKG_VERSION = "2.12.0-rc1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -2444,6 +2444,84 @@ function createNotificationsApi({
2444
2444
  };
2445
2445
  }
2446
2446
 
2447
+ // src/lib/freeze.ts
2448
+ var freeze = process.env.NODE_ENV === "production" ? (
2449
+ /* istanbul ignore next */
2450
+ (x) => x
2451
+ ) : Object.freeze;
2452
+
2453
+ // src/refs/ImmutableRef.ts
2454
+ function merge(target, patch) {
2455
+ let updated = false;
2456
+ const newValue = { ...target };
2457
+ Object.keys(patch).forEach((k) => {
2458
+ const key = k;
2459
+ const val = patch[key];
2460
+ if (newValue[key] !== val) {
2461
+ if (val === void 0) {
2462
+ delete newValue[key];
2463
+ } else {
2464
+ newValue[key] = val;
2465
+ }
2466
+ updated = true;
2467
+ }
2468
+ });
2469
+ return updated ? newValue : target;
2470
+ }
2471
+ var ImmutableRef = class {
2472
+ constructor() {
2473
+ this._ev = makeEventSource();
2474
+ }
2475
+ get didInvalidate() {
2476
+ return this._ev.observable;
2477
+ }
2478
+ invalidate() {
2479
+ if (this._cache !== null) {
2480
+ this._cache = null;
2481
+ this._ev.notify();
2482
+ }
2483
+ }
2484
+ get current() {
2485
+ return this._cache ?? (this._cache = this._toImmutable());
2486
+ }
2487
+ };
2488
+
2489
+ // src/refs/ValueRef.ts
2490
+ var ValueRef = class extends ImmutableRef {
2491
+ constructor(initialValue) {
2492
+ super();
2493
+ this._value = freeze(initialValue);
2494
+ }
2495
+ /** @internal */
2496
+ _toImmutable() {
2497
+ return this._value;
2498
+ }
2499
+ set(newValue) {
2500
+ if (this._value !== newValue) {
2501
+ this._value = freeze(newValue);
2502
+ this.invalidate();
2503
+ }
2504
+ }
2505
+ };
2506
+ var DerivedRef = class extends ImmutableRef {
2507
+ constructor(...args) {
2508
+ super();
2509
+ const transformFn = args.pop();
2510
+ const otherRefs = args;
2511
+ this._refs = otherRefs;
2512
+ this._refs.forEach((ref) => {
2513
+ ref.didInvalidate.subscribe(() => this.invalidate());
2514
+ });
2515
+ this._transform = transformFn;
2516
+ }
2517
+ /** @internal */
2518
+ _toImmutable() {
2519
+ return this._transform(
2520
+ ...this._refs.map((ref) => ref.current)
2521
+ );
2522
+ }
2523
+ };
2524
+
2447
2525
  // src/lib/position.ts
2448
2526
  var MIN_CODE = 32;
2449
2527
  var MAX_CODE = 126;
@@ -3878,12 +3956,6 @@ function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
3878
3956
  });
3879
3957
  }
3880
3958
 
3881
- // src/lib/freeze.ts
3882
- var freeze = process.env.NODE_ENV === "production" ? (
3883
- /* istanbul ignore next */
3884
- (x) => x
3885
- ) : Object.freeze;
3886
-
3887
3959
  // src/crdts/LiveMap.ts
3888
3960
  var LiveMap = class _LiveMap extends AbstractCrdt {
3889
3961
  constructor(entries2) {
@@ -5172,42 +5244,6 @@ var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
5172
5244
  return ClientMsgCode2;
5173
5245
  })(ClientMsgCode || {});
5174
5246
 
5175
- // src/refs/ImmutableRef.ts
5176
- function merge(target, patch) {
5177
- let updated = false;
5178
- const newValue = { ...target };
5179
- Object.keys(patch).forEach((k) => {
5180
- const key = k;
5181
- const val = patch[key];
5182
- if (newValue[key] !== val) {
5183
- if (val === void 0) {
5184
- delete newValue[key];
5185
- } else {
5186
- newValue[key] = val;
5187
- }
5188
- updated = true;
5189
- }
5190
- });
5191
- return updated ? newValue : target;
5192
- }
5193
- var ImmutableRef = class {
5194
- constructor() {
5195
- this._ev = makeEventSource();
5196
- }
5197
- get didInvalidate() {
5198
- return this._ev.observable;
5199
- }
5200
- invalidate() {
5201
- if (this._cache !== void 0) {
5202
- this._cache = void 0;
5203
- this._ev.notify();
5204
- }
5205
- }
5206
- get current() {
5207
- return this._cache ?? (this._cache = this._toImmutable());
5208
- }
5209
- };
5210
-
5211
5247
  // src/refs/OthersRef.ts
5212
5248
  function makeUser(conn, presence) {
5213
5249
  const { connectionId, id, info } = conn;
@@ -5359,40 +5395,6 @@ var PatchableRef = class extends ImmutableRef {
5359
5395
  }
5360
5396
  };
5361
5397
 
5362
- // src/refs/ValueRef.ts
5363
- var ValueRef = class extends ImmutableRef {
5364
- constructor(initialValue) {
5365
- super();
5366
- this._value = freeze(initialValue);
5367
- }
5368
- /** @internal */
5369
- _toImmutable() {
5370
- return this._value;
5371
- }
5372
- set(newValue) {
5373
- this._value = freeze(newValue);
5374
- this.invalidate();
5375
- }
5376
- };
5377
- var DerivedRef = class extends ImmutableRef {
5378
- constructor(...args) {
5379
- super();
5380
- const transformFn = args.pop();
5381
- const otherRefs = args;
5382
- this._refs = otherRefs;
5383
- this._refs.forEach((ref) => {
5384
- ref.didInvalidate.subscribe(() => this.invalidate());
5385
- });
5386
- this._transform = transformFn;
5387
- }
5388
- /** @internal */
5389
- _toImmutable() {
5390
- return this._transform(
5391
- ...this._refs.map((ref) => ref.current)
5392
- );
5393
- }
5394
- };
5395
-
5396
5398
  // src/room.ts
5397
5399
  var MAX_SOCKET_MESSAGE_SIZE = 1024 * 1024 - 1024;
5398
5400
  function makeIdFactory(connectionId) {
@@ -5500,9 +5502,9 @@ function createRoom(options, config) {
5500
5502
  others: new OthersRef(),
5501
5503
  initialStorage,
5502
5504
  idFactory: null,
5503
- // Y.js
5504
- provider: void 0,
5505
- onProviderUpdate: makeEventSource(),
5505
+ // The Yjs provider associated to this room
5506
+ yjsProvider: void 0,
5507
+ yjsProviderDidChange: makeEventSource(),
5506
5508
  // Storage
5507
5509
  clock: 0,
5508
5510
  opClock: 0,
@@ -6516,6 +6518,7 @@ ${Array.from(traces).join("\n\n")}`
6516
6518
  _addToRealUndoStack(historyOps, batchUpdates);
6517
6519
  }
6518
6520
  }
6521
+ const syncSourceForStorage = config.createSyncSource();
6519
6522
  function getStorageStatus() {
6520
6523
  if (context.root === void 0) {
6521
6524
  return _getStorage$ === null ? "not-loaded" : "loading";
@@ -6530,6 +6533,9 @@ ${Array.from(traces).join("\n\n")}`
6530
6533
  _lastStorageStatus = storageStatus;
6531
6534
  eventHub.storageStatus.notify(storageStatus);
6532
6535
  }
6536
+ syncSourceForStorage.setSyncStatus(
6537
+ storageStatus === "synchronizing" ? "synchronizing" : "synchronized"
6538
+ );
6533
6539
  }
6534
6540
  function isPresenceReady() {
6535
6541
  return self.current !== null;
@@ -6619,7 +6625,15 @@ ${Array.from(traces).join("\n\n")}`
6619
6625
  threads: [],
6620
6626
  inboxNotifications: [],
6621
6627
  nextCursor: null,
6622
- requestedAt: /* @__PURE__ */ new Date()
6628
+ //
6629
+ // HACK
6630
+ // requestedAt needs to be a *server* timestamp here. However, on
6631
+ // this 404 error response, there is no such timestamp. So out of
6632
+ // pure necessity we'll fall back to a local timestamp instead (and
6633
+ // allow for a possible 6 hour clock difference between client and
6634
+ // server).
6635
+ //
6636
+ requestedAt: new Date(Date.now() - 6 * 60 * 60 * 1e3)
6623
6637
  };
6624
6638
  }
6625
6639
  throw err;
@@ -6911,6 +6925,12 @@ ${Array.from(traces).join("\n\n")}`
6911
6925
  async function markInboxNotificationAsRead(inboxNotificationId) {
6912
6926
  await batchedMarkInboxNotificationsAsRead.get(inboxNotificationId);
6913
6927
  }
6928
+ const syncSourceForYjs = config.createSyncSource();
6929
+ function yjsStatusDidChange(status) {
6930
+ return syncSourceForYjs.setSyncStatus(
6931
+ status === "synchronizing" ? "synchronizing" : "synchronized"
6932
+ );
6933
+ }
6914
6934
  return Object.defineProperty(
6915
6935
  {
6916
6936
  [kInternal]: {
@@ -6926,14 +6946,16 @@ ${Array.from(traces).join("\n\n")}`
6926
6946
  return context.nodes.size;
6927
6947
  },
6928
6948
  // prettier-ignore
6929
- getProvider() {
6930
- return context.provider;
6949
+ getYjsProvider() {
6950
+ return context.yjsProvider;
6931
6951
  },
6932
- setProvider(provider) {
6933
- context.provider = provider;
6934
- context.onProviderUpdate.notify();
6952
+ setYjsProvider(newProvider) {
6953
+ context.yjsProvider?.off("status", yjsStatusDidChange);
6954
+ context.yjsProvider = newProvider;
6955
+ newProvider?.on("status", yjsStatusDidChange);
6956
+ context.yjsProviderDidChange.notify();
6935
6957
  },
6936
- onProviderUpdate: context.onProviderUpdate.observable,
6958
+ yjsProviderDidChange: context.yjsProviderDidChange.observable,
6937
6959
  // send metadata when using a text editor
6938
6960
  reportTextEditor,
6939
6961
  // create a text mention when using a text editor
@@ -6965,6 +6987,9 @@ ${Array.from(traces).join("\n\n")}`
6965
6987
  reconnect: () => managedSocket.reconnect(),
6966
6988
  disconnect: () => managedSocket.disconnect(),
6967
6989
  destroy: () => {
6990
+ syncSourceForStorage.destroy();
6991
+ context.yjsProvider?.off("status", yjsStatusDidChange);
6992
+ syncSourceForYjs.destroy();
6968
6993
  uninstallBgTabSpy();
6969
6994
  managedSocket.destroy();
6970
6995
  },
@@ -7229,7 +7254,8 @@ function createClient(options) {
7229
7254
  unstable_batchedUpdates: options2?.unstable_batchedUpdates,
7230
7255
  baseUrl,
7231
7256
  unstable_fallbackToHTTP: !!clientOptions.unstable_fallbackToHTTP,
7232
- unstable_streamData: !!clientOptions.unstable_streamData
7257
+ unstable_streamData: !!clientOptions.unstable_streamData,
7258
+ createSyncSource
7233
7259
  }
7234
7260
  );
7235
7261
  const newRoomDetails = {
@@ -7314,6 +7340,46 @@ function createClient(options) {
7314
7340
  function invalidateResolvedMentionSuggestions() {
7315
7341
  mentionSuggestionsCache.clear();
7316
7342
  }
7343
+ const syncStatusSources = [];
7344
+ const syncStatusRef = new ValueRef("synchronized");
7345
+ function getSyncStatus() {
7346
+ const status = syncStatusRef.current;
7347
+ return status === "synchronizing" ? status : "synchronized";
7348
+ }
7349
+ function recompute() {
7350
+ syncStatusRef.set(
7351
+ syncStatusSources.some((src) => src.current === "synchronizing") ? "synchronizing" : syncStatusSources.some((src) => src.current === "has-local-changes") ? "has-local-changes" : "synchronized"
7352
+ );
7353
+ }
7354
+ function createSyncSource() {
7355
+ const source = new ValueRef("synchronized");
7356
+ syncStatusSources.push(source);
7357
+ const unsub = source.didInvalidate.subscribe(() => recompute());
7358
+ function setSyncStatus(status) {
7359
+ source.set(status);
7360
+ }
7361
+ function destroy() {
7362
+ unsub();
7363
+ const index = syncStatusSources.findIndex((item) => item === source);
7364
+ if (index > -1) {
7365
+ const [ref] = syncStatusSources.splice(index, 1);
7366
+ const wasStillPending = ref.current !== "synchronized";
7367
+ if (wasStillPending) {
7368
+ recompute();
7369
+ }
7370
+ }
7371
+ }
7372
+ return { setSyncStatus, destroy };
7373
+ }
7374
+ {
7375
+ const maybePreventClose = (e) => {
7376
+ if (clientOptions.preventUnsavedChanges && syncStatusRef.current !== "synchronized") {
7377
+ e.preventDefault();
7378
+ }
7379
+ };
7380
+ const win = typeof window !== "undefined" ? window : void 0;
7381
+ win?.addEventListener("beforeunload", maybePreventClose);
7382
+ }
7317
7383
  const client = Object.defineProperty(
7318
7384
  {
7319
7385
  enterRoom,
@@ -7326,6 +7392,10 @@ function createClient(options) {
7326
7392
  invalidateRoomsInfo: invalidateResolvedRoomsInfo,
7327
7393
  invalidateMentionSuggestions: invalidateResolvedMentionSuggestions
7328
7394
  },
7395
+ getSyncStatus,
7396
+ events: {
7397
+ syncStatus: syncStatusRef.didInvalidate
7398
+ },
7329
7399
  // Internal
7330
7400
  [kInternal]: {
7331
7401
  currentUserIdStore,
@@ -7340,7 +7410,8 @@ function createClient(options) {
7340
7410
  getUserThreads_experimental: notificationsAPI.getUserThreads_experimental,
7341
7411
  getUserThreadsSince_experimental: notificationsAPI.getUserThreadsSince_experimental,
7342
7412
  // Type-level helper only, it's effectively only an identity-function at runtime
7343
- as: () => client
7413
+ as: () => client,
7414
+ createSyncSource
7344
7415
  }
7345
7416
  },
7346
7417
  kInternal,