@liveblocks/core 3.21.0 → 3.22.0-file1

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.js 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 = "3.21.0";
9
+ var PKG_VERSION = "3.22.0-file1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -1309,6 +1309,7 @@ var nanoid = (t = 21) => crypto.getRandomValues(new Uint8Array(t)).reduce(
1309
1309
  var THREAD_ID_PREFIX = "th";
1310
1310
  var COMMENT_ID_PREFIX = "cm";
1311
1311
  var COMMENT_ATTACHMENT_ID_PREFIX = "at";
1312
+ var STORAGE_FILE_ID_PREFIX = "fl";
1312
1313
  var INBOX_NOTIFICATION_ID_PREFIX = "in";
1313
1314
  function createOptimisticId(prefix) {
1314
1315
  return `${prefix}_${nanoid()}`;
@@ -1322,6 +1323,9 @@ function createCommentId() {
1322
1323
  function createCommentAttachmentId() {
1323
1324
  return createOptimisticId(COMMENT_ATTACHMENT_ID_PREFIX);
1324
1325
  }
1326
+ function createStorageFileId() {
1327
+ return createOptimisticId(STORAGE_FILE_ID_PREFIX);
1328
+ }
1325
1329
  function createInboxNotificationId() {
1326
1330
  return createOptimisticId(INBOX_NOTIFICATION_ID_PREFIX);
1327
1331
  }
@@ -1567,6 +1571,120 @@ function isUrl(string) {
1567
1571
  }
1568
1572
 
1569
1573
  // src/api-client.ts
1574
+ var ROOM_FILE_PART_SIZE = 5 * 1024 * 1024;
1575
+ var ROOM_FILE_RETRY_ATTEMPTS = 10;
1576
+ var ROOM_FILE_RETRY_DELAYS = [
1577
+ 2e3,
1578
+ 2e3,
1579
+ 2e3,
1580
+ 2e3,
1581
+ 2e3,
1582
+ 2e3,
1583
+ 2e3,
1584
+ 2e3,
1585
+ 2e3,
1586
+ 2e3
1587
+ ];
1588
+ async function uploadRoomFile({
1589
+ file,
1590
+ signal,
1591
+ abortErrorMessage,
1592
+ uploadSingle,
1593
+ createMultipartUpload,
1594
+ uploadMultipartPart,
1595
+ completeMultipartUpload,
1596
+ abortMultipartUpload
1597
+ }) {
1598
+ const abortError = createAbortError(abortErrorMessage);
1599
+ if (signal?.aborted) {
1600
+ throw abortError;
1601
+ }
1602
+ const handleRetryError = (err) => {
1603
+ if (signal?.aborted) {
1604
+ throw abortError;
1605
+ }
1606
+ if (err instanceof HttpError && err.status === 413) {
1607
+ throw err;
1608
+ }
1609
+ return false;
1610
+ };
1611
+ if (file.size <= ROOM_FILE_PART_SIZE) {
1612
+ return autoRetry(
1613
+ uploadSingle,
1614
+ ROOM_FILE_RETRY_ATTEMPTS,
1615
+ ROOM_FILE_RETRY_DELAYS,
1616
+ handleRetryError
1617
+ );
1618
+ }
1619
+ let uploadId;
1620
+ const uploadedParts = [];
1621
+ const multipartUpload = await autoRetry(
1622
+ createMultipartUpload,
1623
+ ROOM_FILE_RETRY_ATTEMPTS,
1624
+ ROOM_FILE_RETRY_DELAYS,
1625
+ handleRetryError
1626
+ );
1627
+ try {
1628
+ uploadId = multipartUpload.uploadId;
1629
+ if (signal?.aborted) {
1630
+ throw abortError;
1631
+ }
1632
+ const batches = chunk(splitFileIntoParts(file), 5);
1633
+ for (const parts of batches) {
1634
+ const uploadedPartsPromises = [];
1635
+ for (const { part, partNumber } of parts) {
1636
+ uploadedPartsPromises.push(
1637
+ autoRetry(
1638
+ () => uploadMultipartPart(multipartUpload.uploadId, partNumber, part),
1639
+ ROOM_FILE_RETRY_ATTEMPTS,
1640
+ ROOM_FILE_RETRY_DELAYS,
1641
+ handleRetryError
1642
+ )
1643
+ );
1644
+ }
1645
+ uploadedParts.push(...await Promise.all(uploadedPartsPromises));
1646
+ }
1647
+ if (signal?.aborted) {
1648
+ throw abortError;
1649
+ }
1650
+ return completeMultipartUpload(
1651
+ uploadId,
1652
+ uploadedParts.sort((a, b) => a.partNumber - b.partNumber)
1653
+ );
1654
+ } catch (error3) {
1655
+ if (uploadId && isAbortOrTimeoutError(error3)) {
1656
+ try {
1657
+ await abortMultipartUpload(uploadId);
1658
+ } catch {
1659
+ }
1660
+ }
1661
+ throw error3;
1662
+ }
1663
+ }
1664
+ function splitFileIntoParts(file) {
1665
+ const parts = [];
1666
+ let start = 0;
1667
+ while (start < file.size) {
1668
+ const end = Math.min(start + ROOM_FILE_PART_SIZE, file.size);
1669
+ parts.push({
1670
+ partNumber: parts.length + 1,
1671
+ part: file.slice(start, end)
1672
+ });
1673
+ start = end;
1674
+ }
1675
+ return parts;
1676
+ }
1677
+ function isAbortOrTimeoutError(error3) {
1678
+ return error3 instanceof Error && (error3.name === "AbortError" || error3.name === "TimeoutError");
1679
+ }
1680
+ function createAbortError(message) {
1681
+ if (typeof DOMException === "function") {
1682
+ return new DOMException(message, "AbortError");
1683
+ }
1684
+ const error3 = new Error(message);
1685
+ error3.name = "AbortError";
1686
+ return error3;
1687
+ }
1570
1688
  function commentsResourceForVisibility(visibility) {
1571
1689
  if (visibility === "private") {
1572
1690
  return "comments:private";
@@ -1882,151 +2000,128 @@ function createApiClient({
1882
2000
  }
1883
2001
  async function uploadAttachment(options) {
1884
2002
  const roomId = options.roomId;
1885
- const abortSignal = options.signal;
1886
2003
  const attachment = options.attachment;
1887
- const abortError = abortSignal ? new DOMException(
1888
- `Upload of attachment ${options.attachment.id} was aborted.`,
1889
- "AbortError"
1890
- ) : void 0;
1891
- if (abortSignal?.aborted) {
1892
- throw abortError;
1893
- }
1894
- const handleRetryError = (err) => {
1895
- if (abortSignal?.aborted) {
1896
- throw abortError;
1897
- }
1898
- if (err instanceof HttpError && err.status === 413) {
1899
- throw err;
1900
- }
1901
- return false;
1902
- };
1903
- const ATTACHMENT_PART_SIZE = 5 * 1024 * 1024;
1904
- const RETRY_ATTEMPTS = 10;
1905
- const RETRY_DELAYS = [
1906
- 2e3,
1907
- 2e3,
1908
- 2e3,
1909
- 2e3,
1910
- 2e3,
1911
- 2e3,
1912
- 2e3,
1913
- 2e3,
1914
- 2e3,
1915
- 2e3
1916
- ];
1917
- function splitFileIntoParts(file) {
1918
- const parts = [];
1919
- let start = 0;
1920
- while (start < file.size) {
1921
- const end = Math.min(start + ATTACHMENT_PART_SIZE, file.size);
1922
- parts.push({
1923
- partNumber: parts.length + 1,
1924
- part: file.slice(start, end)
1925
- });
1926
- start = end;
1927
- }
1928
- return parts;
1929
- }
1930
- if (attachment.size <= ATTACHMENT_PART_SIZE) {
1931
- return autoRetry(
1932
- async () => httpClient.putBlob(
1933
- url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/upload/${encodeURIComponent(attachment.name)}`,
1934
- await authManager.getAuthValue({
1935
- roomId,
1936
- resource: "comments",
1937
- access: "write"
1938
- }),
1939
- attachment.file,
1940
- { fileSize: attachment.size },
1941
- { signal: abortSignal }
1942
- ),
1943
- RETRY_ATTEMPTS,
1944
- RETRY_DELAYS,
1945
- handleRetryError
1946
- );
1947
- } else {
1948
- let uploadId;
1949
- const uploadedParts = [];
1950
- const createMultiPartUpload = await autoRetry(
1951
- async () => httpClient.post(
1952
- url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${encodeURIComponent(attachment.name)}`,
2004
+ return uploadRoomFile({
2005
+ file: attachment.file,
2006
+ signal: options.signal,
2007
+ abortErrorMessage: `Upload of attachment ${attachment.id} was aborted.`,
2008
+ uploadSingle: async () => httpClient.putBlob(
2009
+ url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/upload/${encodeURIComponent(attachment.name)}`,
2010
+ await authManager.getAuthValue({
2011
+ roomId,
2012
+ resource: "comments",
2013
+ access: "write"
2014
+ }),
2015
+ attachment.file,
2016
+ { fileSize: attachment.size },
2017
+ { signal: options.signal }
2018
+ ),
2019
+ createMultipartUpload: async () => httpClient.post(
2020
+ url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${encodeURIComponent(attachment.name)}`,
2021
+ await authManager.getAuthValue({
2022
+ roomId,
2023
+ resource: "comments",
2024
+ access: "write"
2025
+ }),
2026
+ void 0,
2027
+ { signal: options.signal },
2028
+ { fileSize: attachment.size }
2029
+ ),
2030
+ uploadMultipartPart: async (uploadId, partNumber, part) => httpClient.putBlob(
2031
+ url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}/${String(partNumber)}`,
2032
+ await authManager.getAuthValue({
2033
+ roomId,
2034
+ resource: "comments",
2035
+ access: "write"
2036
+ }),
2037
+ part,
2038
+ void 0,
2039
+ { signal: options.signal }
2040
+ ),
2041
+ completeMultipartUpload: async (uploadId, parts) => httpClient.post(
2042
+ url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}/complete`,
2043
+ await authManager.getAuthValue({
2044
+ roomId,
2045
+ resource: "comments",
2046
+ access: "write"
2047
+ }),
2048
+ { parts },
2049
+ { signal: options.signal }
2050
+ ),
2051
+ abortMultipartUpload: async (uploadId) => {
2052
+ await httpClient.rawDelete(
2053
+ url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}`,
1953
2054
  await authManager.getAuthValue({
1954
2055
  roomId,
1955
2056
  resource: "comments",
1956
2057
  access: "write"
1957
- }),
1958
- void 0,
1959
- { signal: abortSignal },
1960
- { fileSize: attachment.size }
1961
- ),
1962
- RETRY_ATTEMPTS,
1963
- RETRY_DELAYS,
1964
- handleRetryError
1965
- );
1966
- try {
1967
- uploadId = createMultiPartUpload.uploadId;
1968
- const parts = splitFileIntoParts(attachment.file);
1969
- if (abortSignal?.aborted) {
1970
- throw abortError;
1971
- }
1972
- const batches = chunk(parts, 5);
1973
- for (const parts2 of batches) {
1974
- const uploadedPartsPromises = [];
1975
- for (const { part, partNumber } of parts2) {
1976
- uploadedPartsPromises.push(
1977
- autoRetry(
1978
- async () => httpClient.putBlob(
1979
- url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${createMultiPartUpload.uploadId}/${String(partNumber)}`,
1980
- await authManager.getAuthValue({
1981
- roomId,
1982
- resource: "comments",
1983
- access: "write"
1984
- }),
1985
- part,
1986
- void 0,
1987
- { signal: abortSignal }
1988
- ),
1989
- RETRY_ATTEMPTS,
1990
- RETRY_DELAYS,
1991
- handleRetryError
1992
- )
1993
- );
1994
- }
1995
- uploadedParts.push(...await Promise.all(uploadedPartsPromises));
1996
- }
1997
- if (abortSignal?.aborted) {
1998
- throw abortError;
1999
- }
2000
- const sortedUploadedParts = uploadedParts.sort(
2001
- (a, b) => a.partNumber - b.partNumber
2058
+ })
2002
2059
  );
2003
- return httpClient.post(
2004
- url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}/complete`,
2060
+ }
2061
+ });
2062
+ }
2063
+ async function uploadFile(options) {
2064
+ const roomId = options.roomId;
2065
+ const file = options.file;
2066
+ const fileId = createStorageFileId();
2067
+ return uploadRoomFile({
2068
+ file,
2069
+ signal: options.signal,
2070
+ abortErrorMessage: `Upload of file ${fileId} was aborted.`,
2071
+ uploadSingle: async () => httpClient.putBlob(
2072
+ url`/v2/c/rooms/${roomId}/storage-files/${fileId}/upload/${encodeURIComponent(file.name)}`,
2073
+ await authManager.getAuthValue({
2074
+ roomId,
2075
+ resource: "storage",
2076
+ access: "write"
2077
+ }),
2078
+ file,
2079
+ { fileSize: file.size },
2080
+ { signal: options.signal }
2081
+ ),
2082
+ createMultipartUpload: async () => httpClient.post(
2083
+ url`/v2/c/rooms/${roomId}/storage-files/${fileId}/multipart/${encodeURIComponent(file.name)}`,
2084
+ await authManager.getAuthValue({
2085
+ roomId,
2086
+ resource: "storage",
2087
+ access: "write"
2088
+ }),
2089
+ void 0,
2090
+ { signal: options.signal },
2091
+ { fileSize: file.size }
2092
+ ),
2093
+ uploadMultipartPart: async (uploadId, partNumber, part) => httpClient.putBlob(
2094
+ url`/v2/c/rooms/${roomId}/storage-files/${fileId}/multipart/${uploadId}/${String(partNumber)}`,
2095
+ await authManager.getAuthValue({
2096
+ roomId,
2097
+ resource: "storage",
2098
+ access: "write"
2099
+ }),
2100
+ part,
2101
+ void 0,
2102
+ { signal: options.signal }
2103
+ ),
2104
+ completeMultipartUpload: async (uploadId, parts) => httpClient.post(
2105
+ url`/v2/c/rooms/${roomId}/storage-files/${fileId}/multipart/${uploadId}/complete`,
2106
+ await authManager.getAuthValue({
2107
+ roomId,
2108
+ resource: "storage",
2109
+ access: "write"
2110
+ }),
2111
+ { parts },
2112
+ { signal: options.signal }
2113
+ ),
2114
+ abortMultipartUpload: async (uploadId) => {
2115
+ await httpClient.rawDelete(
2116
+ url`/v2/c/rooms/${roomId}/storage-files/${fileId}/multipart/${uploadId}`,
2005
2117
  await authManager.getAuthValue({
2006
2118
  roomId,
2007
- resource: "comments",
2119
+ resource: "storage",
2008
2120
  access: "write"
2009
- }),
2010
- { parts: sortedUploadedParts },
2011
- { signal: abortSignal }
2121
+ })
2012
2122
  );
2013
- } catch (error3) {
2014
- if (uploadId && error3?.name && (error3.name === "AbortError" || error3.name === "TimeoutError")) {
2015
- try {
2016
- await httpClient.rawDelete(
2017
- url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}`,
2018
- await authManager.getAuthValue({
2019
- roomId,
2020
- resource: "comments",
2021
- access: "write"
2022
- })
2023
- );
2024
- } catch {
2025
- }
2026
- }
2027
- throw error3;
2028
2123
  }
