@liveblocks/core 1.2.0-comments5 → 1.2.0-comments6

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 = "1.2.0-comments5";
9
+ var PKG_VERSION = "1.2.0-comments6";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -637,6 +637,11 @@ var ServerMsgCode = /* @__PURE__ */ ((ServerMsgCode2) => {
637
637
  ServerMsgCode2[ServerMsgCode2["UPDATE_STORAGE"] = 201] = "UPDATE_STORAGE";
638
638
  ServerMsgCode2[ServerMsgCode2["REJECT_STORAGE_OP"] = 299] = "REJECT_STORAGE_OP";
639
639
  ServerMsgCode2[ServerMsgCode2["UPDATE_YDOC"] = 300] = "UPDATE_YDOC";
640
+ ServerMsgCode2[ServerMsgCode2["THREAD_CREATED"] = 400] = "THREAD_CREATED";
641
+ ServerMsgCode2[ServerMsgCode2["THREAD_METADATA_UPDATED"] = 401] = "THREAD_METADATA_UPDATED";
642
+ ServerMsgCode2[ServerMsgCode2["COMMENT_CREATED"] = 402] = "COMMENT_CREATED";
643
+ ServerMsgCode2[ServerMsgCode2["COMMENT_EDITED"] = 403] = "COMMENT_EDITED";
644
+ ServerMsgCode2[ServerMsgCode2["COMMENT_DELETED"] = 404] = "COMMENT_DELETED";
640
645
  return ServerMsgCode2;
641
646
  })(ServerMsgCode || {});
642
647
 
@@ -1250,6 +1255,9 @@ var ManagedSocket = class {
1250
1255
  function canWriteStorage(scopes) {
1251
1256
  return scopes.includes("room:write" /* Write */);
1252
1257
  }
1258
+ function canComment(scopes) {
1259
+ return scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:write" /* Write */);
1260
+ }
1253
1261
  function isValidAuthTokenPayload(data) {
1254
1262
  return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */ || data.k === "sec-legacy" /* SECRET_LEGACY */);
1255
1263
  }
@@ -1662,98 +1670,146 @@ function errorIf(condition, message) {
1662
1670
  }
1663
1671
  }
1664
1672
 
