@liveblocks/core 2.7.1 → 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.1";
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) {
@@ -4796,9 +4795,19 @@ function findNonSerializableValue(value, path = "") {
4796
4795
  return false;
4797
4796
  }
4798
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
+
4799
4807
  // src/lib/createIds.ts
4800
4808
  var THREAD_ID_PREFIX = "th";
4801
4809
  var COMMENT_ID_PREFIX = "cm";
4810
+ var COMMENT_ATTACHMENT_ID_PREFIX = "at";
4802
4811
  var INBOX_NOTIFICATION_ID_PREFIX = "in";
4803
4812
  function createOptimisticId(prefix) {
4804
4813
  return `${prefix}_${nanoid()}`;
@@ -4809,6 +4818,9 @@ function createThreadId() {
4809
4818
  function createCommentId() {
4810
4819
  return createOptimisticId(COMMENT_ID_PREFIX);
4811
4820
  }
4821
+ function createCommentAttachmentId() {
4822
+ return createOptimisticId(COMMENT_ATTACHMENT_ID_PREFIX);
4823
+ }
4812
4824
  function createInboxNotificationId() {
4813
4825
  return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
4814
4826
  }
@@ -5211,6 +5223,22 @@ function installBackgroundTabSpy() {
5211
5223
  };
5212
5224
  return [inBackgroundSince, unsub];
5213
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
+ }
5214
5242
  var CommentsApiError = class extends Error {
5215
5243
  constructor(message, status, details) {
5216
5244
  super(message);
@@ -6504,7 +6532,8 @@ ${Array.from(traces).join("\n\n")}`
6504
6532
  metadata,
6505
6533
  body,
6506
6534
  commentId = createCommentId(),
6507
- threadId = createThreadId()
6535
+ threadId = createThreadId(),
6536
+ attachmentIds
6508
6537
  }) {
6509
6538
  const thread = await fetchCommentsJson("/threads", {
6510
6539
  method: "POST",
@@ -6515,7 +6544,8 @@ ${Array.from(traces).join("\n\n")}`
6515
6544
  id: threadId,
6516
6545
  comment: {
6517
6546
  id: commentId,
6518
- body
6547
+ body,
6548
+ attachmentIds
6519
6549
  },
6520
6550
  metadata
6521
6551
  })
@@ -6561,7 +6591,8 @@ ${Array.from(traces).join("\n\n")}`
6561
6591
  async function createComment({
6562
6592
  threadId,
6563
6593
  commentId = createCommentId(),
6564
- body
6594
+ body,
6595
+ attachmentIds
6565
6596
  }) {
6566
6597
  const comment = await fetchCommentsJson(
6567
6598
  `/threads/${encodeURIComponent(threadId)}/comments`,
@@ -6572,7 +6603,8 @@ ${Array.from(traces).join("\n\n")}`
6572
6603
  },
6573
6604
  body: JSON.stringify({
6574
6605
  id: commentId,
6575
- body
6606
+ body,
6607
+ attachmentIds
6576
6608
  })
6577
6609
  }
6578
6610
  );
@@ -6581,7 +6613,8 @@ ${Array.from(traces).join("\n\n")}`
6581
6613
  async function editComment({
6582
6614
  threadId,
6583
6615
  commentId,
6584
- body
6616
+ body,
6617
+ attachmentIds
6585
6618
  }) {
6586
6619
  const comment = await fetchCommentsJson(
6587
6620
  `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
@@ -6593,7 +6626,8 @@ ${Array.from(traces).join("\n\n")}`
6593
6626
  "Content-Type": "application/json"
6594
6627
  },
6595
6628
  body: JSON.stringify({
6596
- body
6629
+ body,
6630
+ attachmentIds
6597
6631
  })
6598
6632
  }
6599
6633
  );
@@ -6645,6 +6679,126 @@ ${Array.from(traces).join("\n\n")}`
6645
6679
  }
6646
6680
  );
6647
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
+ }
6648
6802
  async function fetchNotificationsJson(endpoint, options2) {
6649
6803
  const authValue = await delegates.authenticate();
6650
6804
  const response = await fetchClientApi(
@@ -6760,7 +6914,8 @@ ${Array.from(traces).join("\n\n")}`
6760
6914
  // These exist only for our E2E testing app
6761
6915
  explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
6762
6916
  rawSend: (data) => managedSocket.send(data)
6763
- }
6917
+ },
6918
+ attachmentUrlsStore
6764
6919
  },
6765
6920
  id: config.roomId,
6766
6921
  subscribe: makeClassicSubscribeFn(events),
@@ -6815,6 +6970,9 @@ ${Array.from(traces).join("\n\n")}`
6815
6970
  deleteComment,
6816
6971
  addReaction,
6817
6972
  removeReaction,
6973
+ prepareAttachment,
6974
+ uploadAttachment,
6975
+ getAttachmentUrl,
6818
6976
  // Notifications
6819
6977
  getNotificationSettings,
6820
6978
  updateNotificationSettings,
@@ -7095,7 +7253,7 @@ function createClient(options) {
7095
7253
  () => !resolveUsers,
7096
7254
  "Set the resolveUsers option in createClient to specify user info."
7097
7255
  );
7098
- const usersStore = createBatchStore(
7256
+ const batchedResolveUsers = new Batch(
7099
7257
  async (batchedUserIds) => {
7100
7258
  const userIds = batchedUserIds.flat();
7101
7259
  const users = await resolveUsers?.({ userIds });
@@ -7104,12 +7262,13 @@ function createClient(options) {
7104
7262
  },
7105
7263
  { delay: RESOLVE_USERS_BATCH_DELAY }
7106
7264
  );
7265
+ const usersStore = createBatchStore(batchedResolveUsers);
7107
7266
  const resolveRoomsInfo = clientOptions.resolveRoomsInfo;
7108
7267
  const warnIfNoResolveRoomsInfo = createDevelopmentWarning(
7109
7268
  () => !resolveRoomsInfo,
7110
7269
  "Set the resolveRoomsInfo option in createClient to specify room info."
7111
7270
  );
7112
- const roomsInfoStore = createBatchStore(
7271
+ const batchedResolveRoomsInfo = new Batch(
7113
7272
  async (batchedRoomIds) => {
7114
7273
  const roomIds = batchedRoomIds.flat();
7115
7274
  const roomsInfo = await resolveRoomsInfo?.({ roomIds });
@@ -7118,6 +7277,7 @@ function createClient(options) {
7118
7277
  },
7119
7278
  { delay: RESOLVE_ROOMS_INFO_BATCH_DELAY }
7120
7279
  );
7280
+ const roomsInfoStore = createBatchStore(batchedResolveRoomsInfo);
7121
7281
  return Object.defineProperty(
7122
7282
  {
7123
7283
  enterRoom,
@@ -8020,6 +8180,7 @@ export {
8020
8180
  assert,
8021
8181
  assertNever,
8022
8182
  b64decode,
8183
+ chunk,
8023
8184
  cloneLson,
8024
8185
  compactObject,
8025
8186
  fancy_console_exports as console,