@liveblocks/core 2.5.1 → 2.7.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.5.1";
9
+ var PKG_VERSION = "2.7.0-beta1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -1859,8 +1859,7 @@ var Batch = class {
1859
1859
  this.clearDelayTimeout();
1860
1860
  }
1861
1861
  };
1862
- function createBatchStore(callback, options) {
1863
- const batch = new Batch(callback, options);
1862
+ function createBatchStore(batch) {
1864
1863
  const cache = /* @__PURE__ */ new Map();
1865
1864
  const eventSource2 = makeEventSource();
1866
1865
  function getCacheKey(args) {
@@ -4749,9 +4748,19 @@ function findNonSerializableValue(value, path = "") {
4749
4748
  return false;
4750
4749
  }
4751
4750
 
4751
+ // src/lib/chunk.ts
4752
+ function chunk(array, size) {
4753
+ const chunks = [];
4754
+ for (let i = 0, j = array.length; i < j; i += size) {
4755
+ chunks.push(array.slice(i, i + size));
4756
+ }
4757
+ return chunks;
4758
+ }
4759
+
4752
4760
  // src/lib/createIds.ts
4753
4761
  var THREAD_ID_PREFIX = "th";
4754
4762
  var COMMENT_ID_PREFIX = "cm";
4763
+ var COMMENT_ATTACHMENT_ID_PREFIX = "at";
4755
4764
  var INBOX_NOTIFICATION_ID_PREFIX = "in";
4756
4765
  function createOptimisticId(prefix) {
4757
4766
  return `${prefix}_${nanoid()}`;
@@ -4762,6 +4771,9 @@ function createThreadId() {
4762
4771
  function createCommentId() {
4763
4772
  return createOptimisticId(COMMENT_ID_PREFIX);
4764
4773
  }
4774
+ function createCommentAttachmentId() {
4775
+ return createOptimisticId(COMMENT_ATTACHMENT_ID_PREFIX);
4776
+ }
4765
4777
  function createInboxNotificationId() {
4766
4778
  return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
4767
4779
  }
@@ -5164,6 +5176,22 @@ function installBackgroundTabSpy() {
5164
5176
  };
5165
5177
  return [inBackgroundSince, unsub];
5166
5178
  }
5179
+ var GET_ATTACHMENT_URLS_BATCH_DELAY = 50;
5180
+ var ATTACHMENT_PART_SIZE = 5 * 1024 * 1024;
5181
+ var ATTACHMENT_PART_BATCH_SIZE = 5;
5182
+ function splitFileIntoParts(file) {
5183
+ const parts = [];
5184
+ let start = 0;
5185
+ while (start < file.size) {
5186
+ const end = Math.min(start + ATTACHMENT_PART_SIZE, file.size);
5187
+ parts.push({
5188
+ partNumber: parts.length + 1,
5189
+ part: file.slice(start, end)
5190
+ });
5191
+ start = end;
5192
+ }
5193
+ return parts;
5194
+ }
5167
5195
  var CommentsApiError = class extends Error {
5168
5196
  constructor(message, status, details) {
5169
5197
  super(message);
@@ -5172,429 +5200,138 @@ var CommentsApiError = class extends Error {
5172
5200
  this.details = details;
5173
5201
  }
5174
5202
  };
5175
- function createCommentsApi(roomId, getAuthValue, fetchClientApi) {
5176
- async function fetchCommentsApi(endpoint, params, options) {
5177
- const authValue = await getAuthValue();
5178
- return fetchClientApi(roomId, endpoint, authValue, options, params);
5179
- }
5180
- async function fetchJson(endpoint, options, params) {
5181
- const response = await fetchCommentsApi(endpoint, params, options);
5182
- if (!response.ok) {
5183
- if (response.status >= 400 && response.status < 600) {
5184
- let error3;
5185
- try {
5186
- const errorBody = await response.json();
5187
- error3 = new CommentsApiError(
5188
- errorBody.message,
5189
- response.status,
5190
- errorBody
5191
- );
5192
- } catch {
5193
- error3 = new CommentsApiError(response.statusText, response.status);
5194
- }
5195
- throw error3;
5196
- }
5197
- }
5198
- let body;
5199
- try {
5200
- body = await response.json();
5201
- } catch {
5202
- body = {};
5203
+ var MARK_INBOX_NOTIFICATIONS_AS_READ_BATCH_DELAY2 = 50;
5204
+ function createRoom(options, config) {
5205
+ const initialPresence = options.initialPresence;
5206
+ const initialStorage = options.initialStorage;
5207
+ const [inBackgroundSince, uninstallBgTabSpy] = installBackgroundTabSpy();
5208
+ const delegates = {
5209
+ ...config.delegates,
5210
+ // A connection is allowed to go into "zombie state" only if all of the
5211
+ // following conditions apply:
5212
+ //
5213
+ // - The `backgroundKeepAliveTimeout` client option is configured
5214
+ // - The browser window has been in the background for at least
5215
+ // `backgroundKeepAliveTimeout` milliseconds
5216
+ // - There are no pending changes
5217
+ //
5218
+ canZombie() {
5219
+ return config.backgroundKeepAliveTimeout !== void 0 && inBackgroundSince.current !== null && Date.now() > inBackgroundSince.current + config.backgroundKeepAliveTimeout && getStorageStatus() !== "synchronizing";
5203
5220
  }
5204
- return body;
5205
- }
5206
- async function getThreadsSince(options) {
5207
- const response = await fetchCommentsApi(
5208
- "/threads",
5209
- {
5210
- since: options?.since?.toISOString()
5211
- },
5212
- {
5213
- headers: {
5214
- "Content-Type": "application/json"
5221
+ };
5222
+ const managedSocket = new ManagedSocket(
5223
+ delegates,
5224
+ config.enableDebugLogging
5225
+ );
5226
+ const context = {
5227
+ buffer: {
5228
+ flushTimerID: void 0,
5229
+ lastFlushedAt: 0,
5230
+ presenceUpdates: (
5231
+ // Queue up the initial presence message as a Full Presence™ update
5232
+ {
5233
+ type: "full",
5234
+ data: initialPresence
5235
+ }
5236
+ ),
5237
+ messages: [],
5238
+ storageOperations: []
5239
+ },
5240
+ staticSessionInfo: new ValueRef(null),
5241
+ dynamicSessionInfo: new ValueRef(null),
5242
+ myPresence: new PatchableRef(initialPresence),
5243
+ others: new OthersRef(),
5244
+ initialStorage,
5245
+ idFactory: null,
5246
+ // Y.js
5247
+ provider: void 0,
5248
+ onProviderUpdate: makeEventSource(),
5249
+ // Storage
5250
+ clock: 0,
5251
+ opClock: 0,
5252
+ nodes: /* @__PURE__ */ new Map(),
5253
+ root: void 0,
5254
+ undoStack: [],
5255
+ redoStack: [],
5256
+ pausedHistory: null,
5257
+ activeBatch: null,
5258
+ unacknowledgedOps: /* @__PURE__ */ new Map(),
5259
+ // Debug
5260
+ opStackTraces: process.env.NODE_ENV !== "production" ? /* @__PURE__ */ new Map() : void 0
5261
+ };
5262
+ const doNotBatchUpdates = (cb) => cb();
5263
+ const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates;
5264
+ let lastTokenKey;
5265
+ function onStatusDidChange(newStatus) {
5266
+ const authValue = managedSocket.authValue;
5267
+ if (authValue !== null) {
5268
+ const tokenKey = getAuthBearerHeaderFromAuthValue(authValue);
5269
+ if (tokenKey !== lastTokenKey) {
5270
+ lastTokenKey = tokenKey;
5271
+ if (authValue.type === "secret") {
5272
+ const token = authValue.token.parsed;
5273
+ context.staticSessionInfo.set({
5274
+ userId: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.id : token.uid,
5275
+ userInfo: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.info : token.ui
5276
+ });
5277
+ } else {
5278
+ context.staticSessionInfo.set({
5279
+ userId: void 0,
5280
+ userInfo: void 0
5281
+ });
5215
5282
  }
5216
5283
  }
5217
- );
5218
- if (response.ok) {
5219
- const json = await response.json();
5220
- return {
5221
- threads: {
5222
- updated: json.data.map(convertToThreadData),
5223
- deleted: json.deletedThreads.map(convertToThreadDeleteInfo)
5224
- },
5225
- inboxNotifications: {
5226
- updated: json.inboxNotifications.map(convertToInboxNotificationData),
5227
- deleted: json.deletedInboxNotifications.map(
5228
- convertToInboxNotificationDeleteInfo
5229
- )
5230
- },
5231
- requestedAt: new Date(json.meta.requestedAt)
5232
- };
5233
- } else if (response.status === 404) {
5234
- return {
5235
- threads: {
5236
- updated: [],
5237
- deleted: []
5238
- },
5239
- inboxNotifications: {
5240
- updated: [],
5241
- deleted: []
5242
- },
5243
- requestedAt: /* @__PURE__ */ new Date()
5244
- };
5245
- } else {
5246
- throw new Error("There was an error while getting threads.");
5247
5284
  }
5285
+ batchUpdates(() => {
5286
+ eventHub.status.notify(newStatus);
5287
+ notifySelfChanged(doNotBatchUpdates);
5288
+ });
5248
5289
  }
5249
- async function getThreads(options) {
5250
- let query;
5251
- if (options?.query) {
5252
- query = objectToQuery(options.query);
5253
- }
5254
- const response = await fetchCommentsApi(
5255
- "/threads",
5256
- {
5257
- query
5258
- },
5259
- {
5260
- headers: {
5261
- "Content-Type": "application/json"
5290
+ let _connectionLossTimerId;
5291
+ let _hasLostConnection = false;
5292
+ function handleConnectionLossEvent(newStatus) {
5293
+ if (newStatus === "reconnecting") {
5294
+ _connectionLossTimerId = setTimeout(() => {
5295
+ batchUpdates(() => {
5296
+ eventHub.lostConnection.notify("lost");
5297
+ _hasLostConnection = true;
5298
+ context.others.clearOthers();
5299
+ notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
5300
+ });
5301
+ }, config.lostConnectionTimeout);
5302
+ } else {
5303
+ clearTimeout(_connectionLossTimerId);
5304
+ if (_hasLostConnection) {
5305
+ if (newStatus === "disconnected") {
5306
+ batchUpdates(() => {
5307
+ eventHub.lostConnection.notify("failed");
5308
+ });
5309
+ } else {
5310
+ batchUpdates(() => {
5311
+ eventHub.lostConnection.notify("restored");
5312
+ });
5262
5313
  }
5314
+ _hasLostConnection = false;
5263
5315
  }
5264
- );
5265
- if (response.ok) {
5266
- const json = await response.json();
5267
- return {
5268
- threads: json.data.map(convertToThreadData),
5269
- inboxNotifications: json.inboxNotifications.map(
5270
- convertToInboxNotificationData
5271
- ),
5272
- requestedAt: new Date(json.meta.requestedAt)
5273
- };
5274
- } else if (response.status === 404) {
5275
- return {
5276
- threads: [],
5277
- inboxNotifications: [],
5278
- deletedThreads: [],
5279
- deletedInboxNotifications: [],
5280
- requestedAt: /* @__PURE__ */ new Date()
5281
- };
5282
- } else {
5283
- throw new Error("There was an error while getting threads.");
5284
5316
  }
5285
5317
  }
5286
- async function getThread(threadId) {
5287
- const response = await fetchCommentsApi(
5288
- `/thread-with-notification/${threadId}`
5289
- );
5290
- if (response.ok) {
5291
- const json = await response.json();
5292
- return {
5293
- thread: convertToThreadData(json.thread),
5294
- inboxNotification: json.inboxNotification ? convertToInboxNotificationData(json.inboxNotification) : void 0
5295
- };
5296
- } else if (response.status === 404) {
5297
- return {
5298
- thread: void 0,
5299
- inboxNotification: void 0
5300
- };
5301
- } else {
5302
- throw new Error(`There was an error while getting thread ${threadId}.`);
5318
+ function onDidConnect() {
5319
+ context.buffer.presenceUpdates = {
5320
+ type: "full",
5321
+ data: (
5322
+ // Because context.me.current is a readonly object, we'll have to
5323
+ // make a copy here. Otherwise, type errors happen later when
5324
+ // "patching" my presence.
5325
+ { ...context.myPresence.current }
5326
+ )
5327
+ };
5328
+ if (_getStorage$ !== null) {
5329
+ refreshStorage({ flush: false });
5303
5330
  }
5331
+ flushNowOrSoon();
5304
5332
  }
5305
- async function createThread({
5306
- metadata,
5307
- body,
5308
- commentId = createCommentId(),
5309
- threadId = createThreadId()
5310
- }) {
5311
- const thread = await fetchJson("/threads", {
5312
- method: "POST",
5313
- headers: {
5314
- "Content-Type": "application/json"
5315
- },
5316
- body: JSON.stringify({
5317
- id: threadId,
5318
- comment: {
5319
- id: commentId,
5320
- body
5321
- },
5322
- metadata
5323
- })
5324
- });
5325
- return convertToThreadData(thread);
5326
- }
5327
- async function deleteThread(threadId) {
5328
- await fetchJson(`/threads/${encodeURIComponent(threadId)}`, {
5329
- method: "DELETE"
5330
- });
5331
- }
5332
- async function editThreadMetadata({
5333
- metadata,
5334
- threadId
5335
- }) {
5336
- return await fetchJson(
5337
- `/threads/${encodeURIComponent(threadId)}/metadata`,
5338
- {
5339
- method: "POST",
5340
- headers: {
5341
- "Content-Type": "application/json"
5342
- },
5343
- body: JSON.stringify(metadata)
5344
- }
5345
- );
5346
- }
5347
- async function markThreadAsResolved(threadId) {
5348
- await fetchJson(
5349
- `/threads/${encodeURIComponent(threadId)}/mark-as-resolved`,
5350
- {
5351
- method: "POST"
5352
- }
5353
- );
5354
- }
5355
- async function markThreadAsUnresolved(threadId) {
5356
- await fetchJson(
5357
- `/threads/${encodeURIComponent(threadId)}/mark-as-unresolved`,
5358
- {
5359
- method: "POST"
5360
- }
5361
- );
5362
- }
5363
- async function createComment({
5364
- threadId,
5365
- commentId = createCommentId(),
5366
- body
5367
- }) {
5368
- const comment = await fetchJson(
5369
- `/threads/${encodeURIComponent(threadId)}/comments`,
5370
- {
5371
- method: "POST",
5372
- headers: {
5373
- "Content-Type": "application/json"
5374
- },
5375
- body: JSON.stringify({
5376
- id: commentId,
5377
- body
5378
- })
5379
- }
5380
- );
5381
- return convertToCommentData(comment);
5382
- }
5383
- async function editComment({
5384
- threadId,
5385
- commentId,
5386
- body
5387
- }) {
5388
- const comment = await fetchJson(
5389
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
5390
- commentId
5391
- )}`,
5392
- {
5393
- method: "POST",
5394
- headers: {
5395
- "Content-Type": "application/json"
5396
- },
5397
- body: JSON.stringify({
5398
- body
5399
- })
5400
- }
5401
- );
5402
- return convertToCommentData(comment);
5403
- }
5404
- async function deleteComment2({
5405
- threadId,
5406
- commentId
5407
- }) {
5408
- await fetchJson(
5409
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
5410
- commentId
5411
- )}`,
5412
- {
5413
- method: "DELETE"
5414
- }
5415
- );
5416
- }
5417
- async function addReaction2({
5418
- threadId,
5419
- commentId,
5420
- emoji
5421
- }) {
5422
- const reaction = await fetchJson(
5423
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
5424
- commentId
5425
- )}/reactions`,
5426
- {
5427
- method: "POST",
5428
- headers: {
5429
- "Content-Type": "application/json"
5430
- },
5431
- body: JSON.stringify({ emoji })
5432
- }
5433
- );
5434
- return convertToCommentUserReaction(reaction);
5435
- }
5436
- async function removeReaction2({
5437
- threadId,
5438
- commentId,
5439
- emoji
5440
- }) {
5441
- await fetchJson(
5442
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
5443
- commentId
5444
- )}/reactions/${encodeURIComponent(emoji)}`,
5445
- {
5446
- method: "DELETE"
5447
- }
5448
- );
5449
- }
5450
- return {
5451
- getThreads,
5452
- getThreadsSince,
5453
- getThread,
5454
- createThread,
5455
- deleteThread,
5456
- editThreadMetadata,
5457
- markThreadAsResolved,
5458
- markThreadAsUnresolved,
5459
- createComment,
5460
- editComment,
5461
- deleteComment: deleteComment2,
5462
- addReaction: addReaction2,
5463
- removeReaction: removeReaction2
5464
- };
5465
- }
5466
- var MARK_INBOX_NOTIFICATIONS_AS_READ_BATCH_DELAY2 = 50;
5467
- function createRoom(options, config) {
5468
- const initialPresence = options.initialPresence;
5469
- const initialStorage = options.initialStorage;
5470
- const [inBackgroundSince, uninstallBgTabSpy] = installBackgroundTabSpy();
5471
- const delegates = {
5472
- ...config.delegates,
5473
- // A connection is allowed to go into "zombie state" only if all of the
5474
- // following conditions apply:
5475
- //
5476
- // - The `backgroundKeepAliveTimeout` client option is configured
5477
- // - The browser window has been in the background for at least
5478
- // `backgroundKeepAliveTimeout` milliseconds
5479
- // - There are no pending changes
5480
- //
5481
- canZombie() {
5482
- return config.backgroundKeepAliveTimeout !== void 0 && inBackgroundSince.current !== null && Date.now() > inBackgroundSince.current + config.backgroundKeepAliveTimeout && getStorageStatus() !== "synchronizing";
5483
- }
5484
- };
5485
- const managedSocket = new ManagedSocket(
5486
- delegates,
5487
- config.enableDebugLogging
5488
- );
5489
- const context = {
5490
- buffer: {
5491
- flushTimerID: void 0,
5492
- lastFlushedAt: 0,
5493
- presenceUpdates: (
5494
- // Queue up the initial presence message as a Full Presence™ update
5495
- {
5496
- type: "full",
5497
- data: initialPresence
5498
- }
5499
- ),
5500
- messages: [],
5501
- storageOperations: []
5502
- },
5503
- staticSessionInfo: new ValueRef(null),
5504
- dynamicSessionInfo: new ValueRef(null),
5505
- myPresence: new PatchableRef(initialPresence),
5506
- others: new OthersRef(),
5507
- initialStorage,
5508
- idFactory: null,
5509
- // Y.js
5510
- provider: void 0,
5511
- onProviderUpdate: makeEventSource(),
5512
- // Storage
5513
- clock: 0,
5514
- opClock: 0,
5515
- nodes: /* @__PURE__ */ new Map(),
5516
- root: void 0,
5517
- undoStack: [],
5518
- redoStack: [],
5519
- pausedHistory: null,
5520
- activeBatch: null,
5521
- unacknowledgedOps: /* @__PURE__ */ new Map(),
5522
- // Debug
5523
- opStackTraces: process.env.NODE_ENV !== "production" ? /* @__PURE__ */ new Map() : void 0
5524
- };
5525
- const doNotBatchUpdates = (cb) => cb();
5526
- const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates;
5527
- let lastTokenKey;
5528
- function onStatusDidChange(newStatus) {
5529
- const authValue = managedSocket.authValue;
5530
- if (authValue !== null) {
5531
- const tokenKey = getAuthBearerHeaderFromAuthValue(authValue);
5532
- if (tokenKey !== lastTokenKey) {
5533
- lastTokenKey = tokenKey;
5534
- if (authValue.type === "secret") {
5535
- const token = authValue.token.parsed;
5536
- context.staticSessionInfo.set({
5537
- userId: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.id : token.uid,
5538
- userInfo: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.info : token.ui
5539
- });
5540
- } else {
5541
- context.staticSessionInfo.set({
5542
- userId: void 0,
5543
- userInfo: void 0
5544
- });
5545
- }
5546
- }
5547
- }
5548
- batchUpdates(() => {
5549
- eventHub.status.notify(newStatus);
5550
- notifySelfChanged(doNotBatchUpdates);
5551
- });
5552
- }
5553
- let _connectionLossTimerId;
5554
- let _hasLostConnection = false;
5555
- function handleConnectionLossEvent(newStatus) {
5556
- if (newStatus === "reconnecting") {
5557
- _connectionLossTimerId = setTimeout(() => {
5558
- batchUpdates(() => {
5559
- eventHub.lostConnection.notify("lost");
5560
- _hasLostConnection = true;
5561
- context.others.clearOthers();
5562
- notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
5563
- });
5564
- }, config.lostConnectionTimeout);
5565
- } else {
5566
- clearTimeout(_connectionLossTimerId);
5567
- if (_hasLostConnection) {
5568
- if (newStatus === "disconnected") {
5569
- batchUpdates(() => {
5570
- eventHub.lostConnection.notify("failed");
5571
- });
5572
- } else {
5573
- batchUpdates(() => {
5574
- eventHub.lostConnection.notify("restored");
5575
- });
5576
- }
5577
- _hasLostConnection = false;
5578
- }
5579
- }
5580
- }
5581
- function onDidConnect() {
5582
- context.buffer.presenceUpdates = {
5583
- type: "full",
5584
- data: (
5585
- // Because context.me.current is a readonly object, we'll have to
5586
- // make a copy here. Otherwise, type errors happen later when
5587
- // "patching" my presence.
5588
- { ...context.myPresence.current }
5589
- )
5590
- };
5591
- if (_getStorage$ !== null) {
5592
- refreshStorage({ flush: false });
5593
- }
5594
- flushNowOrSoon();
5595
- }
5596
- function onDidDisconnect() {
5597
- clearTimeout(context.buffer.flushTimerID);
5333
+ function onDidDisconnect() {
5334
+ clearTimeout(context.buffer.flushTimerID);
5598
5335
  }
5599
5336
  managedSocket.events.onMessage.subscribe(handleServerMessage);
5600
5337
  managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
@@ -6592,16 +6329,411 @@ ${Array.from(traces).join("\n\n")}`
6592
6329
  ydoc: eventHub.ydoc.observable,
6593
6330
  comments: eventHub.comments.observable
6594
6331
  };
6595
- const commentsApi = createCommentsApi(
6596
- config.roomId,
6597
- delegates.authenticate,
6598
- fetchClientApi
6599
- );
6600
- async function fetchNotificationsJson(endpoint, options2) {
6332
+ async function fetchCommentsApi(endpoint, params, options2) {
6601
6333
  const authValue = await delegates.authenticate();
6602
- const response = await fetchClientApi(
6603
- config.roomId,
6604
- endpoint,
6334
+ return fetchClientApi(config.roomId, endpoint, authValue, options2, params);
6335
+ }
6336
+ async function fetchCommentsJson(endpoint, options2, params) {
6337
+ const response = await fetchCommentsApi(endpoint, params, options2);
6338
+ if (!response.ok) {
6339
+ if (response.status >= 400 && response.status < 600) {
6340
+ let error3;
6341
+ try {
6342
+ const errorBody = await response.json();
6343
+ error3 = new CommentsApiError(
6344
+ errorBody.message,
6345
+ response.status,
6346
+ errorBody
6347
+ );
6348
+ } catch {
6349
+ error3 = new CommentsApiError(response.statusText, response.status);
6350
+ }
6351
+ throw error3;
6352
+ }
6353
+ }
6354
+ let body;
6355
+ try {
6356
+ body = await response.json();
6357
+ } catch {
6358
+ body = {};
6359
+ }
6360
+ return body;
6361
+ }
6362
+ async function getThreadsSince(options2) {
6363
+ const response = await fetchCommentsApi(
6364
+ "/threads",
6365
+ {
6366
+ since: options2?.since?.toISOString()
6367
+ },
6368
+ {
6369
+ headers: {
6370
+ "Content-Type": "application/json"
6371
+ }
6372
+ }
6373
+ );
6374
+ if (response.ok) {
6375
+ const json = await response.json();
6376
+ return {
6377
+ threads: {
6378
+ updated: json.data.map(convertToThreadData),
6379
+ deleted: json.deletedThreads.map(convertToThreadDeleteInfo)
6380
+ },
6381
+ inboxNotifications: {
6382
+ updated: json.inboxNotifications.map(convertToInboxNotificationData),
6383
+ deleted: json.deletedInboxNotifications.map(
6384
+ convertToInboxNotificationDeleteInfo
6385
+ )
6386
+ },
6387
+ requestedAt: new Date(json.meta.requestedAt)
6388
+ };
6389
+ } else if (response.status === 404) {
6390
+ return {
6391
+ threads: {
6392
+ updated: [],
6393
+ deleted: []
6394
+ },
6395
+ inboxNotifications: {
6396
+ updated: [],
6397
+ deleted: []
6398
+ },
6399
+ requestedAt: /* @__PURE__ */ new Date()
6400
+ };
6401
+ } else {
6402
+ throw new Error("There was an error while getting threads.");
6403
+ }
6404
+ }
6405
+ async function getThreads(options2) {
6406
+ let query;
6407
+ if (options2?.query) {
6408
+ query = objectToQuery(options2.query);
6409
+ }
6410
+ const response = await fetchCommentsApi(
6411
+ "/threads",
6412
+ {
6413
+ query
6414
+ },
6415
+ {
6416
+ headers: {
6417
+ "Content-Type": "application/json"
6418
+ }
6419
+ }
6420
+ );
6421
+ if (response.ok) {
6422
+ const json = await response.json();
6423
+ return {
6424
+ threads: json.data.map(convertToThreadData),
6425
+ inboxNotifications: json.inboxNotifications.map(
6426
+ convertToInboxNotificationData
6427
+ ),
6428
+ requestedAt: new Date(json.meta.requestedAt)
6429
+ };
6430
+ } else if (response.status === 404) {
6431
+ return {
6432
+ threads: [],
6433
+ inboxNotifications: [],
6434
+ deletedThreads: [],
6435
+ deletedInboxNotifications: [],
6436
+ requestedAt: /* @__PURE__ */ new Date()
6437
+ };
6438
+ } else {
6439
+ throw new Error("There was an error while getting threads.");
6440
+ }
6441
+ }
6442
+ async function getThread(threadId) {
6443
+ const response = await fetchCommentsApi(
6444
+ `/thread-with-notification/${threadId}`
6445
+ );
6446
+ if (response.ok) {
6447
+ const json = await response.json();
6448
+ return {
6449
+ thread: convertToThreadData(json.thread),
6450
+ inboxNotification: json.inboxNotification ? convertToInboxNotificationData(json.inboxNotification) : void 0
6451
+ };
6452
+ } else if (response.status === 404) {
6453
+ return {
6454
+ thread: void 0,
6455
+ inboxNotification: void 0
6456
+ };
6457
+ } else {
6458
+ throw new Error(`There was an error while getting thread ${threadId}.`);
6459
+ }
6460
+ }
6461
+ async function createThread({
6462
+ metadata,
6463
+ body,
6464
+ commentId = createCommentId(),
6465
+ threadId = createThreadId(),
6466
+ attachmentIds
6467
+ }) {
6468
+ const thread = await fetchCommentsJson("/threads", {
6469
+ method: "POST",
6470
+ headers: {
6471
+ "Content-Type": "application/json"
6472
+ },
6473
+ body: JSON.stringify({
6474
+ id: threadId,
6475
+ comment: {
6476
+ id: commentId,
6477
+ body,
6478
+ attachmentIds
6479
+ },
6480
+ metadata
6481
+ })
6482
+ });
6483
+ return convertToThreadData(thread);
6484
+ }
6485
+ async function deleteThread(threadId) {
6486
+ await fetchCommentsJson(`/threads/${encodeURIComponent(threadId)}`, {
6487
+ method: "DELETE"
6488
+ });
6489
+ }
6490
+ async function editThreadMetadata({
6491
+ metadata,
6492
+ threadId
6493
+ }) {
6494
+ return await fetchCommentsJson(
6495
+ `/threads/${encodeURIComponent(threadId)}/metadata`,
6496
+ {
6497
+ method: "POST",
6498
+ headers: {
6499
+ "Content-Type": "application/json"
6500
+ },
6501
+ body: JSON.stringify(metadata)
6502
+ }
6503
+ );
6504
+ }
6505
+ async function markThreadAsResolved(threadId) {
6506
+ await fetchCommentsJson(
6507
+ `/threads/${encodeURIComponent(threadId)}/mark-as-resolved`,
6508
+ {
6509
+ method: "POST"
6510
+ }
6511
+ );
6512
+ }
6513
+ async function markThreadAsUnresolved(threadId) {
6514
+ await fetchCommentsJson(
6515
+ `/threads/${encodeURIComponent(threadId)}/mark-as-unresolved`,
6516
+ {
6517
+ method: "POST"
6518
+ }
6519
+ );
6520
+ }
6521
+ async function createComment({
6522
+ threadId,
6523
+ commentId = createCommentId(),
6524
+ body,
6525
+ attachmentIds
6526
+ }) {
6527
+ const comment = await fetchCommentsJson(
6528
+ `/threads/${encodeURIComponent(threadId)}/comments`,
6529
+ {
6530
+ method: "POST",
6531
+ headers: {
6532
+ "Content-Type": "application/json"
6533
+ },
6534
+ body: JSON.stringify({
6535
+ id: commentId,
6536
+ body,
6537
+ attachmentIds
6538
+ })
6539
+ }
6540
+ );
6541
+ return convertToCommentData(comment);
6542
+ }
6543
+ async function editComment({
6544
+ threadId,
6545
+ commentId,
6546
+ body,
6547
+ attachmentIds
6548
+ }) {
6549
+ const comment = await fetchCommentsJson(
6550
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
6551
+ commentId
6552
+ )}`,
6553
+ {
6554
+ method: "POST",
6555
+ headers: {
6556
+ "Content-Type": "application/json"
6557
+ },
6558
+ body: JSON.stringify({
6559
+ body,
6560
+ attachmentIds
6561
+ })
6562
+ }
6563
+ );
6564
+ return convertToCommentData(comment);
6565
+ }
6566
+ async function deleteComment2({
6567
+ threadId,
6568
+ commentId
6569
+ }) {
6570
+ await fetchCommentsJson(
6571
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
6572
+ commentId
6573
+ )}`,
6574
+ {
6575
+ method: "DELETE"
6576
+ }
6577
+ );
6578
+ }
6579
+ async function addReaction2({
6580
+ threadId,
6581
+ commentId,
6582
+ emoji
6583
+ }) {
6584
+ const reaction = await fetchCommentsJson(
6585
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
6586
+ commentId
6587
+ )}/reactions`,
6588
+ {
6589
+ method: "POST",
6590
+ headers: {
6591
+ "Content-Type": "application/json"
6592
+ },
6593
+ body: JSON.stringify({ emoji })
6594
+ }
6595
+ );
6596
+ return convertToCommentUserReaction(reaction);
6597
+ }
6598
+ async function removeReaction2({
6599
+ threadId,
6600
+ commentId,
6601
+ emoji
6602
+ }) {
6603
+ await fetchCommentsJson(
6604
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
6605
+ commentId
6606
+ )}/reactions/${encodeURIComponent(emoji)}`,
6607
+ {
6608
+ method: "DELETE"
6609
+ }
6610
+ );
6611
+ }
6612
+ function prepareAttachment(file) {
6613
+ return {
6614
+ type: "localAttachment",
6615
+ status: "idle",
6616
+ id: createCommentAttachmentId(),
6617
+ name: file.name,
6618
+ size: file.size,
6619
+ mimeType: file.type,
6620
+ file
6621
+ };
6622
+ }
6623
+ async function uploadAttachment(attachment, options2 = {}) {
6624
+ const abortSignal = options2.signal;
6625
+ const abortError = abortSignal ? new DOMException(
6626
+ `Upload of attachment ${attachment.id} was aborted.`,
6627
+ "AbortError"
6628
+ ) : void 0;
6629
+ if (abortSignal?.aborted) {
6630
+ throw abortError;
6631
+ }
6632
+ if (attachment.size <= ATTACHMENT_PART_SIZE) {
6633
+ return fetchCommentsJson(
6634
+ `/attachments/${encodeURIComponent(attachment.id)}/upload/${encodeURIComponent(attachment.name)}`,
6635
+ {
6636
+ method: "PUT",
6637
+ body: attachment.file,
6638
+ signal: abortSignal
6639
+ }
6640
+ );
6641
+ } else {
6642
+ let uploadId;
6643
+ const uploadedParts = [];
6644
+ try {
6645
+ const createMultiPartUpload = await fetchCommentsJson(
6646
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(attachment.name)}`,
6647
+ {
6648
+ method: "POST",
6649
+ signal: abortSignal
6650
+ }
6651
+ );
6652
+ uploadId = createMultiPartUpload.uploadId;
6653
+ const parts = splitFileIntoParts(attachment.file);
6654
+ if (abortSignal?.aborted) {
6655
+ throw abortError;
6656
+ }
6657
+ const batches = chunk(parts, ATTACHMENT_PART_BATCH_SIZE);
6658
+ for (const parts2 of batches) {
6659
+ const uploadedPartsPromises = [];
6660
+ for (const { part, partNumber } of parts2) {
6661
+ uploadedPartsPromises.push(
6662
+ fetchCommentsJson(
6663
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}/${encodeURIComponent(partNumber)}`,
6664
+ {
6665
+ method: "PUT",
6666
+ body: part,
6667
+ signal: abortSignal
6668
+ }
6669
+ )
6670
+ );
6671
+ }
6672
+ uploadedParts.push(...await Promise.all(uploadedPartsPromises));
6673
+ }
6674
+ if (abortSignal?.aborted) {
6675
+ throw abortError;
6676
+ }
6677
+ const sortedUploadedParts = uploadedParts.sort(
6678
+ (a, b) => a.partNumber - b.partNumber
6679
+ );
6680
+ return fetchCommentsJson(
6681
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}/complete`,
6682
+ {
6683
+ method: "POST",
6684
+ headers: {
6685
+ "Content-Type": "application/json"
6686
+ },
6687
+ body: JSON.stringify({ parts: sortedUploadedParts }),
6688
+ signal: abortSignal
6689
+ }
6690
+ );
6691
+ } catch (error3) {
6692
+ if (uploadId && error3?.name && (error3.name === "AbortError" || error3.name === "TimeoutError")) {
6693
+ await fetchCommentsApi(
6694
+ `/attachments/${encodeURIComponent(attachment.id)}/multipart/${encodeURIComponent(uploadId)}`,
6695
+ void 0,
6696
+ {
6697
+ method: "DELETE"
6698
+ }
6699
+ );
6700
+ }
6701
+ throw error3;
6702
+ }
6703
+ }
6704
+ }
6705
+ async function getAttachmentUrls(attachmentIds) {
6706
+ const { urls } = await fetchCommentsJson(
6707
+ "/attachments/presigned-urls",
6708
+ {
6709
+ method: "POST",
6710
+ headers: {
6711
+ "Content-Type": "application/json"
6712
+ },
6713
+ body: JSON.stringify({ attachmentIds })
6714
+ }
6715
+ );
6716
+ return urls;
6717
+ }
6718
+ const batchedGetAttachmentUrls = new Batch(
6719
+ async (batchedAttachmentIds) => {
6720
+ const attachmentIds = batchedAttachmentIds.flat();
6721
+ const attachmentUrls = await getAttachmentUrls(attachmentIds);
6722
+ return attachmentUrls.map(
6723
+ (url) => url ?? new Error("There was an error while getting this attachment's URL")
6724
+ );
6725
+ },
6726
+ { delay: GET_ATTACHMENT_URLS_BATCH_DELAY }
6727
+ );
6728
+ const attachmentUrlsStore = createBatchStore(batchedGetAttachmentUrls);
6729
+ function getAttachmentUrl(attachmentId) {
6730
+ return batchedGetAttachmentUrls.get(attachmentId);
6731
+ }
6732
+ async function fetchNotificationsJson(endpoint, options2) {
6733
+ const authValue = await delegates.authenticate();
6734
+ const response = await fetchClientApi(
6735
+ config.roomId,
6736
+ endpoint,
6605
6737
  authValue,
6606
6738
  options2
6607
6739
  );
@@ -6706,7 +6838,8 @@ ${Array.from(traces).join("\n\n")}`
6706
6838
  // These exist only for our E2E testing app
6707
6839
  explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
6708
6840
  rawSend: (data) => managedSocket.send(data)
6709
- }
6841
+ },
6842
+ attachmentUrlsStore
6710
6843
  },
6711
6844
  id: config.roomId,
6712
6845
  subscribe: makeClassicSubscribeFn(events),
@@ -6747,10 +6880,27 @@ ${Array.from(traces).join("\n\n")}`
6747
6880
  // Presence
6748
6881
  getPresence: () => context.myPresence.current,
6749
6882
  getOthers: () => context.others.current,
6883
+ // Comments
6884
+ getThreads,
6885
+ getThreadsSince,
6886
+ getThread,
6887
+ createThread,
6888
+ deleteThread,
6889
+ editThreadMetadata,
6890
+ markThreadAsResolved,
6891
+ markThreadAsUnresolved,
6892
+ createComment,
6893
+ editComment,
6894
+ deleteComment: deleteComment2,
6895
+ addReaction: addReaction2,
6896
+ removeReaction: removeReaction2,
6897
+ prepareAttachment,
6898
+ uploadAttachment,
6899
+ getAttachmentUrl,
6900
+ // Notifications
6750
6901
  getNotificationSettings,
6751
6902
  updateNotificationSettings,
6752
- markInboxNotificationAsRead,
6753
- ...commentsApi
6903
+ markInboxNotificationAsRead
6754
6904
  },
6755
6905
  // Explictly make the internal field non-enumerable, to avoid aggressive
6756
6906
  // freezing when used with Immer
@@ -7541,7 +7691,7 @@ function createClient(options) {
7541
7691
  () => !resolveUsers,
7542
7692
  "Set the resolveUsers option in createClient to specify user info."
7543
7693
  );
7544
- const usersStore = createBatchStore(
7694
+ const batchedResolveUsers = new Batch(
7545
7695
  async (batchedUserIds) => {
7546
7696
  const userIds = batchedUserIds.flat();
7547
7697
  const users = await resolveUsers?.({ userIds });
@@ -7550,12 +7700,13 @@ function createClient(options) {
7550
7700
  },
7551
7701
  { delay: RESOLVE_USERS_BATCH_DELAY }
7552
7702
  );
7703
+ const usersStore = createBatchStore(batchedResolveUsers);
7553
7704
  const resolveRoomsInfo = clientOptions.resolveRoomsInfo;
7554
7705
  const warnIfNoResolveRoomsInfo = createDevelopmentWarning(
7555
7706
  () => !resolveRoomsInfo,
7556
7707
  "Set the resolveRoomsInfo option in createClient to specify room info."
7557
7708
  );
7558
- const roomsInfoStore = createBatchStore(
7709
+ const batchedResolveRoomsInfo = new Batch(
7559
7710
  async (batchedRoomIds) => {
7560
7711
  const roomIds = batchedRoomIds.flat();
7561
7712
  const roomsInfo = await resolveRoomsInfo?.({ roomIds });
@@ -7564,6 +7715,7 @@ function createClient(options) {
7564
7715
  },
7565
7716
  { delay: RESOLVE_ROOMS_INFO_BATCH_DELAY }
7566
7717
  );
7718
+ const roomsInfoStore = createBatchStore(batchedResolveRoomsInfo);
7567
7719
  return Object.defineProperty(
7568
7720
  {
7569
7721
  enterRoom,
@@ -8469,6 +8621,7 @@ export {
8469
8621
  assert,
8470
8622
  assertNever,
8471
8623
  b64decode,
8624
+ chunk,
8472
8625
  cloneLson,
8473
8626
  fancy_console_exports as console,
8474
8627
  convertToCommentData,