@liveblocks/core 2.7.0 → 2.8.0-beta1

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.7.0";
9
+ var PKG_VERSION = "2.8.0-beta1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -1871,8 +1871,7 @@ var Batch = class {
1871
1871
  this.clearDelayTimeout();
1872
1872
  }
1873
1873
  };
1874
- function createBatchStore(callback, options) {
1875
- const batch = new Batch(callback, options);
1874
+ function createBatchStore(batch) {
1876
1875
  const cache = /* @__PURE__ */ new Map();
1877
1876
  const eventSource2 = makeEventSource();
1878
1877
  function getCacheKey(args) {
@@ -1952,7 +1951,6 @@ function createStore(initialState) {
1952
1951
  }
1953
1952
  function subscribe(callback) {
1954
1953
  subscribers.add(callback);
1955
- callback(state);
1956
1954
  return () => {
1957
1955
  subscribers.delete(callback);
1958
1956
  };
@@ -4797,9 +4795,19 @@ function findNonSerializableValue(value, path = "") {
4797
4795
  return false;
4798
4796
  }
4799
4797
 
4798
+ // src/lib/chunk.ts
4799
+ function chunk(array, size) {
4800
+ const chunks = [];
4801
+ for (let i = 0, j = array.length; i < j; i += size) {
4802
+ chunks.push(array.slice(i, i + size));
4803
+ }
4804
+ return chunks;
4805
+ }
4806
+
4800
4807
  // src/lib/createIds.ts
4801
4808
  var THREAD_ID_PREFIX = "th";
4802
4809
  var COMMENT_ID_PREFIX = "cm";
4810
+ var COMMENT_ATTACHMENT_ID_PREFIX = "at";
4803
4811
  var INBOX_NOTIFICATION_ID_PREFIX = "in";
4804
4812
  function createOptimisticId(prefix) {
4805
4813
  return `${prefix}_${nanoid()}`;
@@ -4810,6 +4818,9 @@ function createThreadId() {
4810
4818
  function createCommentId() {
4811
4819
  return createOptimisticId(COMMENT_ID_PREFIX);
4812
4820
  }
4821
+ function createCommentAttachmentId() {
4822
+ return createOptimisticId(COMMENT_ATTACHMENT_ID_PREFIX);
4823
+ }
4813
4824
  function createInboxNotificationId() {
4814
4825
  return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
4815
4826
  }
@@ -5212,6 +5223,22 @@ function installBackgroundTabSpy() {
5212
5223
  };
5213
5224
  return [inBackgroundSince, unsub];
5214
5225
  }
5226
+ var GET_ATTACHMENT_URLS_BATCH_DELAY = 50;
5227
+ var ATTACHMENT_PART_SIZE = 5 * 1024 * 1024;
5228
+ var ATTACHMENT_PART_BATCH_SIZE = 5;
5229
+ function splitFileIntoParts(file) {
5230
+ const parts = [];
5231
+ let start = 0;
5232
+ while (start < file.size) {
5233
+ const end = Math.min(start + ATTACHMENT_PART_SIZE, file.size);
5234
+ parts.push({
5235
+ partNumber: parts.length + 1,
5236
+ part: file.slice(start, end)
5237
+ });
5238
+ start = end;
5239
+ }
5240
+ return parts;
5241
+ }
5215
5242
  var CommentsApiError = class extends Error {
5216
5243
  constructor(message, status, details) {
5217
5244
  super(message);
@@ -6505,7 +6532,8 @@ ${Array.from(traces).join("\n\n")}`
6505
6532
  metadata,
6506
6533
  body,
6507
6534
  commentId = createCommentId(),
6508
- threadId = createThreadId()
6535
+ threadId = createThreadId(),
6536
+ attachmentIds
6509
6537
  }) {
6510
6538
  const thread = await fetchCommentsJson("/threads", {
6511
6539
  method: "POST",
@@ -6516,7 +6544,8 @@ ${Array.from(traces).join("\n\n")}`
6516
6544
  id: threadId,
6517
6545
  comment: {
6518
6546
  id: commentId,
6519
- body
6547
+ body,
6548
+ attachmentIds
6520
6549
  },
6521
6550
  metadata
6522
6551
  })
@@ -6562,7 +6591,8 @@ ${Array.from(traces).join("\n\n")}`
6562
6591
  async function createComment({
6563
6592
  threadId,
6564
6593
  commentId = createCommentId(),
6565
- body
6594
+ body,
6595
+ attachmentIds
6566
6596
  }) {
6567
6597
  const comment = await fetchCommentsJson(
6568
6598
  `/threads/${encodeURIComponent(threadId)}/comments`,
@@ -6573,7 +6603,8 @@ ${Array.from(traces).join("\n\n")}`
6573
6603
  },
6574
6604
  body: JSON.stringify({
6575
6605
  id: commentId,
6576
- body
6606
+ body,
6607
+ attachmentIds
6577
6608
  })
6578
6609
  }
6579
6610
  );
@@ -6582,7 +6613,8 @@ ${Array.from(traces).join("\n\n")}`
6582
6613
  async function editComment({
6583
6614
  threadId,
6584
6615
  commentId,
6585
- body
6616
+ body,
6617
+ attachmentIds
6586
6618
  }) {
6587
6619
  const comment = await fetchCommentsJson(
6588
6620
  `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
@@ -6594,7 +6626,8 @@ ${Array.from(traces).join("\n\n")}`
6594
6626
  "Content-Type": "application/json"
6595
6627
  },
6596
6628
  body: JSON.stringify({
6597
- body
6629
+ body,
6630
+ attachmentIds
6598
6631
  })
6599
6632
  }
6600
6633
  );
@@ -6646,6 +6679,126 @@ ${Array.from(traces).join("\n\n")}`
6646
6679
  }
6647
6680
  );