1665
- // src/lib/Json.ts
1666
- function isJsonScalar(data) {
1667
- return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
1668
- }
1669
- function isJsonArray(data) {
1670
- return Array.isArray(data);
1671
- }
1672
- function isJsonObject(data) {
1673
- return !isJsonScalar(data) && !isJsonArray(data);
1674
- }
1675
-
1676
- // src/realtime-client.ts
1677
- function authValueToString(authValue) {
1678
- return authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey;
1673
+ // src/comments/index.ts
1674
+ function getAuthBearerHeaderFromAuthValue(authValue) {
1675
+ if (authValue.type === "public") {
1676
+ return authValue.publicApiKey;
1677
+ } else {
1678
+ return authValue.token.raw;
1679
+ }
1679
1680
  }
1680
- function createRealtimeClient(authManager, serverEndpoint) {
1681
- const eventHub = {
1682
- error: makeEventSource(),
1683
- connection: makeEventSource(),
1684
- events: {}
1685
- };
1686
- let managedSocket = null;
1687
- function createManagedSocket(roomId) {
1688
- managedSocket = new ManagedSocket(
1689
- {
1690
- // TODO: We're trying to (re)connect based on the first roomId that the user is asking for
1691
- // This is bad because the user might now have access to this room (anymore)
1692
- // This prevent any future reconnection to the websocket server
1693
- // We need to find a better way to handle the first (re)connection
1694
- // (Could it be based on the current listeners)
1695
- authenticate: () => authManager.getAuthValue("room:read", roomId),
1696
- createSocket: (authValue) => new WebSocket(
1697
- `${serverEndpoint}?token=${authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey}`
1698
- )
1699
- },
1700
- true,
1701
- false
1702
- );
1703
- managedSocket.events.statusDidChange.subscribe((status) => {
1704
- if (status === "connected") {
1705
- for (const roomId2 in eventHub.events) {
1706
- const eventSource2 = eventHub.events[roomId2];
1707
- if (eventSource2.count() > 0) {
1708
- subscribeToRoomEvents(roomId2);
1709
- }
1681
+ function createCommentsApi(roomId, getAuthValue, { serverEndpoint }) {
1682
+ async function fetchJson(endpoint, options) {
1683
+ const response = await fetchApi(roomId, endpoint, options);
1684
+ if (!response.ok) {
1685
+ if (response.status >= 400 && response.status < 600) {
1686
+ let errorMessage = "";
1687
+ try {
1688
+ const errorBody = await response.json();
1689
+ errorMessage = errorBody.message;
1690
+ } catch (error3) {
1691
+ errorMessage = response.statusText;
1710
1692
  }
1693
+ throw new Error(
1694
+ `Request failed with status ${response.status}: ${errorMessage}`
1695
+ );
1711
1696
  }
1712
- eventHub.connection.notify(status);
1713
- });
1714
- managedSocket.events.onLiveblocksError.subscribe(eventHub.error.notify);
1715
- managedSocket.events.onMessage.subscribe((event) => {
1716
- if (typeof event.data !== "string") {
1717
- return;
1718
- }
1719
- const jsonEvent = tryParseJson(event.data);
1720
- if (jsonEvent !== void 0 && isJsonObject(jsonEvent) && typeof jsonEvent.roomId === "string") {
1721
- eventHub.events[jsonEvent.roomId].notify(jsonEvent);
1697
+ }
1698
+ let body;
1699
+ try {
1700
+ body = await response.json();
1701
+ } catch {
1702
+ body = {};
1703
+ }
1704
+ return body;
1705
+ }
1706
+ async function fetchApi(roomId2, endpoint, options) {
1707
+ const authValue = await getAuthValue();
1708
+ const url = `${serverEndpoint}/c/rooms/${roomId2}${endpoint}`;
1709
+ return await fetch(url, {
1710
+ ...options,
1711
+ headers: {
1712
+ ...options?.headers,
1713
+ Authorization: `Bearer ${getAuthBearerHeaderFromAuthValue(authValue)}`
1722
1714
  }
1723
1715
  });
1724
- managedSocket.connect();
1725
1716
  }
1726
- function getOrCreateEventSource(roomId) {
1727
- let eventSource2 = eventHub.events[roomId];
1728
- if (eventSource2 === void 0) {
1729
- eventSource2 = makeEventSource();
1730
- eventHub.events[roomId] = eventSource2;
1717
+ async function getThreads() {
1718
+ const response = await fetchApi(roomId, "/threads");
1719
+ if (response.ok) {
1720
+ const json = await response.json();
1721
+ return json.data;
1722
+ } else if (response.status === 404) {
1723
+ return [];
1724
+ } else {
1725
+ throw new Error("FAIL");
1731
1726
  }
1732
- return eventSource2;
1733
1727
  }
1734
- async function subscribeToRoomEvents(roomId) {
1735
- const authValue = await authManager.getAuthValue("room:read", roomId);
1736
- if (managedSocket === null || managedSocket.getStatus() !== "connected") {
1737
- return;
1738
- }
1739
- managedSocket.send(
1740
- JSON.stringify({
1741
- type: "subscribeToRooms",
1742
- rooms: [roomId],
1743
- token: authValueToString(authValue)
1728
+ function createThread({
1729
+ metadata,
1730
+ body,
1731
+ commentId,
1732
+ threadId
1733
+ }) {
1734
+ return fetchJson("/threads", {
1735
+ method: "POST",
1736
+ headers: {
1737
+ "Content-Type": "application/json"
1738
+ },
1739
+ body: JSON.stringify({
1740
+ id: threadId,
1741
+ comment: {
1742
+ id: commentId,
1743
+ body
1744
+ },
1745
+ metadata
1744
1746
  })
1747
+ });
1748
+ }
1749
+ function editThreadMetadata({
1750
+ metadata,
1751
+ threadId
1752
+ }) {
1753
+ return fetchJson(
1754
+ `/threads/${threadId}/metadata`,
1755
+ {
1756
+ method: "POST",
1757
+ headers: {
1758
+ "Content-Type": "application/json"
1759
+ },
1760
+ body: JSON.stringify(metadata)
1761
+ }
1745
1762
  );
1746
1763
  }
1747
- return {
1748
- subscribeToEvents: (roomId, callback) => {
1749
- if (!managedSocket) {
1750
- createManagedSocket(roomId);
1764
+ function createComment({
1765
+ threadId,
1766
+ commentId,
1767
+ body
1768
+ }) {
1769
+ return fetchJson(`/threads/${threadId}/comments`, {
1770
+ method: "POST",
1771
+ headers: {
1772
+ "Content-Type": "application/json"
1773
+ },
1774
+ body: JSON.stringify({
1775
+ id: commentId,
1776
+ body
1777
+ })
1778
+ });
1779
+ }
1780
+ function editComment({
1781
+ threadId,
1782
+ commentId,
1783
+ body
1784
+ }) {
1785
+ return fetchJson(
1786
+ `/threads/${threadId}/comments/${commentId}`,
1787
+ {
1788
+ method: "POST",
1789
+ headers: {
1790
+ "Content-Type": "application/json"
1791
+ },
1792
+ body: JSON.stringify({
1793
+ body
1794
+ })
1751
1795
  }
1752
- subscribeToRoomEvents(roomId);
1753
- return getOrCreateEventSource(roomId).subscribe(callback);
1754
- },
1755
- error: eventHub.error.observable,
1756
- connection: eventHub.connection.observable
1796
+ );
1797
+ }
1798
+ async function deleteComment({
1799
+ threadId,
1800
+ commentId
1801
+ }) {
1802
+ await fetchJson(`/threads/${threadId}/comments/${commentId}`, {
1803
+ method: "DELETE"
1804
+ });
1805
+ }
1806
+ return {
1807
+ getThreads,
1808
+ createThread,
1809
+ editThreadMetadata,
1810
+ createComment,
1811
+ editComment,
1812
+ deleteComment
1757
1813
  };
1758
1814
  }
1759
1815
 
@@ -4278,6 +4334,17 @@ function captureStackTrace(msg, traceRoot) {
4278
4334
  return errorLike.stack;
4279
4335
  }
4280
4336
 
4337
+ // src/lib/Json.ts
4338
+ function isJsonScalar(data) {
4339
+ return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
4340
+ }
4341
+ function isJsonArray(data) {
4342
+ return Array.isArray(data);
4343
+ }
4344
+ function isJsonObject(data) {
4345
+ return !isJsonScalar(data) && !isJsonArray(data);
4346
+ }
4347
+
4281
4348
  // src/protocol/ClientMsg.ts
4282
4349
  var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
4283
4350
  ClientMsgCode2[ClientMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
@@ -4348,6 +4415,7 @@ function makeUser(conn, presence) {
4348
4415
  id,
4349
4416
  info,
4350
4417
  canWrite,
4418
+ canComment: canComment(conn.scopes),
4351
4419
  isReadOnly: !canWrite,
4352
4420
  // Deprecated, kept for backward-compatibility
4353
4421
  presence
@@ -4735,7 +4803,8 @@ function createRoom(options, config) {
4735
4803
  history: makeEventSource(),
4736
4804
  storageDidLoad: makeEventSource(),
4737
4805
  storageStatus: makeEventSource(),
4738
- ydoc: makeEventSource()
4806
+ ydoc: makeEventSource(),
4807
+ comments: makeEventSource()
4739
4808
  };
4740
4809
  function sendMessages(messageOrMessages) {
4741
4810
  const message = JSON.stringify(messageOrMessages);
@@ -4776,6 +4845,7 @@ function createRoom(options, config) {
4776
4845
  info: staticSession.userInfo,
4777
4846
  presence: myPresence,
4778
4847
  canWrite,
4848
+ canComment: canComment(dynamicSession.scopes),
4779
4849
  isReadOnly: !canWrite
4780
4850
  // Deprecated, kept for backward-compatibility
4781
4851
  };
@@ -5232,6 +5302,14 @@ ${Array.from(traces).join("\n\n")}`
5232
5302
  }
5233
5303
  break;
5234
5304
  }
5305
+ case 400 /* THREAD_CREATED */:
5306
+ case 401 /* THREAD_METADATA_UPDATED */:
5307
+ case 402 /* COMMENT_CREATED */:
5308
+ case 403 /* COMMENT_EDITED */:
5309
+ case 404 /* COMMENT_DELETED */: {
5310
+ eventHub.comments.notify(message);
5311
+ break;
5312
+ }
5235
5313
  }
5236
5314
  }
5237
5315
  notify(updates, doNotBatchUpdates);
@@ -5494,8 +5572,12 @@ ${Array.from(traces).join("\n\n")}`
5494
5572
  history: eventHub.history.observable,
5495
5573
  storageDidLoad: eventHub.storageDidLoad.observable,
5496
5574
  storageStatus: eventHub.storageStatus.observable,
5497
- ydoc: eventHub.ydoc.observable
5575
+ ydoc: eventHub.ydoc.observable,
5576
+ comments: eventHub.comments.observable
5498
5577
  };
5578
+ const commentsApi = createCommentsApi(config.roomId, delegates.authenticate, {
5579
+ serverEndpoint: "https://api.liveblocks.io/v2"
5580
+ });
5499
5581
  return Object.defineProperty(
5500
5582
  {
5501
5583
  /* NOTE: Exposing __internal here only to allow testing implementation details in unit tests */
@@ -5553,7 +5635,8 @@ ${Array.from(traces).join("\n\n")}`
5553
5635
  getSelf: () => self.current,
5554
5636
  // Presence
5555
5637
  getPresence: () => context.myPresence.current,
5556
- getOthers: () => context.others.current
5638
+ getOthers: () => context.others.current,
5639
+ ...commentsApi
5557
5640
  },
5558
5641
  // Explictly make the __internal field non-enumerable, to avoid aggressive
5559
5642
  // freezing when used with Immer
@@ -5773,13 +5856,6 @@ function createClient(options) {
5773
5856
  }
5774
5857
  }
5775
5858
  return {
5776
- __internal: {
5777
- getAuthValue: authManager.getAuthValue,
5778
- realtimeClient: createRealtimeClient(
5779
- authManager,
5780
- getWsEventServerEndpoint(options)
5781
- )
5782
- },
5783
5859
  getRoom,
5784
5860
  enter,
5785
5861
  leave
@@ -5813,12 +5889,6 @@ function buildLiveblocksHttpSendEndpoint(options, roomId) {
5813
5889
  roomId
5814
5890
  )}/send-message`;
5815
5891
  }
5816
- function getWsEventServerEndpoint(options) {
5817
- if (typeof options.eventsServerEndpoint === "string") {
5818
- return options.eventsServerEndpoint;
5819
- }
5820
- return `wss://events.liveblocks.io/v1`;
5821
- }
5822
5892
 
5823
5893
  // src/crdts/utils.ts
5824
5894
  function toPlainLson(lson) {
@@ -6407,163 +6477,6 @@ function makePoller(callback) {
6407
6477
  };
6408
6478
  }
6409
6479
 
6410
- // src/comments/index.ts
6411
- function createCommentsApi(client, { serverEndpoint }) {
6412
- async function fetchJson(roomId, endpoint, options) {
6413
- const response = await fetchApi(roomId, endpoint, options);
6414
- if (!response.ok) {
6415
- if (response.status >= 400 && response.status < 600) {
6416
- let errorMessage = "";
6417
- try {
6418
- const errorBody = await response.json();
6419
- errorMessage = errorBody.message;
6420
- } catch (error3) {
6421
- errorMessage = response.statusText;
6422
- }
6423
- throw new Error(
6424
- `Request failed with status ${response.status}: ${errorMessage}`
6425
- );
6426
- }
6427
- }
6428
- let body;
6429
- try {
6430
- body = await response.json();
6431
- } catch {
6432
- body = {};
6433
- }
6434
- return body;
6435
- }
6436
- async function fetchApi(roomId, endpoint, options) {
6437
- const authValue = await client.__internal.getAuthValue(
6438
- "comments:read",
6439
- // TODO: Use the right scope
6440
- roomId
6441
- );
6442
- if (authValue.type !== "secret") {
6443
- throw new Error("Only secret key are supported for client.");
6444
- }
6445
- const url = `${serverEndpoint}/rooms/${roomId}${endpoint}`;
6446
- return await fetch(url, {
6447
- ...options,
6448
- headers: {
6449
- ...options?.headers,
6450
- Authorization: `Bearer ${authValue.token.raw}`
6451
- }
6452
- });
6453
- }
6454
- async function getThreads({
6455
- roomId
6456
- }) {
6457
- const response = await fetchApi(roomId, "/threads");
6458
- if (response.ok) {
6459
- const json = await response.json();
6460
- return json.data;
6461
- } else if (response.status === 404) {
6462
- return [];
6463
- } else {
6464
- throw new Error("FAIL");
6465
- }
6466
- }
6467
- function createThread({
6468
- roomId,
6469
- metadata,
6470
- body,
6471
- commentId,
6472
- threadId
6473
- }) {
6474
- return fetchJson(roomId, "/threads", {
6475
- method: "POST",
6476
- headers: {
6477
- "Content-Type": "application/json"
6478
- },
6479
- body: JSON.stringify({
6480
- id: threadId,
6481
- comment: {
6482
- id: commentId,
6483
- body
6484
- },
6485
- metadata
6486
- })
6487
- });
6488
- }
6489
- function editThreadMetadata({
6490
- roomId,
6491
- metadata,
6492
- threadId
6493
- }) {
6494
- return fetchJson(
6495
- roomId,
6496
- `/threads/${threadId}/metadata`,
6497
- {
6498
- method: "POST",
6499
- headers: {
6500
- "Content-Type": "application/json"
6501
- },
6502
- body: JSON.stringify(metadata)
6503
- }
6504
- );
6505
- }
6506
- function createComment({
6507
- roomId,
6508
- threadId,
6509
- commentId,
6510
- body
6511
- }) {
6512
- return fetchJson(roomId, `/threads/${threadId}/comments`, {
6513
- method: "POST",
6514
- headers: {
6515
- "Content-Type": "application/json"
6516
- },
6517
- body: JSON.stringify({
6518
- id: commentId,
6519
- body
6520
- })
6521
- });
6522
- }
6523
- function editComment({
6524
- roomId,
6525
- threadId,
6526
- commentId,
6527
- body
6528
- }) {
6529
- return fetchJson(
6530
- roomId,
6531
- `/threads/${threadId}/comments/${commentId}`,
6532
- {
6533
- method: "POST",
6534
- headers: {
6535
- "Content-Type": "application/json"
6536
- },
6537
- body: JSON.stringify({
6538
- body
6539
- })
6540
- }
6541
- );
6542
- }
6543
- async function deleteComment({
6544
- roomId,
6545
- threadId,
6546
- commentId
6547
- }) {
6548
- await fetchJson(roomId, `/threads/${threadId}/comments/${commentId}`, {
6549
- method: "DELETE"
6550
- });
6551
- }
6552
- return {
6553
- getThreads,
6554
- createThread,
6555
- editThreadMetadata,
6556
- createComment,
6557
- editComment,
6558
- deleteComment
6559
- };
6560
- }
6561
-
6562
- // src/comments/utils.ts
6563
- function isCommentBodyMention(element) {
6564
- return "type" in element && element.type === "mention";
6565
- }
6566
-
6567
6480
  // src/index.ts
6568
6481
  detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
6569
6482
  export {
@@ -6590,7 +6503,6 @@ export {
6590
6503
  errorIf,
6591
6504
  freeze,
6592
6505
  isChildCrdt,
6593
- isCommentBodyMention,
6594
6506
  isJsonArray,
6595
6507
  isJsonObject,
6596
6508
  isJsonScalar,