2029
- }
2124
+ });
2030
2125
  }
2031
2126
  const attachmentUrlsBatchStoresByRoom = new DefaultMap((roomId) => {
2032
2127
  const batch2 = new Batch(
@@ -2056,6 +2151,34 @@ function createApiClient({
2056
2151
  const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).batch;
2057
2152
  return batch2.get(options.attachmentId);
2058
2153
  }
2154
+ const fileUrlsBatchStoresByRoom = new DefaultMap((roomId) => {
2155
+ const batch2 = new Batch(
2156
+ async (batchedFileIds) => {
2157
+ const fileIds = batchedFileIds.flat();
2158
+ const { urls } = await httpClient.post(
2159
+ url`/v2/c/rooms/${roomId}/storage-files/presigned-urls`,
2160
+ await authManager.getAuthValue({
2161
+ roomId,
2162
+ resource: "storage",
2163
+ access: "read"
2164
+ }),
2165
+ { fileIds }
2166
+ );
2167
+ return urls.map(
2168
+ (url2) => url2 ?? new Error("There was an error while getting this file's URL")
2169
+ );
2170
+ },
2171
+ { delay: 50 }
2172
+ );
2173
+ return createBatchStore(batch2);
2174
+ });
2175
+ function getOrCreateFileUrlsStore(roomId) {
2176
+ return fileUrlsBatchStoresByRoom.getOrCreate(roomId);
2177
+ }
2178
+ function getFileUrl(options) {
2179
+ const batch2 = getOrCreateFileUrlsStore(options.roomId).batch;
2180
+ return batch2.get(options.fileId);
2181
+ }
2059
2182
  async function getSubscriptionSettings(options) {
2060
2183
  return httpClient.get(
2061
2184
  url`/v2/c/rooms/${options.roomId}/subscription-settings`,
@@ -2484,6 +2607,10 @@ function createApiClient({
2484
2607
  getAttachmentUrl,
2485
2608
  uploadAttachment,
2486
2609
  getOrCreateAttachmentUrlsStore,
2610
+ // Room storage files
2611
+ getFileUrl,
2612
+ uploadFile,
2613
+ getOrCreateFileUrlsStore,
2487
2614
  // Room storage
2488
2615
  streamStorage,
2489
2616
  // Notifications
@@ -5815,13 +5942,15 @@ var OpCode = Object.freeze({
5815
5942
  DELETE_CRDT: 5,
5816
5943
  DELETE_OBJECT_KEY: 6,
5817
5944
  CREATE_MAP: 7,
5818
- CREATE_REGISTER: 8
5945
+ CREATE_REGISTER: 8,
5946
+ // 9 and 10 are reserved for the parallel LiveText work.
5947
+ CREATE_FILE: 11
5819
5948
  });
5820
5949
  function isIgnoredOp(op) {
5821
5950
  return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
5822
5951
  }
5823
5952
  function isCreateOp(op) {
5824
- return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
5953
+ return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_FILE || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
5825
5954
  }
5826
5955
 
5827
5956
  // src/protocol/StorageNode.ts
@@ -5829,7 +5958,9 @@ var CrdtType = Object.freeze({
5829
5958
  OBJECT: 0,
5830
5959
  LIST: 1,
5831
5960
  MAP: 2,
5832
- REGISTER: 3
5961
+ REGISTER: 3,
5962
+ // 4 is reserved for the parallel LiveText work.
5963
+ FILE: 5
5833
5964
  });
5834
5965
  function isRootStorageNode(node) {
5835
5966
  return node[0] === "root";
@@ -5846,6 +5977,9 @@ function isMapStorageNode(node) {
5846
5977
  function isRegisterStorageNode(node) {
5847
5978
  return node[1].type === CrdtType.REGISTER;
5848
5979
  }
5980
+ function isFileStorageNode(node) {
5981
+ return node[1].type === CrdtType.FILE;
5982
+ }
5849
5983
  function isCompactRootNode(node) {
5850
5984
  return node[0] === "root";
5851
5985
  }
@@ -5868,6 +6002,9 @@ function* compactNodesToNodeStream(compactNodes) {
5868
6002
  case CrdtType.REGISTER:
5869
6003
  yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
5870
6004
  break;
6005
+ case CrdtType.FILE:
6006
+ yield [cnode[0], { type: CrdtType.FILE, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
6007
+ break;
5871
6008
  default:
5872
6009
  }
5873
6010
  }
@@ -5896,6 +6033,10 @@ function* nodeStreamToCompactNodes(nodes) {
5896
6033
  const id = node[0];
5897
6034
  const crdt = node[1];
5898
6035
  yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
6036
+ } else if (isFileStorageNode(node)) {
6037
+ const id = node[0];
6038
+ const crdt = node[1];
6039
+ yield [id, CrdtType.FILE, crdt.parentId, crdt.parentKey, crdt.data];
5899
6040
  } else {
5900
6041
  }
5901
6042
  }
@@ -6408,6 +6549,91 @@ var AbstractCrdt = class {
6408
6549
  }
6409
6550
  };
6410
6551
 
6552
+ // src/crdts/LiveFile.ts
6553
+ var LiveFile = class _LiveFile extends AbstractCrdt {
6554
+ #data;
6555
+ constructor(data) {
6556
+ super();
6557
+ this.#data = Object.freeze({ ...data });
6558
+ }
6559
+ get data() {
6560
+ return this.#data;
6561
+ }
6562
+ get id() {
6563
+ return this.#data.id;
6564
+ }
6565
+ get name() {
6566
+ return this.#data.name;
6567
+ }
6568
+ get size() {
6569
+ return this.#data.size;
6570
+ }
6571
+ get mimeType() {
6572
+ return this.#data.mimeType;
6573
+ }
6574
+ /** @internal */
6575
+ static _deserialize([id, item], _parentToChildren, pool) {
6576
+ const file = new _LiveFile(item.data);
6577
+ file._attach(id, pool);
6578
+ return file;
6579
+ }
6580
+ /** @internal */
6581
+ _toOps(parentId, parentKey) {
6582
+ if (this._id === void 0) {
6583
+ throw new Error("Cannot serialize LiveFile if parent is missing");
6584
+ }
6585
+ return [
6586
+ {
6587
+ type: OpCode.CREATE_FILE,
6588
+ id: this._id,
6589
+ parentId,
6590
+ parentKey,
6591
+ data: this.#data
6592
+ }
6593
+ ];
6594
+ }
6595
+ /** @internal */
6596
+ _serialize() {
6597
+ if (this.parent.type !== "HasParent") {
6598
+ throw new Error("Cannot serialize LiveFile if parent is missing");
6599
+ }
6600
+ return {
6601
+ type: CrdtType.FILE,
6602
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
6603
+ parentKey: this.parent.key,
6604
+ data: this.#data
6605
+ };
6606
+ }
6607
+ /** @internal */
6608
+ _attachChild(_op) {
6609
+ throw new Error("Method not implemented.");
6610
+ }
6611
+ /** @internal */
6612
+ _detachChild(_crdt) {
6613
+ throw new Error("Method not implemented.");
6614
+ }
6615
+ /** @internal */
6616
+ _apply(op, isLocal) {
6617
+ return super._apply(op, isLocal);
6618
+ }
6619
+ /** @internal */
6620
+ _toTreeNode(key) {
6621
+ return {
6622
+ type: "Json",
6623
+ id: this._id ?? nanoid(),
6624
+ key,
6625
+ payload: this.#data
6626
+ };
6627
+ }
6628
+ /** @internal */
6629
+ _toJSON() {
6630
+ return this.#data;
6631
+ }
6632
+ clone() {
6633
+ return new _LiveFile(this.#data);
6634
+ }
6635
+ };
6636
+
6411
6637
  // src/crdts/LiveRegister.ts
6412
6638
  var LiveRegister = class _LiveRegister extends AbstractCrdt {
6413
6639
  #data;
@@ -8764,6 +8990,8 @@ function creationOpToLiveNode(op) {
8764
8990
  }
8765
8991
  function creationOpToLson(op) {
8766
8992
  switch (op.type) {
8993
+ case OpCode.CREATE_FILE:
8994
+ return new LiveFile(op.data);
8767
8995
  case OpCode.CREATE_REGISTER:
8768
8996
  return op.data;
8769
8997
  case OpCode.CREATE_OBJECT:
@@ -8794,6 +9022,8 @@ function deserialize(node, parentToChildren, pool) {
8794
9022
  return LiveMap._deserialize(node, parentToChildren, pool);
8795
9023
  } else if (isRegisterStorageNode(node)) {
8796
9024
  return LiveRegister._deserialize(node, parentToChildren, pool);
9025
+ } else if (isFileStorageNode(node)) {
9026
+ return LiveFile._deserialize(node, parentToChildren, pool);
8797
9027
  } else {
8798
9028
  throw new Error("Unexpected CRDT type");
8799
9029
  }
@@ -8807,12 +9037,14 @@ function deserializeToLson(node, parentToChildren, pool) {
8807
9037
  return LiveMap._deserialize(node, parentToChildren, pool);
8808
9038
  } else if (isRegisterStorageNode(node)) {
8809
9039
  return node[1].data;
9040
+ } else if (isFileStorageNode(node)) {
9041
+ return LiveFile._deserialize(node, parentToChildren, pool);
8810
9042
  } else {
8811
9043
  throw new Error("Unexpected CRDT type");
8812
9044
  }
8813
9045
  }
8814
9046
  function isLiveStructure(value) {
8815
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
9047
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveFile(value);
8816
9048
  }
8817
9049
  function isLiveNode(value) {
8818
9050
  return isLiveStructure(value) || isLiveRegister(value);
@@ -8826,6 +9058,9 @@ function isLiveMap(value) {
8826
9058
  function isLiveObject(value) {
8827
9059
  return value instanceof LiveObject;
8828
9060
  }
9061
+ function isLiveFile(value) {
9062
+ return value instanceof LiveFile;
9063
+ }
8829
9064
  function isLiveRegister(value) {
8830
9065
  return value instanceof LiveRegister;
8831
9066
  }
@@ -8835,14 +9070,14 @@ function cloneLson(value) {
8835
9070
  function liveNodeToLson(obj) {
8836
9071
  if (obj instanceof LiveRegister) {
8837
9072
  return obj.data;
8838
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
9073
+ } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveFile) {
8839
9074
  return obj;
8840
9075
  } else {
8841
9076
  return assertNever(obj, "Unknown AbstractCrdt");
8842
9077
  }
8843
9078
  }
8844
9079
  function lsonToLiveNode(value) {
8845
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
9080
+ if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveFile) {
8846
9081
  return value;
8847
9082
  } else {
8848
9083
  return new LiveRegister(value);
@@ -8859,6 +9094,8 @@ function dumpPool(pool) {
8859
9094
  value = "<LiveList>";
8860
9095
  } else if (node instanceof LiveMap) {
8861
9096
  value = "<LiveMap>";
9097
+ } else if (node instanceof LiveFile) {
9098
+ value = stringifyOrLog(node.data);
8862
9099
  } else {
8863
9100
  value = "<LiveObject>";
8864
9101
  }
@@ -8955,6 +9192,15 @@ function diffNodeMap(prev, next) {
8955
9192
  data: crdt.data
8956
9193
  });
8957
9194
  break;
9195
+ case CrdtType.FILE:
9196
+ ops.push({
9197
+ type: OpCode.CREATE_FILE,
9198
+ id,
9199
+ parentId: crdt.parentId,
9200
+ parentKey: crdt.parentKey,
9201
+ data: crdt.data
9202
+ });
9203
+ break;
8958
9204
  case CrdtType.LIST:
8959
9205
  ops.push({
8960
9206
  type: OpCode.CREATE_LIST,
@@ -9852,6 +10098,9 @@ function defaultMessageFromContext(context) {
9852
10098
 
9853
10099
  // src/room.ts
9854
10100
  var FEEDS_TIMEOUT = 5e3;
10101
+ function getLiveFileId(file) {
10102
+ return typeof file === "string" ? file : file.id;
10103
+ }
9855
10104
  function connectionAccessFromScopes(scopes) {
9856
10105
  const roomPermissions = normalizeRoomPermissions(scopes);
9857
10106
  const matrix = permissionMatrixFromScopes(roomPermissions);
@@ -10341,7 +10590,7 @@ function createRoom(options, config) {
10341
10590
  );
10342
10591
  output.reverse.pushLeft(applyOpResult.reverse);
10343
10592
  }
10344
- if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
10593
+ if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_FILE) {
10345
10594
  createdNodeIds.add(op.id);
10346
10595
  }
10347
10596
  }
@@ -10385,6 +10634,7 @@ function createRoom(options, config) {
10385
10634
  case OpCode.CREATE_OBJECT:
10386
10635
  case OpCode.CREATE_LIST:
10387
10636
  case OpCode.CREATE_MAP:
10637
+ case OpCode.CREATE_FILE:
10388
10638
  case OpCode.CREATE_REGISTER: {
10389
10639
  if (op.parentId === void 0) {
10390
10640
  return { modified: false };
@@ -11488,6 +11738,17 @@ function createRoom(options, config) {
11488
11738
  function getAttachmentUrl(attachmentId) {
11489
11739
  return httpClient.getAttachmentUrl({ roomId, attachmentId });
11490
11740
  }
11741
+ async function uploadFile(file, options2 = {}) {
11742
+ const data = await httpClient.uploadFile({
11743
+ roomId,
11744
+ file,
11745
+ signal: options2.signal
11746
+ });
11747
+ return new LiveFile(data);
11748
+ }
11749
+ function getFileUrl(file) {
11750
+ return httpClient.getFileUrl({ roomId, fileId: getLiveFileId(file) });
11751
+ }
11491
11752
  function getSubscriptionSettings(options2) {
11492
11753
  return httpClient.getSubscriptionSettings({
11493
11754
  roomId,
@@ -11561,7 +11822,8 @@ function createRoom(options, config) {
11561
11822
  rawSend: (data) => managedSocket.send(data),
11562
11823
  incomingMessage: (data) => handleServerMessage(new MessageEvent("message", { data }))
11563
11824
  },
11564
- attachmentUrlsStore: httpClient.getOrCreateAttachmentUrlsStore(roomId)
11825
+ attachmentUrlsStore: httpClient.getOrCreateAttachmentUrlsStore(roomId),
11826
+ fileUrlsStore: httpClient.getOrCreateFileUrlsStore(roomId)
11565
11827
  },
11566
11828
  id: roomId,
11567
11829
  subscribe: makeClassicSubscribeFn(
@@ -11659,6 +11921,8 @@ ${dumpPool(
11659
11921
  prepareAttachment,
11660
11922
  uploadAttachment,
11661
11923
  getAttachmentUrl,
11924
+ uploadFile,
11925
+ getFileUrl,
11662
11926
  // Notifications
11663
11927
  getNotificationSettings: getSubscriptionSettings,
11664
11928
  getSubscriptionSettings,
@@ -12544,6 +12808,11 @@ function toPlainLson(lson) {
12544
12808
  liveblocksType: "LiveList",
12545
12809
  data: [...lson].map((item) => toPlainLson(item))
12546
12810
  };
12811
+ } else if (lson instanceof LiveFile) {
12812
+ return {
12813
+ liveblocksType: "LiveFile",
12814
+ data: lson.data
12815
+ };
12547
12816
  } else {
12548
12817
  return lson;
12549
12818
  }
@@ -12722,6 +12991,7 @@ export {
12722
12991
  DerivedSignal,
12723
12992
  FeedRequestErrorCode,
12724
12993
  HttpError,
12994
+ LiveFile,
12725
12995
  LiveList,
12726
12996
  LiveMap,
12727
12997
  LiveObject,
@@ -12761,6 +13031,7 @@ export {
12761
13031
  createInboxNotificationId,
12762
13032
  createManagedPool,
12763
13033
  createNotificationSettings,
13034
+ createStorageFileId,
12764
13035
  createThreadId,
12765
13036
  deepLiveify,
12766
13037
  defineAiTool,
@@ -12780,6 +13051,7 @@ export {
12780
13051
  isCommentBodyLink,
12781
13052
  isCommentBodyMention,
12782
13053
  isCommentBodyText,
13054
+ isFileStorageNode,
12783
13055
  isJsonArray,
12784
13056
  isJsonObject,
12785
13057
  isJsonScalar,