6648
6681
  }
6682
+ function prepareAttachment(file) {
6683
+ return {
6684
+ type: "localAttachment",
6685
+ status: "idle",
6686
+ id: createCommentAttachmentId(),
6687
+ name: file.name,
6688
+ size: file.size,
6689
+ mimeType: file.type,
6690
+ file
6691
+ };
6692
+ }
6693
+ async function uploadAttachment(attachment, options2 = {}) {
6694
+ const abortSignal = options2.signal;
6695
+ const abortError = abortSignal ? new DOMException(
6696
+ `Upload of attachment ${attachment.id} was aborted.`,
6697
+ "AbortError"
6698
+ ) : void 0;
6699
+ if (abortSignal?.aborted) {
6700
+ throw abortError;
6701
+ }
6702
+ if (attachment.size <= ATTACHMENT_PART_SIZE) {
6703
+ return fetchCommentsJson(
6704
+ `/attachments/${encodeURIComponent(attachment.id)}/upload/${encodeURIComponent(attachment.name)}`,
6705
+ {
6706
+ method: "PUT",
6707
+ body: attachment.file,
6708
+ signal: abortSignal
6709
+ }
6710
+ );
6711
+ } else {
6712
+ let uploadId;
6713
+ const uploadedParts = [];
6714
+ try {
6715
+ const createMultiPartUpload = await fetchCommentsJson(
6716
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(attachment.name)}`,
6717
+ {
6718
+ method: "POST",
6719
+ signal: abortSignal
6720
+ }
6721
+ );
6722
+ uploadId = createMultiPartUpload.uploadId;
6723
+ const parts = splitFileIntoParts(attachment.file);
6724
+ if (abortSignal?.aborted) {
6725
+ throw abortError;
6726
+ }
6727
+ const batches = chunk(parts, ATTACHMENT_PART_BATCH_SIZE);
6728
+ for (const parts2 of batches) {
6729
+ const uploadedPartsPromises = [];
6730
+ for (const { part, partNumber } of parts2) {
6731
+ uploadedPartsPromises.push(
6732
+ fetchCommentsJson(
6733
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}/${encodeURIComponent(partNumber)}`,
6734
+ {
6735
+ method: "PUT",
6736
+ body: part,
6737
+ signal: abortSignal
6738
+ }
6739
+ )
6740
+ );
6741
+ }
6742
+ uploadedParts.push(...await Promise.all(uploadedPartsPromises));
6743
+ }
6744
+ if (abortSignal?.aborted) {
6745
+ throw abortError;
6746
+ }
6747
+ const sortedUploadedParts = uploadedParts.sort(
6748
+ (a, b) => a.partNumber - b.partNumber
6749
+ );
6750
+ return fetchCommentsJson(
6751
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}/complete`,
6752
+ {
6753
+ method: "POST",
6754
+ headers: {
6755
+ "Content-Type": "application/json"
6756
+ },
6757
+ body: JSON.stringify({ parts: sortedUploadedParts }),
6758
+ signal: abortSignal
6759
+ }
6760
+ );
6761
+ } catch (error3) {
6762
+ if (uploadId && error3?.name && (error3.name === "AbortError" || error3.name === "TimeoutError")) {
6763
+ await fetchCommentsApi(
6764
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}`,
6765
+ void 0,
6766
+ {
6767
+ method: "DELETE"
6768
+ }
6769
+ );
6770
+ }
6771
+ throw error3;
6772
+ }
6773
+ }
6774
+ }
6775
+ async function getAttachmentUrls(attachmentIds) {
6776
+ const { urls } = await fetchCommentsJson(
6777
+ "/attachments/presigned-urls",
6778
+ {
6779
+ method: "POST",
6780
+ headers: {
6781
+ "Content-Type": "application/json"
6782
+ },
6783
+ body: JSON.stringify({ attachmentIds })
6784
+ }
6785
+ );
6786
+ return urls;
6787
+ }
6788
+ const batchedGetAttachmentUrls = new Batch(
6789
+ async (batchedAttachmentIds) => {
6790
+ const attachmentIds = batchedAttachmentIds.flat();
6791
+ const attachmentUrls = await getAttachmentUrls(attachmentIds);
6792
+ return attachmentUrls.map(
6793
+ (url) => url ?? new Error("There was an error while getting this attachment's URL")
6794
+ );
6795
+ },
6796
+ { delay: GET_ATTACHMENT_URLS_BATCH_DELAY }
6797
+ );
6798
+ const attachmentUrlsStore = createBatchStore(batchedGetAttachmentUrls);
6799
+ function getAttachmentUrl(attachmentId) {
6800
+ return batchedGetAttachmentUrls.get(attachmentId);
6801
+ }
6649
6802
  async function fetchNotificationsJson(endpoint, options2) {
6650
6803
  const authValue = await delegates.authenticate();
6651
6804
  const response = await fetchClientApi(
@@ -6761,7 +6914,8 @@ ${Array.from(traces).join("\n\n")}`
6761
6914
  // These exist only for our E2E testing app
6762
6915
  explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
6763
6916
  rawSend: (data) => managedSocket.send(data)
6764
- }
6917
+ },
6918
+ attachmentUrlsStore
6765
6919
  },
6766
6920
  id: config.roomId,
6767
6921
  subscribe: makeClassicSubscribeFn(events),
@@ -6816,6 +6970,9 @@ ${Array.from(traces).join("\n\n")}`
6816
6970
  deleteComment,
6817
6971
  addReaction,
6818
6972
  removeReaction,
6973
+ prepareAttachment,
6974
+ uploadAttachment,
6975
+ getAttachmentUrl,
6819
6976
  // Notifications
6820
6977
  getNotificationSettings,
6821
6978
  updateNotificationSettings,
@@ -7096,7 +7253,7 @@ function createClient(options) {
7096
7253
  () => !resolveUsers,
7097
7254
  "Set the resolveUsers option in createClient to specify user info."
7098
7255
  );
7099
- const usersStore = createBatchStore(
7256
+ const batchedResolveUsers = new Batch(
7100
7257
  async (batchedUserIds) => {
7101
7258
  const userIds = batchedUserIds.flat();
7102
7259
  const users = await resolveUsers?.({ userIds });
@@ -7105,12 +7262,13 @@ function createClient(options) {
7105
7262
  },
7106
7263
  { delay: RESOLVE_USERS_BATCH_DELAY }
7107
7264
  );
7265
+ const usersStore = createBatchStore(batchedResolveUsers);
7108
7266
  const resolveRoomsInfo = clientOptions.resolveRoomsInfo;
7109
7267
  const warnIfNoResolveRoomsInfo = createDevelopmentWarning(
7110
7268
  () => !resolveRoomsInfo,
7111
7269
  "Set the resolveRoomsInfo option in createClient to specify room info."
7112
7270
  );
7113
- const roomsInfoStore = createBatchStore(
7271
+ const batchedResolveRoomsInfo = new Batch(
7114
7272
  async (batchedRoomIds) => {
7115
7273
  const roomIds = batchedRoomIds.flat();
7116
7274
  const roomsInfo = await resolveRoomsInfo?.({ roomIds });
@@ -7119,6 +7277,7 @@ function createClient(options) {
7119
7277
  },
7120
7278
  { delay: RESOLVE_ROOMS_INFO_BATCH_DELAY }
7121
7279
  );
7280
+ const roomsInfoStore = createBatchStore(batchedResolveRoomsInfo);
7122
7281
  return Object.defineProperty(
7123
7282
  {
7124
7283
  enterRoom,
@@ -7977,7 +8136,7 @@ function shallowArray(xs, ys) {
7977
8136
  return true;
7978
8137
  }
7979
8138
  function shallowObj(objA, objB) {
7980
- if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null || Object.prototype.toString.call(objA) !== "[object Object]" || Object.prototype.toString.call(objB) !== "[object Object]") {
8139
+ if (!isPlainObject(objA) || !isPlainObject(objB)) {
7981
8140
  return false;
7982
8141
  }
7983
8142
  const keysA = Object.keys(objA);
@@ -8021,7 +8180,9 @@ export {
8021
8180
  assert,
8022
8181
  assertNever,
8023
8182
  b64decode,
8183
+ chunk,
8024
8184
  cloneLson,
8185
+ compactObject,
8025
8186
  fancy_console_exports as console,
8026
8187
  convertToCommentData,
8027
8188
  convertToCommentUserReaction,