@liveblocks/core 3.20.0-rc1 → 3.21.0-exp1
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.cjs +2121 -505
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +546 -157
- package/dist/index.d.ts +546 -157
- package/dist/index.js +2010 -394
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
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.
|
|
9
|
+
var PKG_VERSION = "3.21.0-exp1";
|
|
10
10
|
var PKG_FORMAT = "cjs";
|
|
11
11
|
|
|
12
12
|
// src/dupe-detection.ts
|
|
@@ -1570,7 +1570,6 @@ function isUrl(string) {
|
|
|
1570
1570
|
function createApiClient({
|
|
1571
1571
|
baseUrl,
|
|
1572
1572
|
authManager,
|
|
1573
|
-
currentUserId,
|
|
1574
1573
|
fetchPolyfill
|
|
1575
1574
|
}) {
|
|
1576
1575
|
const httpClient = new HttpClient(baseUrl, fetchPolyfill);
|
|
@@ -1578,8 +1577,9 @@ function createApiClient({
|
|
|
1578
1577
|
const result = await httpClient.get(
|
|
1579
1578
|
url`/v2/c/rooms/${options.roomId}/threads/delta`,
|
|
1580
1579
|
await authManager.getAuthValue({
|
|
1581
|
-
|
|
1582
|
-
|
|
1580
|
+
roomId: options.roomId,
|
|
1581
|
+
resource: "comments",
|
|
1582
|
+
access: "read"
|
|
1583
1583
|
}),
|
|
1584
1584
|
{
|
|
1585
1585
|
since: options.since.toISOString()
|
|
@@ -1617,8 +1617,9 @@ function createApiClient({
|
|
|
1617
1617
|
const result = await httpClient.get(
|
|
1618
1618
|
url`/v2/c/rooms/${options.roomId}/threads`,
|
|
1619
1619
|
await authManager.getAuthValue({
|
|
1620
|
-
|
|
1621
|
-
|
|
1620
|
+
roomId: options.roomId,
|
|
1621
|
+
resource: "comments",
|
|
1622
|
+
access: "read"
|
|
1622
1623
|
}),
|
|
1623
1624
|
{
|
|
1624
1625
|
cursor: options.cursor,
|
|
@@ -1662,8 +1663,9 @@ function createApiClient({
|
|
|
1662
1663
|
const result = await httpClient.get(
|
|
1663
1664
|
url`/v2/c/rooms/${options.roomId}/threads/comments/search`,
|
|
1664
1665
|
await authManager.getAuthValue({
|
|
1665
|
-
|
|
1666
|
-
|
|
1666
|
+
roomId: options.roomId,
|
|
1667
|
+
resource: "comments",
|
|
1668
|
+
access: "read"
|
|
1667
1669
|
}),
|
|
1668
1670
|
{
|
|
1669
1671
|
text: options.query.text,
|
|
@@ -1684,8 +1686,9 @@ function createApiClient({
|
|
|
1684
1686
|
const thread = await httpClient.post(
|
|
1685
1687
|
url`/v2/c/rooms/${options.roomId}/threads`,
|
|
1686
1688
|
await authManager.getAuthValue({
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
+
roomId: options.roomId,
|
|
1690
|
+
resource: "comments",
|
|
1691
|
+
access: "write"
|
|
1689
1692
|
}),
|
|
1690
1693
|
{
|
|
1691
1694
|
id: threadId,
|
|
@@ -1704,8 +1707,9 @@ function createApiClient({
|
|
|
1704
1707
|
await httpClient.delete(
|
|
1705
1708
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}`,
|
|
1706
1709
|
await authManager.getAuthValue({
|
|
1707
|
-
|
|
1708
|
-
|
|
1710
|
+
roomId: options.roomId,
|
|
1711
|
+
resource: "comments",
|
|
1712
|
+
access: "write"
|
|
1709
1713
|
})
|
|
1710
1714
|
);
|
|
1711
1715
|
}
|
|
@@ -1713,8 +1717,9 @@ function createApiClient({
|
|
|
1713
1717
|
const response = await httpClient.rawGet(
|
|
1714
1718
|
url`/v2/c/rooms/${options.roomId}/thread-with-notification/${options.threadId}`,
|
|
1715
1719
|
await authManager.getAuthValue({
|
|
1716
|
-
|
|
1717
|
-
|
|
1720
|
+
roomId: options.roomId,
|
|
1721
|
+
resource: "comments",
|
|
1722
|
+
access: "read"
|
|
1718
1723
|
})
|
|
1719
1724
|
);
|
|
1720
1725
|
if (response.ok) {
|
|
@@ -1740,8 +1745,9 @@ function createApiClient({
|
|
|
1740
1745
|
return await httpClient.post(
|
|
1741
1746
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/metadata`,
|
|
1742
1747
|
await authManager.getAuthValue({
|
|
1743
|
-
|
|
1744
|
-
|
|
1748
|
+
roomId: options.roomId,
|
|
1749
|
+
resource: "comments",
|
|
1750
|
+
access: "write"
|
|
1745
1751
|
}),
|
|
1746
1752
|
options.metadata
|
|
1747
1753
|
);
|
|
@@ -1750,8 +1756,9 @@ function createApiClient({
|
|
|
1750
1756
|
return await httpClient.post(
|
|
1751
1757
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments/${options.commentId}/metadata`,
|
|
1752
1758
|
await authManager.getAuthValue({
|
|
1753
|
-
|
|
1754
|
-
|
|
1759
|
+
roomId: options.roomId,
|
|
1760
|
+
resource: "comments",
|
|
1761
|
+
access: "write"
|
|
1755
1762
|
}),
|
|
1756
1763
|
options.metadata
|
|
1757
1764
|
);
|
|
@@ -1761,8 +1768,9 @@ function createApiClient({
|
|
|
1761
1768
|
const comment = await httpClient.post(
|
|
1762
1769
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments`,
|
|
1763
1770
|
await authManager.getAuthValue({
|
|
1764
|
-
|
|
1765
|
-
|
|
1771
|
+
roomId: options.roomId,
|
|
1772
|
+
resource: "comments",
|
|
1773
|
+
access: "write"
|
|
1766
1774
|
}),
|
|
1767
1775
|
{
|
|
1768
1776
|
id: commentId,
|
|
@@ -1777,8 +1785,9 @@ function createApiClient({
|
|
|
1777
1785
|
const comment = await httpClient.post(
|
|
1778
1786
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments/${options.commentId}`,
|
|
1779
1787
|
await authManager.getAuthValue({
|
|
1780
|
-
|
|
1781
|
-
|
|
1788
|
+
roomId: options.roomId,
|
|
1789
|
+
resource: "comments",
|
|
1790
|
+
access: "write"
|
|
1782
1791
|
}),
|
|
1783
1792
|
{
|
|
1784
1793
|
body: options.body,
|
|
@@ -1792,8 +1801,9 @@ function createApiClient({
|
|
|
1792
1801
|
await httpClient.delete(
|
|
1793
1802
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments/${options.commentId}`,
|
|
1794
1803
|
await authManager.getAuthValue({
|
|
1795
|
-
|
|
1796
|
-
|
|
1804
|
+
roomId: options.roomId,
|
|
1805
|
+
resource: "comments",
|
|
1806
|
+
access: "write"
|
|
1797
1807
|
})
|
|
1798
1808
|
);
|
|
1799
1809
|
}
|
|
@@ -1801,8 +1811,9 @@ function createApiClient({
|
|
|
1801
1811
|
const reaction = await httpClient.post(
|
|
1802
1812
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments/${options.commentId}/reactions`,
|
|
1803
1813
|
await authManager.getAuthValue({
|
|
1804
|
-
|
|
1805
|
-
|
|
1814
|
+
roomId: options.roomId,
|
|
1815
|
+
resource: "comments",
|
|
1816
|
+
access: "write"
|
|
1806
1817
|
}),
|
|
1807
1818
|
{ emoji: options.emoji }
|
|
1808
1819
|
);
|
|
@@ -1812,8 +1823,9 @@ function createApiClient({
|
|
|
1812
1823
|
await httpClient.delete(
|
|
1813
1824
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/comments/${options.commentId}/reactions/${options.emoji}`,
|
|
1814
1825
|
await authManager.getAuthValue({
|
|
1815
|
-
|
|
1816
|
-
|
|
1826
|
+
roomId: options.roomId,
|
|
1827
|
+
resource: "comments",
|
|
1828
|
+
access: "write"
|
|
1817
1829
|
})
|
|
1818
1830
|
);
|
|
1819
1831
|
}
|
|
@@ -1821,8 +1833,9 @@ function createApiClient({
|
|
|
1821
1833
|
await httpClient.post(
|
|
1822
1834
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/mark-as-resolved`,
|
|
1823
1835
|
await authManager.getAuthValue({
|
|
1824
|
-
|
|
1825
|
-
|
|
1836
|
+
roomId: options.roomId,
|
|
1837
|
+
resource: "comments",
|
|
1838
|
+
access: "write"
|
|
1826
1839
|
})
|
|
1827
1840
|
);
|
|
1828
1841
|
}
|
|
@@ -1830,8 +1843,9 @@ function createApiClient({
|
|
|
1830
1843
|
await httpClient.post(
|
|
1831
1844
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/mark-as-unresolved`,
|
|
1832
1845
|
await authManager.getAuthValue({
|
|
1833
|
-
|
|
1834
|
-
|
|
1846
|
+
roomId: options.roomId,
|
|
1847
|
+
resource: "comments",
|
|
1848
|
+
access: "write"
|
|
1835
1849
|
})
|
|
1836
1850
|
);
|
|
1837
1851
|
}
|
|
@@ -1839,8 +1853,9 @@ function createApiClient({
|
|
|
1839
1853
|
const subscription = await httpClient.post(
|
|
1840
1854
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/subscribe`,
|
|
1841
1855
|
await authManager.getAuthValue({
|
|
1842
|
-
|
|
1843
|
-
|
|
1856
|
+
roomId: options.roomId,
|
|
1857
|
+
resource: "comments",
|
|
1858
|
+
access: "read"
|
|
1844
1859
|
})
|
|
1845
1860
|
);
|
|
1846
1861
|
return convertToSubscriptionData(subscription);
|
|
@@ -1849,8 +1864,9 @@ function createApiClient({
|
|
|
1849
1864
|
await httpClient.post(
|
|
1850
1865
|
url`/v2/c/rooms/${options.roomId}/threads/${options.threadId}/unsubscribe`,
|
|
1851
1866
|
await authManager.getAuthValue({
|
|
1852
|
-
|
|
1853
|
-
|
|
1867
|
+
roomId: options.roomId,
|
|
1868
|
+
resource: "comments",
|
|
1869
|
+
access: "read"
|
|
1854
1870
|
})
|
|
1855
1871
|
);
|
|
1856
1872
|
}
|
|
@@ -1906,8 +1922,9 @@ function createApiClient({
|
|
|
1906
1922
|
async () => httpClient.putBlob(
|
|
1907
1923
|
url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/upload/${encodeURIComponent(attachment.name)}`,
|
|
1908
1924
|
await authManager.getAuthValue({
|
|
1909
|
-
|
|
1910
|
-
|
|
1925
|
+
roomId,
|
|
1926
|
+
resource: "comments",
|
|
1927
|
+
access: "write"
|
|
1911
1928
|
}),
|
|
1912
1929
|
attachment.file,
|
|
1913
1930
|
{ fileSize: attachment.size },
|
|
@@ -1924,8 +1941,9 @@ function createApiClient({
|
|
|
1924
1941
|
async () => httpClient.post(
|
|
1925
1942
|
url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${encodeURIComponent(attachment.name)}`,
|
|
1926
1943
|
await authManager.getAuthValue({
|
|
1927
|
-
|
|
1928
|
-
|
|
1944
|
+
roomId,
|
|
1945
|
+
resource: "comments",
|
|
1946
|
+
access: "write"
|
|
1929
1947
|
}),
|
|
1930
1948
|
void 0,
|
|
1931
1949
|
{ signal: abortSignal },
|
|
@@ -1950,8 +1968,9 @@ function createApiClient({
|
|
|
1950
1968
|
async () => httpClient.putBlob(
|
|
1951
1969
|
url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${createMultiPartUpload.uploadId}/${String(partNumber)}`,
|
|
1952
1970
|
await authManager.getAuthValue({
|
|
1953
|
-
|
|
1954
|
-
|
|
1971
|
+
roomId,
|
|
1972
|
+
resource: "comments",
|
|
1973
|
+
access: "write"
|
|
1955
1974
|
}),
|
|
1956
1975
|
part,
|
|
1957
1976
|
void 0,
|
|
@@ -1974,8 +1993,9 @@ function createApiClient({
|
|
|
1974
1993
|
return httpClient.post(
|
|
1975
1994
|
url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}/complete`,
|
|
1976
1995
|
await authManager.getAuthValue({
|
|
1977
|
-
|
|
1978
|
-
|
|
1996
|
+
roomId,
|
|
1997
|
+
resource: "comments",
|
|
1998
|
+
access: "write"
|
|
1979
1999
|
}),
|
|
1980
2000
|
{ parts: sortedUploadedParts },
|
|
1981
2001
|
{ signal: abortSignal }
|
|
@@ -1986,8 +2006,9 @@ function createApiClient({
|
|
|
1986
2006
|
await httpClient.rawDelete(
|
|
1987
2007
|
url`/v2/c/rooms/${roomId}/attachments/${attachment.id}/multipart/${uploadId}`,
|
|
1988
2008
|
await authManager.getAuthValue({
|
|
1989
|
-
|
|
1990
|
-
|
|
2009
|
+
roomId,
|
|
2010
|
+
resource: "comments",
|
|
2011
|
+
access: "write"
|
|
1991
2012
|
})
|
|
1992
2013
|
);
|
|
1993
2014
|
} catch (e8) {
|
|
@@ -2004,8 +2025,9 @@ function createApiClient({
|
|
|
2004
2025
|
const { urls } = await httpClient.post(
|
|
2005
2026
|
url`/v2/c/rooms/${roomId}/attachments/presigned-urls`,
|
|
2006
2027
|
await authManager.getAuthValue({
|
|
2007
|
-
|
|
2008
|
-
|
|
2028
|
+
roomId,
|
|
2029
|
+
resource: "comments",
|
|
2030
|
+
access: "read"
|
|
2009
2031
|
}),
|
|
2010
2032
|
{ attachmentIds }
|
|
2011
2033
|
);
|
|
@@ -2024,109 +2046,13 @@ function createApiClient({
|
|
|
2024
2046
|
const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).batch;
|
|
2025
2047
|
return batch2.get(options.attachmentId);
|
|
2026
2048
|
}
|
|
2027
|
-
async function uploadChatAttachment(options) {
|
|
2028
|
-
const { chatId, attachment, signal } = options;
|
|
2029
|
-
const userId = currentUserId.get();
|
|
2030
|
-
if (userId === void 0) {
|
|
2031
|
-
throw new Error("Attachment upload requires an authenticated user.");
|
|
2032
|
-
}
|
|
2033
|
-
const ATTACHMENT_PART_SIZE = 5 * 1024 * 1024;
|
|
2034
|
-
if (options.attachment.file.size <= ATTACHMENT_PART_SIZE) {
|
|
2035
|
-
await httpClient.putBlob(
|
|
2036
|
-
url`/v2/c/chats/${chatId}/attachments/${attachment.id}/upload/${encodeURIComponent(attachment.file.name)}`,
|
|
2037
|
-
await authManager.getAuthValue({ requestedScope: "comments:read" }),
|
|
2038
|
-
attachment.file,
|
|
2039
|
-
{ fileSize: attachment.file.size },
|
|
2040
|
-
{ signal }
|
|
2041
|
-
);
|
|
2042
|
-
} else {
|
|
2043
|
-
const multipartUpload = await httpClient.post(
|
|
2044
|
-
url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${encodeURIComponent(attachment.file.name)}`,
|
|
2045
|
-
await authManager.getAuthValue({ requestedScope: "comments:read" }),
|
|
2046
|
-
void 0,
|
|
2047
|
-
{ signal },
|
|
2048
|
-
{ fileSize: attachment.file.size }
|
|
2049
|
-
);
|
|
2050
|
-
try {
|
|
2051
|
-
const uploadedParts = [];
|
|
2052
|
-
const parts = [];
|
|
2053
|
-
let start = 0;
|
|
2054
|
-
while (start < attachment.file.size) {
|
|
2055
|
-
const end = Math.min(
|
|
2056
|
-
start + ATTACHMENT_PART_SIZE,
|
|
2057
|
-
attachment.file.size
|
|
2058
|
-
);
|
|
2059
|
-
parts.push({
|
|
2060
|
-
number: parts.length + 1,
|
|
2061
|
-
part: attachment.file.slice(start, end)
|
|
2062
|
-
});
|
|
2063
|
-
start = end;
|
|
2064
|
-
}
|
|
2065
|
-
uploadedParts.push(
|
|
2066
|
-
...await Promise.all(
|
|
2067
|
-
parts.map(async ({ number, part }) => {
|
|
2068
|
-
return await httpClient.putBlob(
|
|
2069
|
-
url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}/${String(number)}`,
|
|
2070
|
-
await authManager.getAuthValue({
|
|
2071
|
-
requestedScope: "comments:read"
|
|
2072
|
-
}),
|
|
2073
|
-
part,
|
|
2074
|
-
void 0,
|
|
2075
|
-
{ signal }
|
|
2076
|
-
);
|
|
2077
|
-
})
|
|
2078
|
-
)
|
|
2079
|
-
);
|
|
2080
|
-
await httpClient.post(
|
|
2081
|
-
url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}/complete`,
|
|
2082
|
-
await authManager.getAuthValue({ requestedScope: "comments:read" }),
|
|
2083
|
-
{ parts: uploadedParts.sort((a, b) => a.number - b.number) },
|
|
2084
|
-
{ signal }
|
|
2085
|
-
);
|
|
2086
|
-
} catch (err) {
|
|
2087
|
-
try {
|
|
2088
|
-
await httpClient.delete(
|
|
2089
|
-
url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}`,
|
|
2090
|
-
await authManager.getAuthValue({ requestedScope: "comments:read" })
|
|
2091
|
-
);
|
|
2092
|
-
} catch (e9) {
|
|
2093
|
-
}
|
|
2094
|
-
throw err;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
const attachmentUrlsBatchStoresByChat = new DefaultMap((chatId) => {
|
|
2099
|
-
const batch2 = new Batch(
|
|
2100
|
-
async (batchedAttachmentIds) => {
|
|
2101
|
-
const attachmentIds = batchedAttachmentIds.flat();
|
|
2102
|
-
const { urls } = await httpClient.post(
|
|
2103
|
-
url`/v2/c/chats/${chatId}/attachments/presigned-urls`,
|
|
2104
|
-
await authManager.getAuthValue({
|
|
2105
|
-
requestedScope: "comments:read"
|
|
2106
|
-
}),
|
|
2107
|
-
{ attachmentIds }
|
|
2108
|
-
);
|
|
2109
|
-
return urls.map(
|
|
2110
|
-
(url2) => _nullishCoalesce(url2, () => ( new Error("There was an error while getting this attachment's URL")))
|
|
2111
|
-
);
|
|
2112
|
-
},
|
|
2113
|
-
{ delay: 50 }
|
|
2114
|
-
);
|
|
2115
|
-
return createBatchStore(batch2);
|
|
2116
|
-
});
|
|
2117
|
-
function getOrCreateChatAttachmentUrlsStore(chatId) {
|
|
2118
|
-
return attachmentUrlsBatchStoresByChat.getOrCreate(chatId);
|
|
2119
|
-
}
|
|
2120
|
-
function getChatAttachmentUrl(options) {
|
|
2121
|
-
const batch2 = getOrCreateChatAttachmentUrlsStore(options.chatId).batch;
|
|
2122
|
-
return batch2.get(options.attachmentId);
|
|
2123
|
-
}
|
|
2124
2049
|
async function getSubscriptionSettings(options) {
|
|
2125
2050
|
return httpClient.get(
|
|
2126
2051
|
url`/v2/c/rooms/${options.roomId}/subscription-settings`,
|
|
2127
2052
|
await authManager.getAuthValue({
|
|
2128
|
-
|
|
2129
|
-
|
|
2053
|
+
roomId: options.roomId,
|
|
2054
|
+
resource: "comments",
|
|
2055
|
+
access: "read"
|
|
2130
2056
|
}),
|
|
2131
2057
|
void 0,
|
|
2132
2058
|
{
|
|
@@ -2138,8 +2064,9 @@ function createApiClient({
|
|
|
2138
2064
|
return httpClient.post(
|
|
2139
2065
|
url`/v2/c/rooms/${options.roomId}/subscription-settings`,
|
|
2140
2066
|
await authManager.getAuthValue({
|
|
2141
|
-
|
|
2142
|
-
|
|
2067
|
+
roomId: options.roomId,
|
|
2068
|
+
resource: "comments",
|
|
2069
|
+
access: "read"
|
|
2143
2070
|
}),
|
|
2144
2071
|
options.settings
|
|
2145
2072
|
);
|
|
@@ -2151,8 +2078,9 @@ function createApiClient({
|
|
|
2151
2078
|
await httpClient.post(
|
|
2152
2079
|
url`/v2/c/rooms/${roomId}/inbox-notifications/read`,
|
|
2153
2080
|
await authManager.getAuthValue({
|
|
2154
|
-
|
|
2155
|
-
|
|
2081
|
+
roomId,
|
|
2082
|
+
resource: "comments",
|
|
2083
|
+
access: "read"
|
|
2156
2084
|
}),
|
|
2157
2085
|
{ inboxNotificationIds }
|
|
2158
2086
|
);
|
|
@@ -2172,8 +2100,9 @@ function createApiClient({
|
|
|
2172
2100
|
await httpClient.rawPost(
|
|
2173
2101
|
url`/v2/c/rooms/${options.roomId}/text-mentions`,
|
|
2174
2102
|
await authManager.getAuthValue({
|
|
2175
|
-
|
|
2176
|
-
|
|
2103
|
+
roomId: options.roomId,
|
|
2104
|
+
resource: "storage",
|
|
2105
|
+
access: "write"
|
|
2177
2106
|
}),
|
|
2178
2107
|
{
|
|
2179
2108
|
userId: options.mention.kind === "user" ? options.mention.id : void 0,
|
|
@@ -2187,8 +2116,9 @@ function createApiClient({
|
|
|
2187
2116
|
await httpClient.rawDelete(
|
|
2188
2117
|
url`/v2/c/rooms/${options.roomId}/text-mentions/${options.mentionId}`,
|
|
2189
2118
|
await authManager.getAuthValue({
|
|
2190
|
-
|
|
2191
|
-
|
|
2119
|
+
roomId: options.roomId,
|
|
2120
|
+
resource: "storage",
|
|
2121
|
+
access: "write"
|
|
2192
2122
|
})
|
|
2193
2123
|
);
|
|
2194
2124
|
}
|
|
@@ -2196,8 +2126,9 @@ function createApiClient({
|
|
|
2196
2126
|
return httpClient.rawGet(
|
|
2197
2127
|
url`/v2/c/rooms/${options.roomId}/y-version/${options.versionId}`,
|
|
2198
2128
|
await authManager.getAuthValue({
|
|
2199
|
-
|
|
2200
|
-
|
|
2129
|
+
roomId: options.roomId,
|
|
2130
|
+
resource: "storage",
|
|
2131
|
+
access: "read"
|
|
2201
2132
|
})
|
|
2202
2133
|
);
|
|
2203
2134
|
}
|
|
@@ -2205,8 +2136,9 @@ function createApiClient({
|
|
|
2205
2136
|
await httpClient.rawPost(
|
|
2206
2137
|
url`/v2/c/rooms/${options.roomId}/version`,
|
|
2207
2138
|
await authManager.getAuthValue({
|
|
2208
|
-
|
|
2209
|
-
|
|
2139
|
+
roomId: options.roomId,
|
|
2140
|
+
resource: "storage",
|
|
2141
|
+
access: "write"
|
|
2210
2142
|
})
|
|
2211
2143
|
);
|
|
2212
2144
|
}
|
|
@@ -2214,8 +2146,9 @@ function createApiClient({
|
|
|
2214
2146
|
await httpClient.rawPost(
|
|
2215
2147
|
url`/v2/c/rooms/${options.roomId}/text-metadata`,
|
|
2216
2148
|
await authManager.getAuthValue({
|
|
2217
|
-
|
|
2218
|
-
|
|
2149
|
+
roomId: options.roomId,
|
|
2150
|
+
resource: "storage",
|
|
2151
|
+
access: "read"
|
|
2219
2152
|
}),
|
|
2220
2153
|
{
|
|
2221
2154
|
type: options.type,
|
|
@@ -2227,8 +2160,9 @@ function createApiClient({
|
|
|
2227
2160
|
const result = await httpClient.post(
|
|
2228
2161
|
url`/v2/c/rooms/${options.roomId}/ai/contextual-prompt`,
|
|
2229
2162
|
await authManager.getAuthValue({
|
|
2230
|
-
|
|
2231
|
-
|
|
2163
|
+
roomId: options.roomId,
|
|
2164
|
+
resource: "storage",
|
|
2165
|
+
access: "read"
|
|
2232
2166
|
}),
|
|
2233
2167
|
{
|
|
2234
2168
|
prompt: options.prompt,
|
|
@@ -2250,8 +2184,9 @@ function createApiClient({
|
|
|
2250
2184
|
const result = await httpClient.get(
|
|
2251
2185
|
url`/v2/c/rooms/${options.roomId}/versions`,
|
|
2252
2186
|
await authManager.getAuthValue({
|
|
2253
|
-
|
|
2254
|
-
|
|
2187
|
+
roomId: options.roomId,
|
|
2188
|
+
resource: "storage",
|
|
2189
|
+
access: "read"
|
|
2255
2190
|
})
|
|
2256
2191
|
);
|
|
2257
2192
|
return {
|
|
@@ -2268,8 +2203,9 @@ function createApiClient({
|
|
|
2268
2203
|
const result = await httpClient.get(
|
|
2269
2204
|
url`/v2/c/rooms/${options.roomId}/versions/delta`,
|
|
2270
2205
|
await authManager.getAuthValue({
|
|
2271
|
-
|
|
2272
|
-
|
|
2206
|
+
roomId: options.roomId,
|
|
2207
|
+
resource: "storage",
|
|
2208
|
+
access: "read"
|
|
2273
2209
|
}),
|
|
2274
2210
|
{ since: options.since.toISOString() },
|
|
2275
2211
|
{ signal: options.signal }
|
|
@@ -2288,8 +2224,9 @@ function createApiClient({
|
|
|
2288
2224
|
const result = await httpClient.rawGet(
|
|
2289
2225
|
url`/v2/c/rooms/${options.roomId}/storage`,
|
|
2290
2226
|
await authManager.getAuthValue({
|
|
2291
|
-
|
|
2292
|
-
|
|
2227
|
+
roomId: options.roomId,
|
|
2228
|
+
resource: "storage",
|
|
2229
|
+
access: "read"
|
|
2293
2230
|
})
|
|
2294
2231
|
);
|
|
2295
2232
|
return await result.json();
|
|
@@ -2302,7 +2239,7 @@ function createApiClient({
|
|
|
2302
2239
|
}
|
|
2303
2240
|
const json = await httpClient.get(
|
|
2304
2241
|
url`/v2/c/inbox-notifications`,
|
|
2305
|
-
await authManager.getAuthValue({
|
|
2242
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2306
2243
|
{
|
|
2307
2244
|
cursor: _optionalChain([options, 'optionalAccess', _22 => _22.cursor]),
|
|
2308
2245
|
limit: PAGE_SIZE,
|
|
@@ -2328,7 +2265,7 @@ function createApiClient({
|
|
|
2328
2265
|
}
|
|
2329
2266
|
const json = await httpClient.get(
|
|
2330
2267
|
url`/v2/c/inbox-notifications/delta`,
|
|
2331
|
-
await authManager.getAuthValue({
|
|
2268
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2332
2269
|
{ since: options.since.toISOString(), query },
|
|
2333
2270
|
{ signal: options.signal }
|
|
2334
2271
|
);
|
|
@@ -2357,7 +2294,7 @@ function createApiClient({
|
|
|
2357
2294
|
}
|
|
2358
2295
|
const { count } = await httpClient.get(
|
|
2359
2296
|
url`/v2/c/inbox-notifications/count`,
|
|
2360
|
-
await authManager.getAuthValue({
|
|
2297
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2361
2298
|
{ query },
|
|
2362
2299
|
{ signal: _optionalChain([options, 'optionalAccess', _25 => _25.signal]) }
|
|
2363
2300
|
);
|
|
@@ -2366,7 +2303,7 @@ function createApiClient({
|
|
|
2366
2303
|
async function markAllInboxNotificationsAsRead() {
|
|
2367
2304
|
await httpClient.post(
|
|
2368
2305
|
url`/v2/c/inbox-notifications/read`,
|
|
2369
|
-
await authManager.getAuthValue({
|
|
2306
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2370
2307
|
{
|
|
2371
2308
|
inboxNotificationIds: "all"
|
|
2372
2309
|
}
|
|
@@ -2375,7 +2312,7 @@ function createApiClient({
|
|
|
2375
2312
|
async function markInboxNotificationsAsRead(inboxNotificationIds) {
|
|
2376
2313
|
await httpClient.post(
|
|
2377
2314
|
url`/v2/c/inbox-notifications/read`,
|
|
2378
|
-
await authManager.getAuthValue({
|
|
2315
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2379
2316
|
{
|
|
2380
2317
|
inboxNotificationIds
|
|
2381
2318
|
}
|
|
@@ -2395,19 +2332,19 @@ function createApiClient({
|
|
|
2395
2332
|
async function deleteAllInboxNotifications() {
|
|
2396
2333
|
await httpClient.delete(
|
|
2397
2334
|
url`/v2/c/inbox-notifications`,
|
|
2398
|
-
await authManager.getAuthValue({
|
|
2335
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" })
|
|
2399
2336
|
);
|
|
2400
2337
|
}
|
|
2401
2338
|
async function deleteInboxNotification(inboxNotificationId) {
|
|
2402
2339
|
await httpClient.delete(
|
|
2403
2340
|
url`/v2/c/inbox-notifications/${inboxNotificationId}`,
|
|
2404
|
-
await authManager.getAuthValue({
|
|
2341
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" })
|
|
2405
2342
|
);
|
|
2406
2343
|
}
|
|
2407
2344
|
async function getNotificationSettings(options) {
|
|
2408
2345
|
return httpClient.get(
|
|
2409
2346
|
url`/v2/c/notification-settings`,
|
|
2410
|
-
await authManager.getAuthValue({
|
|
2347
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2411
2348
|
void 0,
|
|
2412
2349
|
{ signal: _optionalChain([options, 'optionalAccess', _26 => _26.signal]) }
|
|
2413
2350
|
);
|
|
@@ -2415,7 +2352,7 @@ function createApiClient({
|
|
|
2415
2352
|
async function updateNotificationSettings(settings) {
|
|
2416
2353
|
return httpClient.post(
|
|
2417
2354
|
url`/v2/c/notification-settings`,
|
|
2418
|
-
await authManager.getAuthValue({
|
|
2355
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2419
2356
|
settings
|
|
2420
2357
|
);
|
|
2421
2358
|
}
|
|
@@ -2427,7 +2364,7 @@ function createApiClient({
|
|
|
2427
2364
|
const PAGE_SIZE = 50;
|
|
2428
2365
|
const json = await httpClient.get(
|
|
2429
2366
|
url`/v2/c/threads`,
|
|
2430
|
-
await authManager.getAuthValue({
|
|
2367
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2431
2368
|
{
|
|
2432
2369
|
cursor: _optionalChain([options, 'optionalAccess', _28 => _28.cursor]),
|
|
2433
2370
|
query,
|
|
@@ -2448,7 +2385,7 @@ function createApiClient({
|
|
|
2448
2385
|
async function getUserThreadsSince_experimental(options) {
|
|
2449
2386
|
const json = await httpClient.get(
|
|
2450
2387
|
url`/v2/c/threads/delta`,
|
|
2451
|
-
await authManager.getAuthValue({
|
|
2388
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2452
2389
|
{ since: options.since.toISOString() },
|
|
2453
2390
|
{ signal: options.signal }
|
|
2454
2391
|
);
|
|
@@ -2477,7 +2414,8 @@ function createApiClient({
|
|
|
2477
2414
|
const { groups: plainGroups } = await httpClient.post(
|
|
2478
2415
|
url`/v2/c/groups/find`,
|
|
2479
2416
|
await authManager.getAuthValue({
|
|
2480
|
-
|
|
2417
|
+
resource: "personal",
|
|
2418
|
+
access: "write"
|
|
2481
2419
|
}),
|
|
2482
2420
|
{ groupIds }
|
|
2483
2421
|
);
|
|
@@ -2496,7 +2434,7 @@ function createApiClient({
|
|
|
2496
2434
|
async function getUrlMetadata(_url) {
|
|
2497
2435
|
const { metadata } = await httpClient.get(
|
|
2498
2436
|
url`/v2/c/urls/metadata`,
|
|
2499
|
-
await authManager.getAuthValue({
|
|
2437
|
+
await authManager.getAuthValue({ resource: "personal", access: "write" }),
|
|
2500
2438
|
{ url: _url }
|
|
2501
2439
|
);
|
|
2502
2440
|
return metadata;
|
|
@@ -2536,10 +2474,6 @@ function createApiClient({
|
|
|
2536
2474
|
getAttachmentUrl,
|
|
2537
2475
|
uploadAttachment,
|
|
2538
2476
|
getOrCreateAttachmentUrlsStore,
|
|
2539
|
-
// User attachments
|
|
2540
|
-
uploadChatAttachment,
|
|
2541
|
-
getOrCreateChatAttachmentUrlsStore,
|
|
2542
|
-
getChatAttachmentUrl,
|
|
2543
2477
|
// Room storage
|
|
2544
2478
|
streamStorage,
|
|
2545
2479
|
// Notifications
|
|
@@ -2645,7 +2579,7 @@ var HttpClient = class {
|
|
|
2645
2579
|
let body;
|
|
2646
2580
|
try {
|
|
2647
2581
|
body = await response.json();
|
|
2648
|
-
} catch (
|
|
2582
|
+
} catch (e9) {
|
|
2649
2583
|
body = {};
|
|
2650
2584
|
}
|
|
2651
2585
|
return body;
|
|
@@ -3845,7 +3779,7 @@ var ManagedSocket = class {
|
|
|
3845
3779
|
getStatus() {
|
|
3846
3780
|
try {
|
|
3847
3781
|
return toNewConnectionStatus(this.#machine);
|
|
3848
|
-
} catch (
|
|
3782
|
+
} catch (e10) {
|
|
3849
3783
|
return "initial";
|
|
3850
3784
|
}
|
|
3851
3785
|
}
|
|
@@ -3913,6 +3847,7 @@ var ManagedSocket = class {
|
|
|
3913
3847
|
|
|
3914
3848
|
// src/internal.ts
|
|
3915
3849
|
var kInternal = /* @__PURE__ */ Symbol();
|
|
3850
|
+
var kStorageUpdateSource = /* @__PURE__ */ Symbol();
|
|
3916
3851
|
|
|
3917
3852
|
// src/lib/IncrementalJsonParser.ts
|
|
3918
3853
|
var EMPTY_OBJECT = Object.freeze({});
|
|
@@ -5233,22 +5168,327 @@ function createReceivingToolInvocation(invocationId, name, partialArgsText = "")
|
|
|
5233
5168
|
};
|
|
5234
5169
|
}
|
|
5235
5170
|
|
|
5236
|
-
// src/
|
|
5237
|
-
var Permission =
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5171
|
+
// src/permissions.ts
|
|
5172
|
+
var Permission = {
|
|
5173
|
+
/**
|
|
5174
|
+
* Default permission for a room.
|
|
5175
|
+
*/
|
|
5176
|
+
Read: "*:read",
|
|
5177
|
+
Write: "*:write",
|
|
5178
|
+
/**
|
|
5179
|
+
* Legacy aliases for default room permissions.
|
|
5180
|
+
*/
|
|
5181
|
+
RoomWrite: "room:write",
|
|
5182
|
+
RoomRead: "room:read",
|
|
5183
|
+
/**
|
|
5184
|
+
* Storage
|
|
5185
|
+
*/
|
|
5186
|
+
StorageRead: "storage:read",
|
|
5187
|
+
StorageWrite: "storage:write",
|
|
5188
|
+
StorageNone: "storage:none",
|
|
5189
|
+
/**
|
|
5190
|
+
* Comments
|
|
5191
|
+
*/
|
|
5192
|
+
CommentsWrite: "comments:write",
|
|
5193
|
+
CommentsRead: "comments:read",
|
|
5194
|
+
CommentsNone: "comments:none",
|
|
5195
|
+
/**
|
|
5196
|
+
* Feeds
|
|
5197
|
+
*/
|
|
5198
|
+
FeedsRead: "feeds:read",
|
|
5199
|
+
FeedsWrite: "feeds:write",
|
|
5200
|
+
FeedsNone: "feeds:none",
|
|
5201
|
+
/**
|
|
5202
|
+
* Legacy
|
|
5203
|
+
*/
|
|
5204
|
+
LegacyRoomPresenceWrite: "room:presence:write"
|
|
5205
|
+
};
|
|
5206
|
+
var ACCESS_LEVELS = ["none", "read", "write"];
|
|
5207
|
+
var basePermissionScopes = /* @__PURE__ */ new Set([
|
|
5208
|
+
Permission.Read,
|
|
5209
|
+
Permission.Write,
|
|
5210
|
+
Permission.RoomRead,
|
|
5211
|
+
Permission.RoomWrite
|
|
5212
|
+
]);
|
|
5213
|
+
var ACCESS_LEVEL_RANKS = {
|
|
5214
|
+
none: 0,
|
|
5215
|
+
read: 1,
|
|
5216
|
+
write: 2
|
|
5217
|
+
};
|
|
5218
|
+
var PERMISSIONS_BY_RESOURCE = {
|
|
5219
|
+
room: {
|
|
5220
|
+
read: [Permission.Read, Permission.RoomRead],
|
|
5221
|
+
write: [Permission.Write, Permission.RoomWrite]
|
|
5222
|
+
},
|
|
5223
|
+
personal: {
|
|
5224
|
+
write: []
|
|
5225
|
+
},
|
|
5226
|
+
storage: {
|
|
5227
|
+
write: [Permission.StorageWrite],
|
|
5228
|
+
read: [Permission.StorageRead],
|
|
5229
|
+
none: [Permission.StorageNone]
|
|
5230
|
+
},
|
|
5231
|
+
comments: {
|
|
5232
|
+
write: [Permission.CommentsWrite],
|
|
5233
|
+
read: [Permission.CommentsRead],
|
|
5234
|
+
none: [Permission.CommentsNone]
|
|
5235
|
+
},
|
|
5236
|
+
feeds: {
|
|
5237
|
+
write: [Permission.FeedsWrite],
|
|
5238
|
+
read: [Permission.FeedsRead],
|
|
5239
|
+
none: [Permission.FeedsNone]
|
|
5240
|
+
}
|
|
5241
|
+
};
|
|
5242
|
+
var NO_PERMISSION_MATRIX = {
|
|
5243
|
+
room: "none",
|
|
5244
|
+
storage: "none",
|
|
5245
|
+
comments: "none",
|
|
5246
|
+
feeds: "none",
|
|
5247
|
+
personal: "none"
|
|
5248
|
+
};
|
|
5249
|
+
var BASE_PERMISSION_RESOURCE = "room";
|
|
5250
|
+
var ROOM_PERMISSION_RESOURCES = [
|
|
5251
|
+
"storage",
|
|
5252
|
+
"comments",
|
|
5253
|
+
"feeds"
|
|
5254
|
+
];
|
|
5255
|
+
var VALID_PERMISSIONS = new Set(Object.values(Permission));
|
|
5256
|
+
function isPermission(permission) {
|
|
5257
|
+
return VALID_PERMISSIONS.has(permission);
|
|
5258
|
+
}
|
|
5259
|
+
function resolveResourceAccess(scopes, resource) {
|
|
5260
|
+
const permissions = PERMISSIONS_BY_RESOURCE[resource];
|
|
5261
|
+
let resourceAccess;
|
|
5262
|
+
for (const access of ACCESS_LEVELS) {
|
|
5263
|
+
const scopedPermissions = permissions[access];
|
|
5264
|
+
if (scopedPermissions !== void 0 && scopedPermissions.some((permission) => scopes.includes(permission))) {
|
|
5265
|
+
resourceAccess = access;
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
return resourceAccess;
|
|
5269
|
+
}
|
|
5270
|
+
function permissionMatrixFromResolvedScopes(resolved) {
|
|
5271
|
+
if (!resolved.hasDefaultPermission) {
|
|
5272
|
+
return { ...NO_PERMISSION_MATRIX };
|
|
5273
|
+
}
|
|
5274
|
+
const matrix = {
|
|
5275
|
+
...NO_PERMISSION_MATRIX,
|
|
5276
|
+
[BASE_PERMISSION_RESOURCE]: resolved.baseAccess,
|
|
5277
|
+
personal: "write"
|
|
5278
|
+
};
|
|
5279
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5280
|
+
matrix[resource] = _nullishCoalesce(resolved.matrix[resource], () => ( resolved.baseAccess));
|
|
5281
|
+
}
|
|
5282
|
+
return matrix;
|
|
5283
|
+
}
|
|
5284
|
+
function permissionMatrixFromScopes(scopes) {
|
|
5285
|
+
return permissionMatrixFromResolvedScopes(resolvePermissionScopes(scopes));
|
|
5286
|
+
}
|
|
5287
|
+
function resolvePermissionScopes(scopes) {
|
|
5288
|
+
const hasDefaultPermission = scopes.includes(Permission.Write) || scopes.includes(Permission.Read) || scopes.includes(Permission.RoomWrite) || scopes.includes(Permission.RoomRead);
|
|
5289
|
+
const baseAccess = scopes.includes(Permission.Write) || scopes.includes(Permission.RoomWrite) ? "write" : scopes.includes(Permission.Read) || scopes.includes(Permission.RoomRead) ? "read" : "none";
|
|
5290
|
+
const matrix = {};
|
|
5291
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5292
|
+
const access = resolveResourceAccess(scopes, resource);
|
|
5293
|
+
if (access !== void 0) {
|
|
5294
|
+
matrix[resource] = access;
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
return { hasDefaultPermission, baseAccess, matrix };
|
|
5298
|
+
}
|
|
5299
|
+
function hasPermissionAccess(matrix, resource, requiredAccess) {
|
|
5300
|
+
const access = _nullishCoalesce(matrix[resource], () => ( "none"));
|
|
5301
|
+
return ACCESS_LEVEL_RANKS[access] >= ACCESS_LEVEL_RANKS[requiredAccess];
|
|
5302
|
+
}
|
|
5303
|
+
function resolveRoomPermissionMatrix(permissions, roomId) {
|
|
5304
|
+
const matchedPermissions = permissions.filter(
|
|
5305
|
+
(entry) => roomPatternMatches(entry.pattern, roomId)
|
|
5306
|
+
);
|
|
5307
|
+
if (matchedPermissions.length === 0) {
|
|
5308
|
+
return void 0;
|
|
5309
|
+
}
|
|
5310
|
+
let hasDefaultPermission = false;
|
|
5311
|
+
let baseAccess = "none";
|
|
5312
|
+
const explicitMatrix = {};
|
|
5313
|
+
const explicitSpecificity = {};
|
|
5314
|
+
for (const entry of matchedPermissions) {
|
|
5315
|
+
const resolved = resolvePermissionScopes(entry.scopes);
|
|
5316
|
+
const specificity = roomPatternSpecificity(entry.pattern);
|
|
5317
|
+
if (resolved.hasDefaultPermission) {
|
|
5318
|
+
hasDefaultPermission = true;
|
|
5319
|
+
baseAccess = strongestAccess(baseAccess, resolved.baseAccess);
|
|
5320
|
+
}
|
|
5321
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5322
|
+
const access = resolved.matrix[resource];
|
|
5323
|
+
if (access !== void 0) {
|
|
5324
|
+
const currentSpecificity = _nullishCoalesce(explicitSpecificity[resource], () => ( -1));
|
|
5325
|
+
if (specificity > currentSpecificity) {
|
|
5326
|
+
explicitMatrix[resource] = access;
|
|
5327
|
+
explicitSpecificity[resource] = specificity;
|
|
5328
|
+
} else if (specificity === currentSpecificity) {
|
|
5329
|
+
explicitMatrix[resource] = strongestAccess(
|
|
5330
|
+
_nullishCoalesce(explicitMatrix[resource], () => ( "none")),
|
|
5331
|
+
access
|
|
5332
|
+
);
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
return permissionMatrixFromResolvedScopes({
|
|
5338
|
+
hasDefaultPermission,
|
|
5339
|
+
baseAccess,
|
|
5340
|
+
matrix: explicitMatrix
|
|
5341
|
+
});
|
|
5342
|
+
}
|
|
5343
|
+
function normalizeRoomPermissions(permissions) {
|
|
5344
|
+
if (!Array.isArray(permissions)) {
|
|
5345
|
+
throw new Error("Permission list must be an array");
|
|
5346
|
+
}
|
|
5347
|
+
const result = [];
|
|
5348
|
+
for (const permission of permissions) {
|
|
5349
|
+
if (!isPermission(permission)) {
|
|
5350
|
+
throw new Error(`Not a valid permission: ${permission}`);
|
|
5351
|
+
}
|
|
5352
|
+
result.push(permission);
|
|
5353
|
+
}
|
|
5354
|
+
return result;
|
|
5355
|
+
}
|
|
5356
|
+
function normalizeRoomAccesses(accesses) {
|
|
5357
|
+
if (accesses === void 0) {
|
|
5358
|
+
return void 0;
|
|
5359
|
+
}
|
|
5360
|
+
return Object.fromEntries(
|
|
5361
|
+
Object.entries(accesses).map(([id, permissions]) => [
|
|
5362
|
+
id,
|
|
5363
|
+
normalizeRoomPermissions(permissions)
|
|
5364
|
+
])
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
function normalizeUpdateRoomAccesses(accesses) {
|
|
5368
|
+
if (accesses === void 0) {
|
|
5369
|
+
return void 0;
|
|
5370
|
+
}
|
|
5371
|
+
return Object.fromEntries(
|
|
5372
|
+
Object.entries(accesses).map(([id, permissions]) => [
|
|
5373
|
+
id,
|
|
5374
|
+
permissions === null ? null : normalizeRoomPermissions(permissions)
|
|
5375
|
+
])
|
|
5376
|
+
);
|
|
5377
|
+
}
|
|
5378
|
+
function permissionMatrixToScopes(matrix) {
|
|
5379
|
+
const scopes = [];
|
|
5380
|
+
const baseAccess = matrix.room;
|
|
5381
|
+
if (baseAccess !== "none") {
|
|
5382
|
+
scopes.push(permissionForAccessLevel(BASE_PERMISSION_RESOURCE, baseAccess));
|
|
5383
|
+
}
|
|
5384
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5385
|
+
const access = matrix[resource];
|
|
5386
|
+
if (access !== baseAccess) {
|
|
5387
|
+
scopes.push(permissionForAccessLevel(resource, access));
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
return scopes;
|
|
5391
|
+
}
|
|
5392
|
+
function mergeRoomPermissionScopes({
|
|
5393
|
+
defaultAccesses,
|
|
5394
|
+
groupsAccesses,
|
|
5395
|
+
userAccesses
|
|
5396
|
+
}) {
|
|
5397
|
+
const sources = [
|
|
5398
|
+
resolvePermissionScopes(defaultAccesses),
|
|
5399
|
+
mergeResolvedScopesByHighestAccess(
|
|
5400
|
+
groupsAccesses.map(resolvePermissionScopes)
|
|
5401
|
+
),
|
|
5402
|
+
resolvePermissionScopes(userAccesses)
|
|
5403
|
+
];
|
|
5404
|
+
const merged = {
|
|
5405
|
+
hasDefaultPermission: false,
|
|
5406
|
+
baseAccess: "none",
|
|
5407
|
+
matrix: {}
|
|
5408
|
+
};
|
|
5409
|
+
for (const source of sources) {
|
|
5410
|
+
if (source.hasDefaultPermission) {
|
|
5411
|
+
merged.hasDefaultPermission = true;
|
|
5412
|
+
merged.baseAccess = source.baseAccess;
|
|
5413
|
+
}
|
|
5414
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5415
|
+
const access = source.matrix[resource];
|
|
5416
|
+
if (access !== void 0) {
|
|
5417
|
+
merged.matrix[resource] = access;
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
return permissionMatrixToScopes(permissionMatrixFromResolvedScopes(merged));
|
|
5422
|
+
}
|
|
5423
|
+
function mergeResolvedScopesByHighestAccess(sources) {
|
|
5424
|
+
const merged = {
|
|
5425
|
+
hasDefaultPermission: false,
|
|
5426
|
+
baseAccess: "none",
|
|
5427
|
+
matrix: {}
|
|
5428
|
+
};
|
|
5429
|
+
for (const source of sources) {
|
|
5430
|
+
if (source.hasDefaultPermission) {
|
|
5431
|
+
merged.hasDefaultPermission = true;
|
|
5432
|
+
merged.baseAccess = strongestAccess(merged.baseAccess, source.baseAccess);
|
|
5433
|
+
}
|
|
5434
|
+
for (const resource of ROOM_PERMISSION_RESOURCES) {
|
|
5435
|
+
const access = source.matrix[resource];
|
|
5436
|
+
if (access !== void 0) {
|
|
5437
|
+
merged.matrix[resource] = strongestAccess(
|
|
5438
|
+
_nullishCoalesce(merged.matrix[resource], () => ( "none")),
|
|
5439
|
+
access
|
|
5440
|
+
);
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
return merged;
|
|
5445
|
+
}
|
|
5446
|
+
function permissionForAccessLevel(resource, access, field = resource) {
|
|
5447
|
+
const levels = PERMISSIONS_BY_RESOURCE[resource];
|
|
5448
|
+
const permissions = levels[access];
|
|
5449
|
+
if (permissions === void 0 || permissions.length === 0) {
|
|
5450
|
+
throw new Error(
|
|
5451
|
+
`Invalid permission level for ${field}: ${_nullishCoalesce(JSON.stringify(access), () => ( String(access)))}`
|
|
5452
|
+
);
|
|
5453
|
+
}
|
|
5454
|
+
return permissions[0];
|
|
5455
|
+
}
|
|
5456
|
+
function strongestAccess(left, right) {
|
|
5457
|
+
return ACCESS_LEVEL_RANKS[right] > ACCESS_LEVEL_RANKS[left] ? right : left;
|
|
5458
|
+
}
|
|
5459
|
+
function roomPatternMatches(pattern, roomId) {
|
|
5460
|
+
if (pattern.includes("*")) {
|
|
5461
|
+
return roomId.startsWith(pattern.replace("*", ""));
|
|
5462
|
+
}
|
|
5463
|
+
return pattern === roomId;
|
|
5464
|
+
}
|
|
5465
|
+
function roomPatternSpecificity(pattern) {
|
|
5466
|
+
return pattern.replace("*", "").length;
|
|
5467
|
+
}
|
|
5468
|
+
function validatePermissionsSet(scopes) {
|
|
5469
|
+
const unknownScopes = scopes.filter((scope) => !VALID_PERMISSIONS.has(scope));
|
|
5470
|
+
if (unknownScopes.length > 0) {
|
|
5471
|
+
return `Unknown permission scope(s): ${unknownScopes.join(", ")}`;
|
|
5472
|
+
}
|
|
5473
|
+
const baseScopes = scopes.filter((scope) => basePermissionScopes.has(scope));
|
|
5474
|
+
if (baseScopes.length !== 1) {
|
|
5475
|
+
return `Permissions must include exactly one of ${Permission.Read}, ${Permission.Write} (or the legacy aliases ${Permission.RoomRead}, ${Permission.RoomWrite}), got ${baseScopes.length === 0 ? "none" : baseScopes.join(", ")}`;
|
|
5476
|
+
}
|
|
5477
|
+
const seenFeatures = /* @__PURE__ */ new Set();
|
|
5478
|
+
for (const scope of scopes) {
|
|
5479
|
+
if (basePermissionScopes.has(scope) || scope === Permission.LegacyRoomPresenceWrite) {
|
|
5480
|
+
continue;
|
|
5481
|
+
}
|
|
5482
|
+
const feature = scope.slice(0, scope.indexOf(":"));
|
|
5483
|
+
if (seenFeatures.has(feature)) {
|
|
5484
|
+
return `Permissions can include at most one scope per feature, got multiple "${feature}" scopes`;
|
|
5485
|
+
}
|
|
5486
|
+
seenFeatures.add(feature);
|
|
5487
|
+
}
|
|
5488
|
+
return true;
|
|
5251
5489
|
}
|
|
5490
|
+
|
|
5491
|
+
// src/protocol/AuthToken.ts
|
|
5252
5492
|
function isValidAuthTokenPayload(data) {
|
|
5253
5493
|
return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */);
|
|
5254
5494
|
}
|
|
@@ -5287,47 +5527,22 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5287
5527
|
const authentication = prepareAuthentication(authOptions);
|
|
5288
5528
|
const seenTokens = /* @__PURE__ */ new Set();
|
|
5289
5529
|
const tokens = [];
|
|
5290
|
-
const expiryTimes = [];
|
|
5291
5530
|
const requestPromises = /* @__PURE__ */ new Map();
|
|
5292
5531
|
function reset() {
|
|
5293
5532
|
seenTokens.clear();
|
|
5294
5533
|
tokens.length = 0;
|
|
5295
|
-
expiryTimes.length = 0;
|
|
5296
5534
|
requestPromises.clear();
|
|
5297
5535
|
}
|
|
5298
|
-
function hasCorrespondingScopes(requestedScope, scopes) {
|
|
5299
|
-
if (requestedScope === "comments:read") {
|
|
5300
|
-
return scopes.includes("comments:read" /* CommentsRead */) || scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
5301
|
-
} else if (requestedScope === "room:read") {
|
|
5302
|
-
return scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
5303
|
-
}
|
|
5304
|
-
return false;
|
|
5305
|
-
}
|
|
5306
5536
|
function getCachedToken(requestOptions) {
|
|
5307
5537
|
const now2 = Math.ceil(Date.now() / 1e3);
|
|
5308
5538
|
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
5309
|
-
const
|
|
5310
|
-
|
|
5311
|
-
if (expiresAt <= now2) {
|
|
5539
|
+
const cachedToken = tokens[i];
|
|
5540
|
+
if (cachedToken.expiresAt <= now2) {
|
|
5312
5541
|
tokens.splice(i, 1);
|
|
5313
|
-
expiryTimes.splice(i, 1);
|
|
5314
5542
|
continue;
|
|
5315
5543
|
}
|
|
5316
|
-
if (
|
|
5317
|
-
return token;
|
|
5318
|
-
} else if (token.parsed.k === "acc" /* ACCESS_TOKEN */) {
|
|
5319
|
-
if (!requestOptions.roomId && Object.entries(token.parsed.perms).length === 0) {
|
|
5320
|
-
return token;
|
|
5321
|
-
}
|
|
5322
|
-
for (const [resource, scopes] of Object.entries(token.parsed.perms)) {
|
|
5323
|
-
if (!requestOptions.roomId) {
|
|
5324
|
-
if (resource.includes("*") && hasCorrespondingScopes(requestOptions.requestedScope, scopes)) {
|
|
5325
|
-
return token;
|
|
5326
|
-
}
|
|
5327
|
-
} else if (resource.includes("*") && requestOptions.roomId.startsWith(resource.replace("*", "")) || requestOptions.roomId === resource && hasCorrespondingScopes(requestOptions.requestedScope, scopes)) {
|
|
5328
|
-
return token;
|
|
5329
|
-
}
|
|
5330
|
-
}
|
|
5544
|
+
if (cachedTokenSatisfiesRequest(cachedToken, requestOptions)) {
|
|
5545
|
+
return cachedToken.token;
|
|
5331
5546
|
}
|
|
5332
5547
|
}
|
|
5333
5548
|
return void 0;
|
|
@@ -5345,11 +5560,15 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5345
5560
|
});
|
|
5346
5561
|
const parsed = parseAuthToken(response.token);
|
|
5347
5562
|
if (seenTokens.has(parsed.raw)) {
|
|
5563
|
+
const cachedToken = getCachedToken(options);
|
|
5564
|
+
if (_optionalChain([cachedToken, 'optionalAccess', _109 => _109.raw]) === parsed.raw) {
|
|
5565
|
+
return cachedToken;
|
|
5566
|
+
}
|
|
5348
5567
|
throw new StopRetrying(
|
|
5349
5568
|
"The same Liveblocks auth token was issued from the backend before. Caching Liveblocks tokens is not supported."
|
|
5350
5569
|
);
|
|
5351
5570
|
}
|
|
5352
|
-
_optionalChain([onAuthenticate, 'optionalCall',
|
|
5571
|
+
_optionalChain([onAuthenticate, 'optionalCall', _110 => _110(parsed.parsed)]);
|
|
5353
5572
|
return parsed;
|
|
5354
5573
|
}
|
|
5355
5574
|
if (authentication.type === "custom") {
|
|
@@ -5357,7 +5576,7 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5357
5576
|
if (response && typeof response === "object") {
|
|
5358
5577
|
if (typeof response.token === "string") {
|
|
5359
5578
|
const parsed = parseAuthToken(response.token);
|
|
5360
|
-
_optionalChain([onAuthenticate, 'optionalCall',
|
|
5579
|
+
_optionalChain([onAuthenticate, 'optionalCall', _111 => _111(parsed.parsed)]);
|
|
5361
5580
|
return parsed;
|
|
5362
5581
|
} else if (typeof response.error === "string") {
|
|
5363
5582
|
const reason = `Authentication failed: ${"reason" in response && typeof response.reason === "string" ? response.reason : "Forbidden"}`;
|
|
@@ -5385,11 +5604,12 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5385
5604
|
return { type: "secret", token: cachedToken };
|
|
5386
5605
|
}
|
|
5387
5606
|
let currentPromise;
|
|
5388
|
-
|
|
5389
|
-
|
|
5607
|
+
const requestKey = getAuthRequestKey(requestOptions);
|
|
5608
|
+
if (requestKey !== void 0) {
|
|
5609
|
+
currentPromise = requestPromises.get(requestKey);
|
|
5390
5610
|
if (currentPromise === void 0) {
|
|
5391
5611
|
currentPromise = makeAuthRequest(requestOptions);
|
|
5392
|
-
requestPromises.set(
|
|
5612
|
+
requestPromises.set(requestKey, currentPromise);
|
|
5393
5613
|
}
|
|
5394
5614
|
} else {
|
|
5395
5615
|
currentPromise = requestPromises.get("liveblocks-user-token");
|
|
@@ -5403,12 +5623,12 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5403
5623
|
const BUFFER = 30;
|
|
5404
5624
|
const expiresAt = Math.floor(Date.now() / 1e3) + (token.parsed.exp - token.parsed.iat) - BUFFER;
|
|
5405
5625
|
seenTokens.add(token.raw);
|
|
5406
|
-
|
|
5407
|
-
|
|
5626
|
+
const cachedToken2 = makeCachedToken(token, expiresAt);
|
|
5627
|
+
tokens.push(cachedToken2);
|
|
5408
5628
|
return { type: "secret", token };
|
|
5409
5629
|
} finally {
|
|
5410
|
-
if (
|
|
5411
|
-
requestPromises.delete(
|
|
5630
|
+
if (requestKey !== void 0) {
|
|
5631
|
+
requestPromises.delete(requestKey);
|
|
5412
5632
|
} else {
|
|
5413
5633
|
requestPromises.delete("liveblocks-user-token");
|
|
5414
5634
|
}
|
|
@@ -5419,6 +5639,43 @@ function createAuthManager(authOptions, onAuthenticate) {
|
|
|
5419
5639
|
getAuthValue
|
|
5420
5640
|
};
|
|
5421
5641
|
}
|
|
5642
|
+
function getAuthRequestKey(request) {
|
|
5643
|
+
if (request.roomId === void 0) {
|
|
5644
|
+
return void 0;
|
|
5645
|
+
}
|
|
5646
|
+
return `${request.roomId}:${request.resource}:${request.access}`;
|
|
5647
|
+
}
|
|
5648
|
+
function makeCachedToken(token, expiresAt) {
|
|
5649
|
+
if (token.parsed.k === "acc" /* ACCESS_TOKEN */) {
|
|
5650
|
+
return {
|
|
5651
|
+
token,
|
|
5652
|
+
expiresAt,
|
|
5653
|
+
permissions: Object.entries(token.parsed.perms).map(
|
|
5654
|
+
([pattern, scopes]) => ({
|
|
5655
|
+
pattern,
|
|
5656
|
+
scopes: normalizeRoomPermissions(scopes)
|
|
5657
|
+
})
|
|
5658
|
+
)
|
|
5659
|
+
};
|
|
5660
|
+
}
|
|
5661
|
+
return { token, expiresAt };
|
|
5662
|
+
}
|
|
5663
|
+
function cachedTokenSatisfiesRequest(cachedToken, request) {
|
|
5664
|
+
if (cachedToken.token.parsed.k === "id" /* ID_TOKEN */) {
|
|
5665
|
+
return true;
|
|
5666
|
+
}
|
|
5667
|
+
if (request.resource === "personal") {
|
|
5668
|
+
return true;
|
|
5669
|
+
}
|
|
5670
|
+
if (request.roomId === void 0) {
|
|
5671
|
+
return false;
|
|
5672
|
+
}
|
|
5673
|
+
const matrix = resolveRoomPermissionMatrix(
|
|
5674
|
+
_nullishCoalesce(cachedToken.permissions, () => ( [])),
|
|
5675
|
+
request.roomId
|
|
5676
|
+
);
|
|
5677
|
+
return matrix !== void 0 && hasPermissionAccess(matrix, request.resource, request.access);
|
|
5678
|
+
}
|
|
5422
5679
|
function prepareAuthentication(authOptions) {
|
|
5423
5680
|
const { publicApiKey, authEndpoint } = authOptions;
|
|
5424
5681
|
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
|
|
@@ -5511,13 +5768,15 @@ var OpCode = Object.freeze({
|
|
|
5511
5768
|
DELETE_CRDT: 5,
|
|
5512
5769
|
DELETE_OBJECT_KEY: 6,
|
|
5513
5770
|
CREATE_MAP: 7,
|
|
5514
|
-
CREATE_REGISTER: 8
|
|
5771
|
+
CREATE_REGISTER: 8,
|
|
5772
|
+
CREATE_TEXT: 9,
|
|
5773
|
+
UPDATE_TEXT: 10
|
|
5515
5774
|
});
|
|
5516
5775
|
function isIgnoredOp(op) {
|
|
5517
5776
|
return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
|
|
5518
5777
|
}
|
|
5519
5778
|
function isCreateOp(op) {
|
|
5520
|
-
return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
|
|
5779
|
+
return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_TEXT;
|
|
5521
5780
|
}
|
|
5522
5781
|
|
|
5523
5782
|
// src/protocol/StorageNode.ts
|
|
@@ -5525,7 +5784,8 @@ var CrdtType = Object.freeze({
|
|
|
5525
5784
|
OBJECT: 0,
|
|
5526
5785
|
LIST: 1,
|
|
5527
5786
|
MAP: 2,
|
|
5528
|
-
REGISTER: 3
|
|
5787
|
+
REGISTER: 3,
|
|
5788
|
+
TEXT: 4
|
|
5529
5789
|
});
|
|
5530
5790
|
function isRootStorageNode(node) {
|
|
5531
5791
|
return node[0] === "root";
|
|
@@ -5542,6 +5802,9 @@ function isMapStorageNode(node) {
|
|
|
5542
5802
|
function isRegisterStorageNode(node) {
|
|
5543
5803
|
return node[1].type === CrdtType.REGISTER;
|
|
5544
5804
|
}
|
|
5805
|
+
function isTextStorageNode(node) {
|
|
5806
|
+
return node[1].type === CrdtType.TEXT;
|
|
5807
|
+
}
|
|
5545
5808
|
function isCompactRootNode(node) {
|
|
5546
5809
|
return node[0] === "root";
|
|
5547
5810
|
}
|
|
@@ -5564,6 +5827,9 @@ function* compactNodesToNodeStream(compactNodes) {
|
|
|
5564
5827
|
case CrdtType.REGISTER:
|
|
5565
5828
|
yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
|
|
5566
5829
|
break;
|
|
5830
|
+
case CrdtType.TEXT:
|
|
5831
|
+
yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
|
|
5832
|
+
break;
|
|
5567
5833
|
default:
|
|
5568
5834
|
}
|
|
5569
5835
|
}
|
|
@@ -5592,6 +5858,17 @@ function* nodeStreamToCompactNodes(nodes) {
|
|
|
5592
5858
|
const id = node[0];
|
|
5593
5859
|
const crdt = node[1];
|
|
5594
5860
|
yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
|
|
5861
|
+
} else if (isTextStorageNode(node)) {
|
|
5862
|
+
const id = node[0];
|
|
5863
|
+
const crdt = node[1];
|
|
5864
|
+
yield [
|
|
5865
|
+
id,
|
|
5866
|
+
CrdtType.TEXT,
|
|
5867
|
+
crdt.parentId,
|
|
5868
|
+
crdt.parentKey,
|
|
5869
|
+
crdt.data,
|
|
5870
|
+
crdt.version
|
|
5871
|
+
];
|
|
5595
5872
|
} else {
|
|
5596
5873
|
}
|
|
5597
5874
|
}
|
|
@@ -5784,6 +6061,9 @@ var UnacknowledgedOps = class {
|
|
|
5784
6061
|
#createOpsByPosition = /* @__PURE__ */ new Map();
|
|
5785
6062
|
// parentId -> (opId -> Create op)
|
|
5786
6063
|
#createOpsByParent = /* @__PURE__ */ new Map();
|
|
6064
|
+
// opIds of pending ops that were in flight when a connection died, so the
|
|
6065
|
+
// server may already have processed them. See isPossiblyStored().
|
|
6066
|
+
#possiblyStoredOpIds = /* @__PURE__ */ new Set();
|
|
5787
6067
|
#posKey(parentId, parentKey) {
|
|
5788
6068
|
return `${parentId}
|
|
5789
6069
|
${parentKey}`;
|
|
@@ -5791,6 +6071,10 @@ ${parentKey}`;
|
|
|
5791
6071
|
get size() {
|
|
5792
6072
|
return this.#byOpId.size;
|
|
5793
6073
|
}
|
|
6074
|
+
/** The still-unacknowledged op with the given opId, if any. */
|
|
6075
|
+
get(opId) {
|
|
6076
|
+
return this.#byOpId.get(opId);
|
|
6077
|
+
}
|
|
5794
6078
|
/**
|
|
5795
6079
|
* Mark the given Op as still unacknowledged.
|
|
5796
6080
|
*/
|
|
@@ -5823,15 +6107,16 @@ ${parentKey}`;
|
|
|
5823
6107
|
return;
|
|
5824
6108
|
}
|
|
5825
6109
|
this.#byOpId.delete(opId);
|
|
6110
|
+
this.#possiblyStoredOpIds.delete(opId);
|
|
5826
6111
|
if (isCreateOp(op)) {
|
|
5827
6112
|
const posKey = this.#posKey(op.parentId, op.parentKey);
|
|
5828
6113
|
const atPosition = this.#createOpsByPosition.get(posKey);
|
|
5829
|
-
_optionalChain([atPosition, 'optionalAccess',
|
|
6114
|
+
_optionalChain([atPosition, 'optionalAccess', _112 => _112.delete, 'call', _113 => _113(opId)]);
|
|
5830
6115
|
if (atPosition !== void 0 && atPosition.size === 0) {
|
|
5831
6116
|
this.#createOpsByPosition.delete(posKey);
|
|
5832
6117
|
}
|
|
5833
6118
|
const inParent = this.#createOpsByParent.get(op.parentId);
|
|
5834
|
-
_optionalChain([inParent, 'optionalAccess',
|
|
6119
|
+
_optionalChain([inParent, 'optionalAccess', _114 => _114.delete, 'call', _115 => _115(opId)]);
|
|
5835
6120
|
if (inParent !== void 0 && inParent.size === 0) {
|
|
5836
6121
|
this.#createOpsByParent.delete(op.parentId);
|
|
5837
6122
|
}
|
|
@@ -5843,19 +6128,32 @@ ${parentKey}`;
|
|
|
5843
6128
|
* Empty if none.
|
|
5844
6129
|
*/
|
|
5845
6130
|
getByParentIdAndKey(parentId, parentKey) {
|
|
5846
|
-
return _nullishCoalesce(_optionalChain([this, 'access',
|
|
6131
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _116 => _116.#createOpsByPosition, 'access', _117 => _117.get, 'call', _118 => _118(this.#posKey(parentId, parentKey)), 'optionalAccess', _119 => _119.values, 'call', _120 => _120()]), () => ( []));
|
|
5847
6132
|
}
|
|
5848
6133
|
/**
|
|
5849
6134
|
* The still-unacknowledged Create ops with the given `parentId` (across all
|
|
5850
6135
|
* positions), in dispatch order. O(1) lookup. Empty if none.
|
|
5851
6136
|
*/
|
|
5852
6137
|
getByParentId(parentId) {
|
|
5853
|
-
return _nullishCoalesce(_optionalChain([this, 'access',
|
|
6138
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _121 => _121.#createOpsByParent, 'access', _122 => _122.get, 'call', _123 => _123(parentId), 'optionalAccess', _124 => _124.values, 'call', _125 => _125()]), () => ( []));
|
|
5854
6139
|
}
|
|
5855
6140
|
/** All still-unacknowledged ops, in dispatch order. */
|
|
5856
6141
|
values() {
|
|
5857
6142
|
return this.#byOpId.values();
|
|
5858
6143
|
}
|
|
6144
|
+
isPossiblyStored(opId) {
|
|
6145
|
+
return this.#possiblyStoredOpIds.has(opId);
|
|
6146
|
+
}
|
|
6147
|
+
/**
|
|
6148
|
+
* Mark every currently pending op as possibly stored on the server. Called
|
|
6149
|
+
* when the connection dies: all of these ops were in flight, and their
|
|
6150
|
+
* (possibly lost) acks would have been the only way to know their fate.
|
|
6151
|
+
*/
|
|
6152
|
+
markAllAsPossiblyStored() {
|
|
6153
|
+
for (const opId of this.#byOpId.keys()) {
|
|
6154
|
+
this.#possiblyStoredOpIds.add(opId);
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
5859
6157
|
};
|
|
5860
6158
|
|
|
5861
6159
|
// src/crdts/AbstractCrdt.ts
|
|
@@ -5877,8 +6175,8 @@ function createManagedPool(roomId, options) {
|
|
|
5877
6175
|
deleteNode: (id) => void nodes.delete(id),
|
|
5878
6176
|
generateId: () => `${getCurrentConnectionId()}:${clock++}`,
|
|
5879
6177
|
generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
|
|
5880
|
-
dispatch(ops, reverse, storageUpdates) {
|
|
5881
|
-
_optionalChain([onDispatch, 'optionalCall',
|
|
6178
|
+
dispatch(ops, reverse, storageUpdates, options2) {
|
|
6179
|
+
_optionalChain([onDispatch, 'optionalCall', _126 => _126(ops, reverse, storageUpdates, options2)]);
|
|
5882
6180
|
},
|
|
5883
6181
|
assertStorageIsWritable: () => {
|
|
5884
6182
|
if (!isStorageWritable()) {
|
|
@@ -6452,7 +6750,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6452
6750
|
}
|
|
6453
6751
|
return result.modified.updates[0];
|
|
6454
6752
|
}
|
|
6455
|
-
#applyRemoteInsert(op
|
|
6753
|
+
#applyRemoteInsert(op) {
|
|
6456
6754
|
if (this._pool === void 0) {
|
|
6457
6755
|
throw new Error("Can't attach child if managed pool is not present");
|
|
6458
6756
|
}
|
|
@@ -6462,7 +6760,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6462
6760
|
this.#shiftItemPosition(existingItemIndex, key);
|
|
6463
6761
|
}
|
|
6464
6762
|
const { newItem, newIndex } = this.#createAttachItemAndSort(op, key);
|
|
6465
|
-
const bumpDeltas =
|
|
6763
|
+
const bumpDeltas = this.#bumpUnackedPushesAbove(key);
|
|
6466
6764
|
return {
|
|
6467
6765
|
modified: makeUpdate(this, [
|
|
6468
6766
|
insertDelta(newIndex, newItem),
|
|
@@ -6477,6 +6775,13 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6477
6775
|
* the single source of truth, so an item drops out the instant its op is
|
|
6478
6776
|
* acked, with no per-instance membership to leak. Yielded in push order.
|
|
6479
6777
|
*
|
|
6778
|
+
* Excludes ops that may already be stored on the server (they were in
|
|
6779
|
+
* flight when a connection died, so their fate is unknown): the bump
|
|
6780
|
+
* prediction assumes the server has not processed the op yet, which is only
|
|
6781
|
+
* guaranteed for ops sent on the current connection. For these excluded
|
|
6782
|
+
* ops, the server's (re-)ack states the authoritative position; predicting
|
|
6783
|
+
* locally could produce a wrong position that no ack would correct.
|
|
6784
|
+
*
|
|
6480
6785
|
* Restricted to items currently in `#items`: a pushed node whose op is still
|
|
6481
6786
|
* pending may have been pulled out of the list (e.g. implicitly deleted by a
|
|
6482
6787
|
* remote set, or removed by an undo) while still living in the pool, and such
|
|
@@ -6490,6 +6795,9 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6490
6795
|
if (op.intent !== "push") {
|
|
6491
6796
|
continue;
|
|
6492
6797
|
}
|
|
6798
|
+
if (this._pool.unacknowledgedOps.isPossiblyStored(op.opId)) {
|
|
6799
|
+
continue;
|
|
6800
|
+
}
|
|
6493
6801
|
const node = this._pool.getNode(op.id);
|
|
6494
6802
|
if (node !== void 0 && this.#items.includes(node)) {
|
|
6495
6803
|
yield node;
|
|
@@ -6592,7 +6900,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6592
6900
|
#applyInsertUndoRedo(op) {
|
|
6593
6901
|
const { id, parentKey: key } = op;
|
|
6594
6902
|
const child = creationOpToLiveNode(op);
|
|
6595
|
-
if (_optionalChain([this, 'access',
|
|
6903
|
+
if (_optionalChain([this, 'access', _127 => _127._pool, 'optionalAccess', _128 => _128.getNode, 'call', _129 => _129(id)]) !== void 0) {
|
|
6596
6904
|
return { modified: false };
|
|
6597
6905
|
}
|
|
6598
6906
|
child._attach(id, nn(this._pool));
|
|
@@ -6600,8 +6908,8 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6600
6908
|
const existingItemIndex = this._indexOfPosition(key);
|
|
6601
6909
|
let newKey = key;
|
|
6602
6910
|
if (existingItemIndex !== -1) {
|
|
6603
|
-
const before2 = _optionalChain([this, 'access',
|
|
6604
|
-
const after2 = _optionalChain([this, 'access',
|
|
6911
|
+
const before2 = _optionalChain([this, 'access', _130 => _130.#items, 'access', _131 => _131.at, 'call', _132 => _132(existingItemIndex), 'optionalAccess', _133 => _133._parentPos]);
|
|
6912
|
+
const after2 = _optionalChain([this, 'access', _134 => _134.#items, 'access', _135 => _135.at, 'call', _136 => _136(existingItemIndex + 1), 'optionalAccess', _137 => _137._parentPos]);
|
|
6605
6913
|
newKey = makePosition(before2, after2);
|
|
6606
6914
|
child._setParentLink(this, newKey);
|
|
6607
6915
|
}
|
|
@@ -6615,7 +6923,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6615
6923
|
#applySetUndoRedo(op) {
|
|
6616
6924
|
const { id, parentKey: key } = op;
|
|
6617
6925
|
const child = creationOpToLiveNode(op);
|
|
6618
|
-
if (_optionalChain([this, 'access',
|
|
6926
|
+
if (_optionalChain([this, 'access', _138 => _138._pool, 'optionalAccess', _139 => _139.getNode, 'call', _140 => _140(id)]) !== void 0) {
|
|
6619
6927
|
return { modified: false };
|
|
6620
6928
|
}
|
|
6621
6929
|
const indexOfItemWithSameKey = this._indexOfPosition(key);
|
|
@@ -6654,7 +6962,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6654
6962
|
}
|
|
6655
6963
|
}
|
|
6656
6964
|
/** @internal */
|
|
6657
|
-
_attachChild(op, source
|
|
6965
|
+
_attachChild(op, source) {
|
|
6658
6966
|
if (this._pool === void 0) {
|
|
6659
6967
|
throw new Error("Can't attach child if managed pool is not present");
|
|
6660
6968
|
}
|
|
@@ -6669,7 +6977,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6669
6977
|
}
|
|
6670
6978
|
} else {
|
|
6671
6979
|
if (source === 1 /* THEIRS */) {
|
|
6672
|
-
result = this.#applyRemoteInsert(op
|
|
6980
|
+
result = this.#applyRemoteInsert(op);
|
|
6673
6981
|
} else if (source === 2 /* OURS */) {
|
|
6674
6982
|
result = this.#applyInsertAck(op);
|
|
6675
6983
|
} else {
|
|
@@ -6736,7 +7044,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6736
7044
|
} else {
|
|
6737
7045
|
this.#updateItemPositionAt(
|
|
6738
7046
|
existingItemIndex,
|
|
6739
|
-
makePosition(newKey, _optionalChain([this, 'access',
|
|
7047
|
+
makePosition(newKey, _optionalChain([this, 'access', _141 => _141.#items, 'access', _142 => _142.at, 'call', _143 => _143(existingItemIndex + 1), 'optionalAccess', _144 => _144._parentPos]))
|
|
6740
7048
|
);
|
|
6741
7049
|
const previousIndex = this.#items.findIndex((item) => item === child);
|
|
6742
7050
|
this.#updateItemPosition(child, newKey);
|
|
@@ -6763,7 +7071,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6763
7071
|
this,
|
|
6764
7072
|
makePosition(
|
|
6765
7073
|
newKey,
|
|
6766
|
-
_optionalChain([this, 'access',
|
|
7074
|
+
_optionalChain([this, 'access', _145 => _145.#items, 'access', _146 => _146.at, 'call', _147 => _147(existingItemIndex + 1), 'optionalAccess', _148 => _148._parentPos])
|
|
6767
7075
|
)
|
|
6768
7076
|
);
|
|
6769
7077
|
this.#items.reposition(existingItem);
|
|
@@ -6787,7 +7095,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6787
7095
|
existingItemIndex,
|
|
6788
7096
|
makePosition(
|
|
6789
7097
|
newKey,
|
|
6790
|
-
_optionalChain([this, 'access',
|
|
7098
|
+
_optionalChain([this, 'access', _149 => _149.#items, 'access', _150 => _150.at, 'call', _151 => _151(existingItemIndex + 1), 'optionalAccess', _152 => _152._parentPos])
|
|
6791
7099
|
)
|
|
6792
7100
|
);
|
|
6793
7101
|
}
|
|
@@ -6815,7 +7123,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6815
7123
|
if (existingItemIndex !== -1) {
|
|
6816
7124
|
actualNewKey = makePosition(
|
|
6817
7125
|
newKey,
|
|
6818
|
-
_optionalChain([this, 'access',
|
|
7126
|
+
_optionalChain([this, 'access', _153 => _153.#items, 'access', _154 => _154.at, 'call', _155 => _155(existingItemIndex + 1), 'optionalAccess', _156 => _156._parentPos])
|
|
6819
7127
|
);
|
|
6820
7128
|
}
|
|
6821
7129
|
this.#updateItemPosition(child, actualNewKey);
|
|
@@ -6889,14 +7197,14 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6889
7197
|
* instead of resolving its position against the client's stale view.
|
|
6890
7198
|
*/
|
|
6891
7199
|
#injectAt(element, index, intent) {
|
|
6892
|
-
_optionalChain([this, 'access',
|
|
7200
|
+
_optionalChain([this, 'access', _157 => _157._pool, 'optionalAccess', _158 => _158.assertStorageIsWritable, 'call', _159 => _159()]);
|
|
6893
7201
|
if (index < 0 || index > this.#items.length) {
|
|
6894
7202
|
throw new Error(
|
|
6895
7203
|
`Cannot insert list item at index "${index}". index should be between 0 and ${this.#items.length}`
|
|
6896
7204
|
);
|
|
6897
7205
|
}
|
|
6898
|
-
const before2 = _optionalChain([this, 'access',
|
|
6899
|
-
const after2 = _optionalChain([this, 'access',
|
|
7206
|
+
const before2 = _optionalChain([this, 'access', _160 => _160.#items, 'access', _161 => _161.at, 'call', _162 => _162(index - 1), 'optionalAccess', _163 => _163._parentPos]);
|
|
7207
|
+
const after2 = _optionalChain([this, 'access', _164 => _164.#items, 'access', _165 => _165.at, 'call', _166 => _166(index), 'optionalAccess', _167 => _167._parentPos]);
|
|
6900
7208
|
const position = makePosition(before2, after2);
|
|
6901
7209
|
const value = lsonToLiveNode(element);
|
|
6902
7210
|
value._setParentLink(this, position);
|
|
@@ -6920,7 +7228,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6920
7228
|
* @param targetIndex The index where the element should be after moving.
|
|
6921
7229
|
*/
|
|
6922
7230
|
move(index, targetIndex) {
|
|
6923
|
-
_optionalChain([this, 'access',
|
|
7231
|
+
_optionalChain([this, 'access', _168 => _168._pool, 'optionalAccess', _169 => _169.assertStorageIsWritable, 'call', _170 => _170()]);
|
|
6924
7232
|
if (targetIndex < 0) {
|
|
6925
7233
|
throw new Error("targetIndex cannot be less than 0");
|
|
6926
7234
|
}
|
|
@@ -6938,11 +7246,11 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6938
7246
|
let beforePosition = null;
|
|
6939
7247
|
let afterPosition = null;
|
|
6940
7248
|
if (index < targetIndex) {
|
|
6941
|
-
afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access',
|
|
7249
|
+
afterPosition = targetIndex === this.#items.length - 1 ? void 0 : _optionalChain([this, 'access', _171 => _171.#items, 'access', _172 => _172.at, 'call', _173 => _173(targetIndex + 1), 'optionalAccess', _174 => _174._parentPos]);
|
|
6942
7250
|
beforePosition = this.#items.at(targetIndex)._parentPos;
|
|
6943
7251
|
} else {
|
|
6944
7252
|
afterPosition = this.#items.at(targetIndex)._parentPos;
|
|
6945
|
-
beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access',
|
|
7253
|
+
beforePosition = targetIndex === 0 ? void 0 : _optionalChain([this, 'access', _175 => _175.#items, 'access', _176 => _176.at, 'call', _177 => _177(targetIndex - 1), 'optionalAccess', _178 => _178._parentPos]);
|
|
6946
7254
|
}
|
|
6947
7255
|
const position = makePosition(beforePosition, afterPosition);
|
|
6948
7256
|
const item = this.#items.at(index);
|
|
@@ -6977,7 +7285,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
6977
7285
|
* @param index The index of the element to delete
|
|
6978
7286
|
*/
|
|
6979
7287
|
delete(index) {
|
|
6980
|
-
_optionalChain([this, 'access',
|
|
7288
|
+
_optionalChain([this, 'access', _179 => _179._pool, 'optionalAccess', _180 => _180.assertStorageIsWritable, 'call', _181 => _181()]);
|
|
6981
7289
|
if (index < 0 || index >= this.#items.length) {
|
|
6982
7290
|
throw new Error(
|
|
6983
7291
|
`Cannot delete list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
|
|
@@ -7010,7 +7318,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
7010
7318
|
}
|
|
7011
7319
|
}
|
|
7012
7320
|
clear() {
|
|
7013
|
-
_optionalChain([this, 'access',
|
|
7321
|
+
_optionalChain([this, 'access', _182 => _182._pool, 'optionalAccess', _183 => _183.assertStorageIsWritable, 'call', _184 => _184()]);
|
|
7014
7322
|
if (this._pool) {
|
|
7015
7323
|
const ops = [];
|
|
7016
7324
|
const reverseOps = [];
|
|
@@ -7044,7 +7352,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
7044
7352
|
}
|
|
7045
7353
|
}
|
|
7046
7354
|
set(index, item) {
|
|
7047
|
-
_optionalChain([this, 'access',
|
|
7355
|
+
_optionalChain([this, 'access', _185 => _185._pool, 'optionalAccess', _186 => _186.assertStorageIsWritable, 'call', _187 => _187()]);
|
|
7048
7356
|
if (index < 0 || index >= this.#items.length) {
|
|
7049
7357
|
throw new Error(
|
|
7050
7358
|
`Cannot set list item at index "${index}". index should be between 0 and ${this.#items.length - 1}`
|
|
@@ -7203,7 +7511,7 @@ var LiveList = class _LiveList extends AbstractCrdt {
|
|
|
7203
7511
|
#shiftItemPosition(index, key) {
|
|
7204
7512
|
const shiftedPosition = makePosition(
|
|
7205
7513
|
key,
|
|
7206
|
-
this.#items.length > index + 1 ? _optionalChain([this, 'access',
|
|
7514
|
+
this.#items.length > index + 1 ? _optionalChain([this, 'access', _188 => _188.#items, 'access', _189 => _189.at, 'call', _190 => _190(index + 1), 'optionalAccess', _191 => _191._parentPos]) : void 0
|
|
7207
7515
|
);
|
|
7208
7516
|
this.#updateItemPositionAt(index, shiftedPosition);
|
|
7209
7517
|
}
|
|
@@ -7452,7 +7760,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
|
|
|
7452
7760
|
* @param value The value of the element to add. Should be serializable to JSON.
|
|
7453
7761
|
*/
|
|
7454
7762
|
set(key, value) {
|
|
7455
|
-
_optionalChain([this, 'access',
|
|
7763
|
+
_optionalChain([this, 'access', _192 => _192._pool, 'optionalAccess', _193 => _193.assertStorageIsWritable, 'call', _194 => _194()]);
|
|
7456
7764
|
const oldValue = this.#map.get(key);
|
|
7457
7765
|
if (oldValue) {
|
|
7458
7766
|
oldValue._detach();
|
|
@@ -7498,7 +7806,7 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
|
|
|
7498
7806
|
* @returns true if an element existed and has been removed, or false if the element does not exist.
|
|
7499
7807
|
*/
|
|
7500
7808
|
delete(key) {
|
|
7501
|
-
_optionalChain([this, 'access',
|
|
7809
|
+
_optionalChain([this, 'access', _195 => _195._pool, 'optionalAccess', _196 => _196.assertStorageIsWritable, 'call', _197 => _197()]);
|
|
7502
7810
|
const item = this.#map.get(key);
|
|
7503
7811
|
if (item === void 0) {
|
|
7504
7812
|
return false;
|
|
@@ -7928,6 +8236,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
7928
8236
|
const id = nn(this._id);
|
|
7929
8237
|
const parentKey = nn(child._parentKey);
|
|
7930
8238
|
const reverse = child._toOps(id, parentKey);
|
|
8239
|
+
const deletedItem = liveNodeToLson(child);
|
|
7931
8240
|
for (const [key, value] of this.#synced) {
|
|
7932
8241
|
if (value === child) {
|
|
7933
8242
|
this.#synced.delete(key);
|
|
@@ -7939,7 +8248,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
7939
8248
|
node: this,
|
|
7940
8249
|
type: "LiveObject",
|
|
7941
8250
|
updates: {
|
|
7942
|
-
[parentKey]: { type: "delete" }
|
|
8251
|
+
[parentKey]: { type: "delete", deletedItem }
|
|
7943
8252
|
}
|
|
7944
8253
|
};
|
|
7945
8254
|
return { modified: storageUpdate, reverse };
|
|
@@ -8109,20 +8418,20 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8109
8418
|
* Caveat: this method will not add changes to the undo/redo stack.
|
|
8110
8419
|
*/
|
|
8111
8420
|
setLocal(key, value) {
|
|
8112
|
-
_optionalChain([this, 'access',
|
|
8421
|
+
_optionalChain([this, 'access', _198 => _198._pool, 'optionalAccess', _199 => _199.assertStorageIsWritable, 'call', _200 => _200()]);
|
|
8113
8422
|
const deleteResult = this.#prepareDelete(key);
|
|
8114
8423
|
this.#local.set(key, value);
|
|
8115
8424
|
this.invalidate();
|
|
8116
8425
|
if (this._pool !== void 0 && this._id !== void 0) {
|
|
8117
|
-
const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess',
|
|
8118
|
-
const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess',
|
|
8119
|
-
const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess',
|
|
8426
|
+
const ops = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _201 => _201[0]]), () => ( []));
|
|
8427
|
+
const reverse = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _202 => _202[1]]), () => ( []));
|
|
8428
|
+
const storageUpdates = _nullishCoalesce(_optionalChain([deleteResult, 'optionalAccess', _203 => _203[2]]), () => ( /* @__PURE__ */ new Map()));
|
|
8120
8429
|
const existing = storageUpdates.get(this._id);
|
|
8121
8430
|
storageUpdates.set(this._id, {
|
|
8122
8431
|
node: this,
|
|
8123
8432
|
type: "LiveObject",
|
|
8124
8433
|
updates: {
|
|
8125
|
-
..._optionalChain([existing, 'optionalAccess',
|
|
8434
|
+
..._optionalChain([existing, 'optionalAccess', _204 => _204.updates]),
|
|
8126
8435
|
[key]: { type: "update" }
|
|
8127
8436
|
}
|
|
8128
8437
|
});
|
|
@@ -8142,7 +8451,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8142
8451
|
* #synced or pool/id are unavailable. Does NOT dispatch.
|
|
8143
8452
|
*/
|
|
8144
8453
|
#prepareDelete(key) {
|
|
8145
|
-
_optionalChain([this, 'access',
|
|
8454
|
+
_optionalChain([this, 'access', _205 => _205._pool, 'optionalAccess', _206 => _206.assertStorageIsWritable, 'call', _207 => _207()]);
|
|
8146
8455
|
const k = key;
|
|
8147
8456
|
if (this.#local.has(k) && !this.#synced.has(k)) {
|
|
8148
8457
|
const oldValue2 = this.#local.get(k);
|
|
@@ -8218,7 +8527,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8218
8527
|
const result = this.#prepareDelete(key);
|
|
8219
8528
|
if (result) {
|
|
8220
8529
|
const [ops, reverse, storageUpdates] = result;
|
|
8221
|
-
_optionalChain([this, 'access',
|
|
8530
|
+
_optionalChain([this, 'access', _208 => _208._pool, 'optionalAccess', _209 => _209.dispatch, 'call', _210 => _210(ops, reverse, storageUpdates)]);
|
|
8222
8531
|
}
|
|
8223
8532
|
}
|
|
8224
8533
|
/**
|
|
@@ -8226,7 +8535,7 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8226
8535
|
* @param patch The object used to overrides properties
|
|
8227
8536
|
*/
|
|
8228
8537
|
update(patch) {
|
|
8229
|
-
_optionalChain([this, 'access',
|
|
8538
|
+
_optionalChain([this, 'access', _211 => _211._pool, 'optionalAccess', _212 => _212.assertStorageIsWritable, 'call', _213 => _213()]);
|
|
8230
8539
|
if (_LiveObject.detectLargeObjects) {
|
|
8231
8540
|
const data = {};
|
|
8232
8541
|
for (const [key, value] of this.#synced) {
|
|
@@ -8426,91 +8735,1199 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8426
8735
|
}
|
|
8427
8736
|
}, _class2.__initStatic(), _class2);
|
|
8428
8737
|
|
|
8429
|
-
// src/crdts/
|
|
8430
|
-
function
|
|
8431
|
-
|
|
8432
|
-
}
|
|
8433
|
-
function creationOpToLson(op) {
|
|
8434
|
-
switch (op.type) {
|
|
8435
|
-
case OpCode.CREATE_REGISTER:
|
|
8436
|
-
return op.data;
|
|
8437
|
-
case OpCode.CREATE_OBJECT:
|
|
8438
|
-
return new LiveObject(op.data);
|
|
8439
|
-
case OpCode.CREATE_MAP:
|
|
8440
|
-
return new LiveMap();
|
|
8441
|
-
case OpCode.CREATE_LIST:
|
|
8442
|
-
return new LiveList([]);
|
|
8443
|
-
default:
|
|
8444
|
-
return assertNever(op, "Unknown creation Op");
|
|
8445
|
-
}
|
|
8446
|
-
}
|
|
8447
|
-
function isSameNodeOrChildOf(node, parent) {
|
|
8448
|
-
if (node === parent) {
|
|
8738
|
+
// src/crdts/liveTextOps.ts
|
|
8739
|
+
function attributesEqual(left, right) {
|
|
8740
|
+
if (left === right) {
|
|
8449
8741
|
return true;
|
|
8450
8742
|
}
|
|
8451
|
-
if (
|
|
8452
|
-
return
|
|
8743
|
+
if (left === void 0 || right === void 0) {
|
|
8744
|
+
return false;
|
|
8453
8745
|
}
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
return LiveObject._deserialize(node, parentToChildren, pool);
|
|
8459
|
-
} else if (isListStorageNode(node)) {
|
|
8460
|
-
return LiveList._deserialize(node, parentToChildren, pool);
|
|
8461
|
-
} else if (isMapStorageNode(node)) {
|
|
8462
|
-
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8463
|
-
} else if (isRegisterStorageNode(node)) {
|
|
8464
|
-
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
8465
|
-
} else {
|
|
8466
|
-
throw new Error("Unexpected CRDT type");
|
|
8746
|
+
const leftKeys = Object.keys(left);
|
|
8747
|
+
const rightKeys = Object.keys(right);
|
|
8748
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
8749
|
+
return false;
|
|
8467
8750
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
} else if (isListStorageNode(node)) {
|
|
8473
|
-
return LiveList._deserialize(node, parentToChildren, pool);
|
|
8474
|
-
} else if (isMapStorageNode(node)) {
|
|
8475
|
-
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8476
|
-
} else if (isRegisterStorageNode(node)) {
|
|
8477
|
-
return node[1].data;
|
|
8478
|
-
} else {
|
|
8479
|
-
throw new Error("Unexpected CRDT type");
|
|
8751
|
+
for (const key of leftKeys) {
|
|
8752
|
+
if (left[key] !== right[key]) {
|
|
8753
|
+
return false;
|
|
8754
|
+
}
|
|
8480
8755
|
}
|
|
8756
|
+
return true;
|
|
8481
8757
|
}
|
|
8482
|
-
function
|
|
8483
|
-
return
|
|
8484
|
-
}
|
|
8485
|
-
function isLiveNode(value) {
|
|
8486
|
-
return isLiveStructure(value) || isLiveRegister(value);
|
|
8758
|
+
function cloneAttributes(attributes) {
|
|
8759
|
+
return attributes === void 0 ? void 0 : freeze({ ...attributes });
|
|
8487
8760
|
}
|
|
8488
|
-
function
|
|
8489
|
-
|
|
8761
|
+
function normalizeSegments(segments) {
|
|
8762
|
+
const normalized = [];
|
|
8763
|
+
for (const segment of segments) {
|
|
8764
|
+
if (segment.text.length === 0) {
|
|
8765
|
+
continue;
|
|
8766
|
+
}
|
|
8767
|
+
const last = normalized.at(-1);
|
|
8768
|
+
const attributes = cloneAttributes(segment.attributes);
|
|
8769
|
+
if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
|
|
8770
|
+
last.text += segment.text;
|
|
8771
|
+
} else {
|
|
8772
|
+
normalized.push({ text: segment.text, attributes });
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
return normalized;
|
|
8490
8776
|
}
|
|
8491
|
-
function
|
|
8492
|
-
return
|
|
8777
|
+
function dataToSegments(data) {
|
|
8778
|
+
return normalizeSegments(
|
|
8779
|
+
data.map(([text, attributes]) => ({
|
|
8780
|
+
text,
|
|
8781
|
+
attributes
|
|
8782
|
+
}))
|
|
8783
|
+
);
|
|
8493
8784
|
}
|
|
8494
|
-
function
|
|
8495
|
-
return
|
|
8785
|
+
function segmentsToData(segments) {
|
|
8786
|
+
return segments.map(
|
|
8787
|
+
(segment) => segment.attributes === void 0 ? [segment.text] : [segment.text, { ...segment.attributes }]
|
|
8788
|
+
);
|
|
8496
8789
|
}
|
|
8497
|
-
function
|
|
8498
|
-
return
|
|
8790
|
+
function textLength(segments) {
|
|
8791
|
+
return segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
8499
8792
|
}
|
|
8500
|
-
function
|
|
8793
|
+
function splitSegmentsAt(segments, index) {
|
|
8794
|
+
const result = [];
|
|
8795
|
+
let offset = 0;
|
|
8796
|
+
for (const segment of segments) {
|
|
8797
|
+
const end = offset + segment.text.length;
|
|
8798
|
+
if (index > offset && index < end) {
|
|
8799
|
+
const before2 = segment.text.slice(0, index - offset);
|
|
8800
|
+
const after2 = segment.text.slice(index - offset);
|
|
8801
|
+
result.push({ text: before2, attributes: segment.attributes });
|
|
8802
|
+
result.push({ text: after2, attributes: segment.attributes });
|
|
8803
|
+
} else {
|
|
8804
|
+
result.push({ text: segment.text, attributes: segment.attributes });
|
|
8805
|
+
}
|
|
8806
|
+
offset = end;
|
|
8807
|
+
}
|
|
8808
|
+
return result;
|
|
8809
|
+
}
|
|
8810
|
+
function clipRange(index, length, contentLength) {
|
|
8811
|
+
const clippedIndex = Math.max(0, Math.min(index, contentLength));
|
|
8812
|
+
const clippedEnd = Math.max(
|
|
8813
|
+
clippedIndex,
|
|
8814
|
+
Math.min(index + length, contentLength)
|
|
8815
|
+
);
|
|
8816
|
+
return { index: clippedIndex, length: clippedEnd - clippedIndex };
|
|
8817
|
+
}
|
|
8818
|
+
function applyInsert(segments, index, text, attributes) {
|
|
8819
|
+
if (text.length === 0) {
|
|
8820
|
+
return normalizeSegments(segments);
|
|
8821
|
+
}
|
|
8822
|
+
const split = splitSegmentsAt(segments, index);
|
|
8823
|
+
const result = [];
|
|
8824
|
+
let offset = 0;
|
|
8825
|
+
let inserted = false;
|
|
8826
|
+
for (const segment of split) {
|
|
8827
|
+
if (!inserted && offset === index) {
|
|
8828
|
+
result.push({ text, attributes });
|
|
8829
|
+
inserted = true;
|
|
8830
|
+
}
|
|
8831
|
+
result.push(segment);
|
|
8832
|
+
offset += segment.text.length;
|
|
8833
|
+
}
|
|
8834
|
+
if (!inserted) {
|
|
8835
|
+
result.push({ text, attributes });
|
|
8836
|
+
}
|
|
8837
|
+
return normalizeSegments(result);
|
|
8838
|
+
}
|
|
8839
|
+
function extractDeletedSegments(segments, index, length) {
|
|
8840
|
+
const split = splitSegmentsAt(
|
|
8841
|
+
splitSegmentsAt(segments, index),
|
|
8842
|
+
index + length
|
|
8843
|
+
);
|
|
8844
|
+
const deleted = [];
|
|
8845
|
+
let offset = 0;
|
|
8846
|
+
for (const segment of split) {
|
|
8847
|
+
const end = offset + segment.text.length;
|
|
8848
|
+
if (offset >= index && end <= index + length) {
|
|
8849
|
+
deleted.push({
|
|
8850
|
+
text: segment.text,
|
|
8851
|
+
attributes: segment.attributes
|
|
8852
|
+
});
|
|
8853
|
+
}
|
|
8854
|
+
offset = end;
|
|
8855
|
+
}
|
|
8856
|
+
return normalizeSegments(deleted);
|
|
8857
|
+
}
|
|
8858
|
+
function applyDelete(segments, index, length) {
|
|
8859
|
+
const deletedSegments = extractDeletedSegments(segments, index, length);
|
|
8860
|
+
const split = splitSegmentsAt(
|
|
8861
|
+
splitSegmentsAt(segments, index),
|
|
8862
|
+
index + length
|
|
8863
|
+
);
|
|
8864
|
+
const result = [];
|
|
8865
|
+
let offset = 0;
|
|
8866
|
+
let deletedText = "";
|
|
8867
|
+
for (const segment of split) {
|
|
8868
|
+
const end = offset + segment.text.length;
|
|
8869
|
+
if (offset >= index && end <= index + length) {
|
|
8870
|
+
deletedText += segment.text;
|
|
8871
|
+
} else {
|
|
8872
|
+
result.push(segment);
|
|
8873
|
+
}
|
|
8874
|
+
offset = end;
|
|
8875
|
+
}
|
|
8876
|
+
return {
|
|
8877
|
+
segments: normalizeSegments(result),
|
|
8878
|
+
deletedText,
|
|
8879
|
+
deletedSegments
|
|
8880
|
+
};
|
|
8881
|
+
}
|
|
8882
|
+
function applyFormat(segments, index, length, attributes) {
|
|
8883
|
+
const split = splitSegmentsAt(
|
|
8884
|
+
splitSegmentsAt(segments, index),
|
|
8885
|
+
index + length
|
|
8886
|
+
);
|
|
8887
|
+
const result = [];
|
|
8888
|
+
let offset = 0;
|
|
8889
|
+
for (const segment of split) {
|
|
8890
|
+
const end = offset + segment.text.length;
|
|
8891
|
+
if (offset >= index && end <= index + length) {
|
|
8892
|
+
const nextAttributes = {
|
|
8893
|
+
..._nullishCoalesce(segment.attributes, () => ( {}))
|
|
8894
|
+
};
|
|
8895
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
8896
|
+
if (value === null) {
|
|
8897
|
+
delete nextAttributes[key];
|
|
8898
|
+
} else {
|
|
8899
|
+
nextAttributes[key] = value;
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
result.push({
|
|
8903
|
+
text: segment.text,
|
|
8904
|
+
attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
|
|
8905
|
+
});
|
|
8906
|
+
} else {
|
|
8907
|
+
result.push(segment);
|
|
8908
|
+
}
|
|
8909
|
+
offset = end;
|
|
8910
|
+
}
|
|
8911
|
+
return normalizeSegments(result);
|
|
8912
|
+
}
|
|
8913
|
+
function formatReverseOperations(segments, index, length, patch) {
|
|
8914
|
+
const split = splitSegmentsAt(
|
|
8915
|
+
splitSegmentsAt(segments, index),
|
|
8916
|
+
index + length
|
|
8917
|
+
);
|
|
8918
|
+
const result = [];
|
|
8919
|
+
let offset = 0;
|
|
8920
|
+
for (const segment of split) {
|
|
8921
|
+
const end = offset + segment.text.length;
|
|
8922
|
+
if (offset >= index && end <= index + length) {
|
|
8923
|
+
const attributes = {};
|
|
8924
|
+
for (const key of Object.keys(patch)) {
|
|
8925
|
+
attributes[key] = _nullishCoalesce(_optionalChain([segment, 'access', _214 => _214.attributes, 'optionalAccess', _215 => _215[key]]), () => ( null));
|
|
8926
|
+
}
|
|
8927
|
+
result.push({
|
|
8928
|
+
type: "format",
|
|
8929
|
+
index: offset,
|
|
8930
|
+
length: segment.text.length,
|
|
8931
|
+
attributes
|
|
8932
|
+
});
|
|
8933
|
+
}
|
|
8934
|
+
offset = end;
|
|
8935
|
+
}
|
|
8936
|
+
return result;
|
|
8937
|
+
}
|
|
8938
|
+
function mapIndexThroughOperation(index, op) {
|
|
8939
|
+
if (op.type === "insert") {
|
|
8940
|
+
return op.index <= index ? index + op.text.length : index;
|
|
8941
|
+
} else if (op.type === "delete") {
|
|
8942
|
+
if (op.index >= index) {
|
|
8943
|
+
return index;
|
|
8944
|
+
}
|
|
8945
|
+
return Math.max(op.index, index - op.length);
|
|
8946
|
+
} else {
|
|
8947
|
+
return index;
|
|
8948
|
+
}
|
|
8949
|
+
}
|
|
8950
|
+
function mapTextIndexThroughOperations(index, ops) {
|
|
8951
|
+
let mapped = index;
|
|
8952
|
+
for (const op of ops) {
|
|
8953
|
+
mapped = mapIndexThroughOperation(mapped, op);
|
|
8954
|
+
}
|
|
8955
|
+
return mapped;
|
|
8956
|
+
}
|
|
8957
|
+
function inverseMapIndexThroughOperation(index, op) {
|
|
8958
|
+
if (op.type === "insert") {
|
|
8959
|
+
if (index <= op.index) {
|
|
8960
|
+
return index;
|
|
8961
|
+
}
|
|
8962
|
+
return Math.max(op.index, index - op.text.length);
|
|
8963
|
+
} else if (op.type === "delete") {
|
|
8964
|
+
return op.index <= index ? index + op.length : index;
|
|
8965
|
+
} else {
|
|
8966
|
+
return index;
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
function inverseMapTextIndexThroughOperations(index, ops) {
|
|
8970
|
+
let mapped = index;
|
|
8971
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
8972
|
+
mapped = inverseMapIndexThroughOperation(mapped, ops[i]);
|
|
8973
|
+
}
|
|
8974
|
+
return mapped;
|
|
8975
|
+
}
|
|
8976
|
+
function oppositeOrder(order) {
|
|
8977
|
+
return order === "before" ? "after" : "before";
|
|
8978
|
+
}
|
|
8979
|
+
function mapIndexOverDelete(index, deleteIndex, deleteLength) {
|
|
8980
|
+
if (deleteIndex >= index) {
|
|
8981
|
+
return index;
|
|
8982
|
+
}
|
|
8983
|
+
return Math.max(deleteIndex, index - deleteLength);
|
|
8984
|
+
}
|
|
8985
|
+
function transformInsert(op, over, order) {
|
|
8986
|
+
if (over.type === "insert") {
|
|
8987
|
+
const shifts = over.index < op.index || over.index === op.index && order === "after";
|
|
8988
|
+
return [shifts ? { ...op, index: op.index + over.text.length } : { ...op }];
|
|
8989
|
+
} else if (over.type === "delete") {
|
|
8990
|
+
return [
|
|
8991
|
+
{ ...op, index: mapIndexOverDelete(op.index, over.index, over.length) }
|
|
8992
|
+
];
|
|
8993
|
+
} else {
|
|
8994
|
+
return [{ ...op }];
|
|
8995
|
+
}
|
|
8996
|
+
}
|
|
8997
|
+
function transformDelete(op, over) {
|
|
8998
|
+
const start = op.index;
|
|
8999
|
+
const end = op.index + op.length;
|
|
9000
|
+
if (over.type === "insert") {
|
|
9001
|
+
const at = over.index;
|
|
9002
|
+
const len = over.text.length;
|
|
9003
|
+
if (at <= start) {
|
|
9004
|
+
return [{ ...op, index: start + len }];
|
|
9005
|
+
}
|
|
9006
|
+
if (at >= end) {
|
|
9007
|
+
return [{ ...op }];
|
|
9008
|
+
}
|
|
9009
|
+
return [
|
|
9010
|
+
{ type: "delete", index: start, length: at - start },
|
|
9011
|
+
{ type: "delete", index: start + len, length: end - at }
|
|
9012
|
+
];
|
|
9013
|
+
} else if (over.type === "delete") {
|
|
9014
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9015
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9016
|
+
return newEnd - newStart > 0 ? [{ type: "delete", index: newStart, length: newEnd - newStart }] : [];
|
|
9017
|
+
} else {
|
|
9018
|
+
return [{ ...op }];
|
|
9019
|
+
}
|
|
9020
|
+
}
|
|
9021
|
+
function transformFormat(op, over, order) {
|
|
9022
|
+
const start = op.index;
|
|
9023
|
+
const end = op.index + op.length;
|
|
9024
|
+
if (over.type === "insert") {
|
|
9025
|
+
const at = over.index;
|
|
9026
|
+
const len = over.text.length;
|
|
9027
|
+
if (at <= start) {
|
|
9028
|
+
return [{ ...op, index: start + len }];
|
|
9029
|
+
}
|
|
9030
|
+
if (at >= end) {
|
|
9031
|
+
return [{ ...op }];
|
|
9032
|
+
}
|
|
9033
|
+
return [
|
|
9034
|
+
{
|
|
9035
|
+
type: "format",
|
|
9036
|
+
index: start,
|
|
9037
|
+
length: at - start,
|
|
9038
|
+
attributes: op.attributes
|
|
9039
|
+
},
|
|
9040
|
+
{
|
|
9041
|
+
type: "format",
|
|
9042
|
+
index: at + len,
|
|
9043
|
+
length: end - at,
|
|
9044
|
+
attributes: op.attributes
|
|
9045
|
+
}
|
|
9046
|
+
];
|
|
9047
|
+
} else if (over.type === "delete") {
|
|
9048
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9049
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9050
|
+
return newEnd - newStart > 0 ? [
|
|
9051
|
+
{
|
|
9052
|
+
type: "format",
|
|
9053
|
+
index: newStart,
|
|
9054
|
+
length: newEnd - newStart,
|
|
9055
|
+
attributes: op.attributes
|
|
9056
|
+
}
|
|
9057
|
+
] : [];
|
|
9058
|
+
} else {
|
|
9059
|
+
if (order === "after") {
|
|
9060
|
+
return [{ ...op }];
|
|
9061
|
+
}
|
|
9062
|
+
const overlapStart = Math.max(start, over.index);
|
|
9063
|
+
const overlapEnd = Math.min(end, over.index + over.length);
|
|
9064
|
+
if (overlapStart >= overlapEnd) {
|
|
9065
|
+
return [{ ...op }];
|
|
9066
|
+
}
|
|
9067
|
+
const hasConflict = Object.keys(op.attributes).some(
|
|
9068
|
+
(key) => key in over.attributes
|
|
9069
|
+
);
|
|
9070
|
+
if (!hasConflict) {
|
|
9071
|
+
return [{ ...op }];
|
|
9072
|
+
}
|
|
9073
|
+
const reduced = {};
|
|
9074
|
+
for (const [key, value] of Object.entries(op.attributes)) {
|
|
9075
|
+
if (!(key in over.attributes)) {
|
|
9076
|
+
reduced[key] = value;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
const pieces = [];
|
|
9080
|
+
if (start < overlapStart) {
|
|
9081
|
+
pieces.push({
|
|
9082
|
+
type: "format",
|
|
9083
|
+
index: start,
|
|
9084
|
+
length: overlapStart - start,
|
|
9085
|
+
attributes: op.attributes
|
|
9086
|
+
});
|
|
9087
|
+
}
|
|
9088
|
+
if (Object.keys(reduced).length > 0) {
|
|
9089
|
+
pieces.push({
|
|
9090
|
+
type: "format",
|
|
9091
|
+
index: overlapStart,
|
|
9092
|
+
length: overlapEnd - overlapStart,
|
|
9093
|
+
attributes: reduced
|
|
9094
|
+
});
|
|
9095
|
+
}
|
|
9096
|
+
if (overlapEnd < end) {
|
|
9097
|
+
pieces.push({
|
|
9098
|
+
type: "format",
|
|
9099
|
+
index: overlapEnd,
|
|
9100
|
+
length: end - overlapEnd,
|
|
9101
|
+
attributes: op.attributes
|
|
9102
|
+
});
|
|
9103
|
+
}
|
|
9104
|
+
return pieces;
|
|
9105
|
+
}
|
|
9106
|
+
}
|
|
9107
|
+
function transformSingle(op, over, order) {
|
|
9108
|
+
switch (op.type) {
|
|
9109
|
+
case "insert":
|
|
9110
|
+
return transformInsert(op, over, order);
|
|
9111
|
+
case "delete":
|
|
9112
|
+
return transformDelete(op, over);
|
|
9113
|
+
case "format":
|
|
9114
|
+
return transformFormat(op, over, order);
|
|
9115
|
+
}
|
|
9116
|
+
}
|
|
9117
|
+
function transformTextOperationsX(a, b, order) {
|
|
9118
|
+
if (a.length === 0 || b.length === 0) {
|
|
9119
|
+
return [[...a], [...b]];
|
|
9120
|
+
}
|
|
9121
|
+
if (a.length === 1 && b.length === 1) {
|
|
9122
|
+
return [
|
|
9123
|
+
transformSingle(a[0], b[0], order),
|
|
9124
|
+
transformSingle(b[0], a[0], oppositeOrder(order))
|
|
9125
|
+
];
|
|
9126
|
+
}
|
|
9127
|
+
if (a.length > 1) {
|
|
9128
|
+
const [headA1, b1] = transformTextOperationsX([a[0]], b, order);
|
|
9129
|
+
const [restA1, b2] = transformTextOperationsX(a.slice(1), b1, order);
|
|
9130
|
+
return [[...headA1, ...restA1], b2];
|
|
9131
|
+
}
|
|
9132
|
+
const [a1, headB1] = transformTextOperationsX(a, [b[0]], order);
|
|
9133
|
+
const [a2, restB1] = transformTextOperationsX(a1, b.slice(1), order);
|
|
9134
|
+
return [a2, [...headB1, ...restB1]];
|
|
9135
|
+
}
|
|
9136
|
+
function transformTextOperations(ops, over, order) {
|
|
9137
|
+
return transformTextOperationsX(ops, over, order)[0];
|
|
9138
|
+
}
|
|
9139
|
+
function textOperationsEqual(a, b) {
|
|
9140
|
+
return a === b || stableStringify(a) === stableStringify(b);
|
|
9141
|
+
}
|
|
9142
|
+
function applyTextOperationsToSegments(segments, ops) {
|
|
9143
|
+
let next = [...segments];
|
|
9144
|
+
for (const op of ops) {
|
|
9145
|
+
if (op.type === "insert") {
|
|
9146
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9147
|
+
next = applyInsert(next, index, op.text, op.attributes);
|
|
9148
|
+
} else if (op.type === "delete") {
|
|
9149
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9150
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9151
|
+
next = applyDelete(next, clipped.index, clipped.length).segments;
|
|
9152
|
+
} else {
|
|
9153
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9154
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9155
|
+
next = applyFormat(next, clipped.index, clipped.length, op.attributes);
|
|
9156
|
+
}
|
|
9157
|
+
}
|
|
9158
|
+
return next;
|
|
9159
|
+
}
|
|
9160
|
+
function applyLiveTextOperations(data, ops) {
|
|
9161
|
+
return segmentsToData(
|
|
9162
|
+
applyTextOperationsToSegments(dataToSegments(data), ops)
|
|
9163
|
+
);
|
|
9164
|
+
}
|
|
9165
|
+
function invertTextOperations(segments, ops) {
|
|
9166
|
+
let shadow = [...segments];
|
|
9167
|
+
const reverse = [];
|
|
9168
|
+
for (const op of ops) {
|
|
9169
|
+
if (op.type === "insert") {
|
|
9170
|
+
shadow = applyInsert(shadow, op.index, op.text, op.attributes);
|
|
9171
|
+
reverse.unshift({
|
|
9172
|
+
type: "delete",
|
|
9173
|
+
index: op.index,
|
|
9174
|
+
length: op.text.length
|
|
9175
|
+
});
|
|
9176
|
+
} else if (op.type === "delete") {
|
|
9177
|
+
const deletedSegments = extractDeletedSegments(
|
|
9178
|
+
shadow,
|
|
9179
|
+
op.index,
|
|
9180
|
+
op.length
|
|
9181
|
+
);
|
|
9182
|
+
shadow = applyDelete(shadow, op.index, op.length).segments;
|
|
9183
|
+
const inserts = [];
|
|
9184
|
+
let insertIndex = op.index;
|
|
9185
|
+
for (const segment of deletedSegments) {
|
|
9186
|
+
inserts.push({
|
|
9187
|
+
type: "insert",
|
|
9188
|
+
index: insertIndex,
|
|
9189
|
+
text: segment.text,
|
|
9190
|
+
attributes: segment.attributes
|
|
9191
|
+
});
|
|
9192
|
+
insertIndex += segment.text.length;
|
|
9193
|
+
}
|
|
9194
|
+
for (let index = inserts.length - 1; index >= 0; index--) {
|
|
9195
|
+
reverse.unshift(inserts[index]);
|
|
9196
|
+
}
|
|
9197
|
+
} else {
|
|
9198
|
+
const inverse = formatReverseOperations(
|
|
9199
|
+
shadow,
|
|
9200
|
+
op.index,
|
|
9201
|
+
op.length,
|
|
9202
|
+
op.attributes
|
|
9203
|
+
);
|
|
9204
|
+
shadow = applyFormat(shadow, op.index, op.length, op.attributes);
|
|
9205
|
+
reverse.unshift(...inverse.reverse());
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
return reverse;
|
|
9209
|
+
}
|
|
9210
|
+
|
|
9211
|
+
// src/crdts/LiveText.ts
|
|
9212
|
+
var ACCEPTED_OPS_HISTORY_LIMIT = 1e3;
|
|
9213
|
+
var LiveText = class _LiveText extends AbstractCrdt {
|
|
9214
|
+
/** The local document: #confirmed ⊕ #inFlightOps ⊕ #queuedOps. */
|
|
9215
|
+
#segments;
|
|
9216
|
+
/** The server-confirmed document (only authoritative ops applied). */
|
|
9217
|
+
#confirmed;
|
|
9218
|
+
#version;
|
|
9219
|
+
/** The op currently awaiting server acknowledgement (at most one). */
|
|
9220
|
+
#inFlightOpId;
|
|
9221
|
+
/** Its ops, continuously re-expressed against current server state. */
|
|
9222
|
+
#inFlightOps = [];
|
|
9223
|
+
/** Local edits made while an op is in flight; sent after the ack. */
|
|
9224
|
+
#queuedOps = [];
|
|
9225
|
+
#acceptedOps = [];
|
|
9226
|
+
/**
|
|
9227
|
+
* Creates a new LiveText document.
|
|
9228
|
+
*
|
|
9229
|
+
* @param textOrData Initial plain text, or an array of `[text]` /
|
|
9230
|
+
* `[text, attributes]` segments. Defaults to an empty document.
|
|
9231
|
+
*
|
|
9232
|
+
* @example
|
|
9233
|
+
* new LiveText();
|
|
9234
|
+
* new LiveText("Hello world");
|
|
9235
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]);
|
|
9236
|
+
*/
|
|
9237
|
+
constructor(textOrData = "", version = 0) {
|
|
9238
|
+
super();
|
|
9239
|
+
this.#segments = typeof textOrData === "string" ? textOrData.length === 0 ? [] : [{ text: textOrData }] : dataToSegments(textOrData);
|
|
9240
|
+
this.#confirmed = [...this.#segments];
|
|
9241
|
+
this.#version = version;
|
|
9242
|
+
Object.defineProperty(this, kInternal, {
|
|
9243
|
+
value: {
|
|
9244
|
+
encodeIndex: (localIndex) => this.#encodeIndex(localIndex),
|
|
9245
|
+
decodeIndex: (index, fromVersion) => this.#decodeIndex(index, fromVersion)
|
|
9246
|
+
},
|
|
9247
|
+
enumerable: false
|
|
9248
|
+
});
|
|
9249
|
+
}
|
|
9250
|
+
get version() {
|
|
9251
|
+
return this.#version;
|
|
9252
|
+
}
|
|
9253
|
+
get length() {
|
|
9254
|
+
return textLength(this.#segments);
|
|
9255
|
+
}
|
|
9256
|
+
/** @internal */
|
|
9257
|
+
static _deserialize([id, item], _parentToChildren, pool) {
|
|
9258
|
+
const text = new _LiveText(item.data, item.version);
|
|
9259
|
+
text._attach(id, pool);
|
|
9260
|
+
return text;
|
|
9261
|
+
}
|
|
9262
|
+
/** @internal */
|
|
9263
|
+
_toOps(parentId, parentKey) {
|
|
9264
|
+
if (this._id === void 0) {
|
|
9265
|
+
throw new Error("Cannot serialize LiveText if it is not attached");
|
|
9266
|
+
}
|
|
9267
|
+
return [
|
|
9268
|
+
{
|
|
9269
|
+
type: OpCode.CREATE_TEXT,
|
|
9270
|
+
id: this._id,
|
|
9271
|
+
parentId,
|
|
9272
|
+
parentKey,
|
|
9273
|
+
data: this.toJSON(),
|
|
9274
|
+
version: this.#version
|
|
9275
|
+
}
|
|
9276
|
+
];
|
|
9277
|
+
}
|
|
9278
|
+
/** @internal */
|
|
9279
|
+
_serialize() {
|
|
9280
|
+
if (this.parent.type !== "HasParent") {
|
|
9281
|
+
throw new Error("Cannot serialize LiveText if parent is missing");
|
|
9282
|
+
}
|
|
9283
|
+
return {
|
|
9284
|
+
type: CrdtType.TEXT,
|
|
9285
|
+
parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
|
|
9286
|
+
parentKey: this.parent.key,
|
|
9287
|
+
data: this.toJSON(),
|
|
9288
|
+
version: this.#version
|
|
9289
|
+
};
|
|
9290
|
+
}
|
|
9291
|
+
/** @internal */
|
|
9292
|
+
_attachChild(_op) {
|
|
9293
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9294
|
+
}
|
|
9295
|
+
/** @internal */
|
|
9296
|
+
_detachChild(_crdt) {
|
|
9297
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9298
|
+
}
|
|
9299
|
+
/** @internal */
|
|
9300
|
+
_apply(op, isLocal) {
|
|
9301
|
+
if (op.type !== OpCode.UPDATE_TEXT) {
|
|
9302
|
+
return super._apply(op, isLocal);
|
|
9303
|
+
}
|
|
9304
|
+
if (isLocal) {
|
|
9305
|
+
return this.#applyLocal(op);
|
|
9306
|
+
}
|
|
9307
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9308
|
+
return this.#applyAck(op);
|
|
9309
|
+
}
|
|
9310
|
+
if (op.opId !== void 0 && this.#acceptedOps.some((entry) => entry.opId === op.opId)) {
|
|
9311
|
+
this.#version = Math.max(this.#version, _nullishCoalesce(op.version, () => ( op.baseVersion + 1)));
|
|
9312
|
+
return { modified: false };
|
|
9313
|
+
}
|
|
9314
|
+
return this.#applyRemote(op);
|
|
9315
|
+
}
|
|
9316
|
+
/**
|
|
9317
|
+
* Inserts text at the given index.
|
|
9318
|
+
*
|
|
9319
|
+
* @param index Character index at which to insert. Values outside the
|
|
9320
|
+
* document range are clipped.
|
|
9321
|
+
* @param text Text to insert.
|
|
9322
|
+
* @param attributes Optional inline attributes for the inserted text.
|
|
9323
|
+
*
|
|
9324
|
+
* @example
|
|
9325
|
+
* const text = new LiveText("Hello");
|
|
9326
|
+
* text.insert(5, " world");
|
|
9327
|
+
* text.insert(0, "Say: ", { italic: true });
|
|
9328
|
+
*/
|
|
9329
|
+
insert(index, text, attributes) {
|
|
9330
|
+
const clippedIndex = Math.max(0, Math.min(index, this.length));
|
|
9331
|
+
this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
|
|
9332
|
+
}
|
|
9333
|
+
/**
|
|
9334
|
+
* Deletes `length` characters starting at `index`.
|
|
9335
|
+
*
|
|
9336
|
+
* @example
|
|
9337
|
+
* const text = new LiveText("Hello world");
|
|
9338
|
+
* text.delete(5, 6); // "Hello"
|
|
9339
|
+
*/
|
|
9340
|
+
delete(index, length) {
|
|
9341
|
+
const clipped = clipRange(index, length, this.length);
|
|
9342
|
+
if (clipped.length === 0) {
|
|
9343
|
+
return;
|
|
9344
|
+
}
|
|
9345
|
+
this.#dispatch([
|
|
9346
|
+
{ type: "delete", index: clipped.index, length: clipped.length }
|
|
9347
|
+
]);
|
|
9348
|
+
}
|
|
9349
|
+
/**
|
|
9350
|
+
* Replaces a range of text with new text.
|
|
9351
|
+
*
|
|
9352
|
+
* @example
|
|
9353
|
+
* const text = new LiveText("Hello world");
|
|
9354
|
+
* text.replace(0, 5, "Hi"); // "Hi world"
|
|
9355
|
+
*/
|
|
9356
|
+
replace(index, length, text, attributes) {
|
|
9357
|
+
const clipped = clipRange(index, length, this.length);
|
|
9358
|
+
const ops = [];
|
|
9359
|
+
if (clipped.length > 0) {
|
|
9360
|
+
ops.push({
|
|
9361
|
+
type: "delete",
|
|
9362
|
+
index: clipped.index,
|
|
9363
|
+
length: clipped.length
|
|
9364
|
+
});
|
|
9365
|
+
}
|
|
9366
|
+
if (text.length > 0) {
|
|
9367
|
+
ops.push({ type: "insert", index: clipped.index, text, attributes });
|
|
9368
|
+
}
|
|
9369
|
+
this.#dispatch(ops);
|
|
9370
|
+
}
|
|
9371
|
+
/**
|
|
9372
|
+
* Encode a local-document index (an offset into this LiveText's current
|
|
9373
|
+
* #segments, which CodeMirror or any consumer mirrors as its document)
|
|
9374
|
+
* into server-confirmed coordinates suitable for broadcasting to peers via
|
|
9375
|
+
* presence or any other side channel.
|
|
9376
|
+
*
|
|
9377
|
+
* The returned index is in this LiveText's current #confirmed coordinates
|
|
9378
|
+
* — that is, with this client's local pending ops inverse-mapped out.
|
|
9379
|
+
* Pair it with the current {@link LiveText.version} when sending so the
|
|
9380
|
+
* receiver can call {@link PrivateLiveTextApi.decodeIndex} to land the
|
|
9381
|
+
* position in their own local document coordinates regardless of their
|
|
9382
|
+
* private pending ops.
|
|
9383
|
+
*
|
|
9384
|
+
* Index ambiguity at boundaries is resolved by an inverse-of-forward
|
|
9385
|
+
* convention: a position at or before a local insertion is reported as
|
|
9386
|
+
* the position right before the insertion in #confirmed; a position past
|
|
9387
|
+
* the insertion shifts left by the insertion's length. Positions inside
|
|
9388
|
+
* an own-pending insertion collapse to the insertion point.
|
|
9389
|
+
*/
|
|
9390
|
+
#encodeIndex(localIndex) {
|
|
9391
|
+
let mapped = Math.max(0, Math.min(localIndex, this.length));
|
|
9392
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9393
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9394
|
+
return mapped;
|
|
9395
|
+
}
|
|
9396
|
+
/**
|
|
9397
|
+
* Decode an `(index, fromVersion)` pair produced by
|
|
9398
|
+
* {@link PrivateLiveTextApi.encodeIndex} — typically on a peer — into an
|
|
9399
|
+
* offset in this LiveText's current local document (an index suitable for
|
|
9400
|
+
* placing a CodeMirror marker, an annotation anchor, or anything else that
|
|
9401
|
+
* lives over #segments).
|
|
9402
|
+
*
|
|
9403
|
+
* Composes the accepted ops applied since `fromVersion` (drawn from
|
|
9404
|
+
* #acceptedOps in locally-applied form) with this client's own local
|
|
9405
|
+
* pending ops, in that order. The result is in current #segments
|
|
9406
|
+
* coordinates.
|
|
9407
|
+
*
|
|
9408
|
+
* Returns `null` when the position cannot be decoded against the current
|
|
9409
|
+
* state:
|
|
9410
|
+
* - `fromVersion` is greater than this LiveText's current version: the
|
|
9411
|
+
* peer is ahead of us. The caller should park the message and retry
|
|
9412
|
+
* after more accepted ops arrive.
|
|
9413
|
+
* - `fromVersion` falls outside the retained accepted-ops history. This
|
|
9414
|
+
* only happens after very long-lived disconnections; the caller can
|
|
9415
|
+
* fall back to using the raw index and letting subsequent local
|
|
9416
|
+
* transactions map it (with bounded drift).
|
|
9417
|
+
*/
|
|
9418
|
+
#decodeIndex(index, fromVersion) {
|
|
9419
|
+
if (fromVersion > this.#version) {
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
if (fromVersion < this.#version) {
|
|
9423
|
+
const oldest = _optionalChain([this, 'access', _216 => _216.#acceptedOps, 'access', _217 => _217[0], 'optionalAccess', _218 => _218.version]);
|
|
9424
|
+
if (oldest === void 0 || oldest > fromVersion + 1) {
|
|
9425
|
+
return null;
|
|
9426
|
+
}
|
|
9427
|
+
}
|
|
9428
|
+
let mapped = index;
|
|
9429
|
+
for (const entry of this.#acceptedOps) {
|
|
9430
|
+
if (entry.version <= fromVersion) continue;
|
|
9431
|
+
if (entry.version > this.#version) break;
|
|
9432
|
+
if (entry.ops.length === 0) continue;
|
|
9433
|
+
mapped = mapTextIndexThroughOperations(mapped, entry.ops);
|
|
9434
|
+
}
|
|
9435
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9436
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9437
|
+
return Math.max(0, Math.min(mapped, this.length));
|
|
9438
|
+
}
|
|
9439
|
+
/**
|
|
9440
|
+
* Applies or removes inline attributes on a range of text.
|
|
9441
|
+
*
|
|
9442
|
+
* Set an attribute to `null` to remove it from the range.
|
|
9443
|
+
*
|
|
9444
|
+
* @example
|
|
9445
|
+
* const text = new LiveText("Hello world");
|
|
9446
|
+
* text.format(0, 5, { bold: true });
|
|
9447
|
+
* text.format(0, 5, { bold: null });
|
|
9448
|
+
*/
|
|
9449
|
+
format(index, length, attributes) {
|
|
9450
|
+
const clipped = clipRange(index, length, this.length);
|
|
9451
|
+
if (clipped.length === 0) {
|
|
9452
|
+
return;
|
|
9453
|
+
}
|
|
9454
|
+
this.#dispatch([
|
|
9455
|
+
{
|
|
9456
|
+
type: "format",
|
|
9457
|
+
index: clipped.index,
|
|
9458
|
+
length: clipped.length,
|
|
9459
|
+
attributes
|
|
9460
|
+
}
|
|
9461
|
+
]);
|
|
9462
|
+
}
|
|
9463
|
+
/** Local edits made through the public API. */
|
|
9464
|
+
#dispatch(ops) {
|
|
9465
|
+
if (ops.length === 0) {
|
|
9466
|
+
return;
|
|
9467
|
+
}
|
|
9468
|
+
_optionalChain([this, 'access', _219 => _219._pool, 'optionalAccess', _220 => _220.assertStorageIsWritable, 'call', _221 => _221()]);
|
|
9469
|
+
const attached = this._pool !== void 0 && this._id !== void 0;
|
|
9470
|
+
const reverse = attached ? this.#invertOperations(ops) : [];
|
|
9471
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9472
|
+
if (!attached) {
|
|
9473
|
+
return;
|
|
9474
|
+
}
|
|
9475
|
+
const pool = nn(this._pool);
|
|
9476
|
+
const id = nn(this._id);
|
|
9477
|
+
const updates = /* @__PURE__ */ new Map([
|
|
9478
|
+
[
|
|
9479
|
+
id,
|
|
9480
|
+
{
|
|
9481
|
+
type: "LiveText",
|
|
9482
|
+
node: this,
|
|
9483
|
+
version: this.#version,
|
|
9484
|
+
updates: changes
|
|
9485
|
+
}
|
|
9486
|
+
]
|
|
9487
|
+
]);
|
|
9488
|
+
if (this.#inFlightOpId === void 0) {
|
|
9489
|
+
const opId = pool.generateOpId();
|
|
9490
|
+
this.#inFlightOpId = opId;
|
|
9491
|
+
this.#inFlightOps = [...ops];
|
|
9492
|
+
pool.dispatch(
|
|
9493
|
+
[
|
|
9494
|
+
{
|
|
9495
|
+
type: OpCode.UPDATE_TEXT,
|
|
9496
|
+
id,
|
|
9497
|
+
opId,
|
|
9498
|
+
baseVersion: this.#version,
|
|
9499
|
+
ops: [...ops]
|
|
9500
|
+
}
|
|
9501
|
+
],
|
|
9502
|
+
reverse,
|
|
9503
|
+
updates
|
|
9504
|
+
);
|
|
9505
|
+
} else {
|
|
9506
|
+
this.#queuedOps.push(...ops);
|
|
9507
|
+
pool.dispatch([], reverse, updates, { clearRedoStack: true });
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
/**
|
|
9511
|
+
* A local replay of an existing wire op: an undo/redo frame, or an
|
|
9512
|
+
* unacknowledged op re-sent after a reconnect.
|
|
9513
|
+
*/
|
|
9514
|
+
#applyLocal(op) {
|
|
9515
|
+
const mutableOp = op;
|
|
9516
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9517
|
+
this.#inFlightOps = [...this.#inFlightOps, ...this.#queuedOps];
|
|
9518
|
+
this.#queuedOps = [];
|
|
9519
|
+
mutableOp.baseVersion = this.#version;
|
|
9520
|
+
mutableOp.ops = [...this.#inFlightOps];
|
|
9521
|
+
return { modified: false };
|
|
9522
|
+
}
|
|
9523
|
+
let ops = op.ops;
|
|
9524
|
+
for (const entry of this.#acceptedOps) {
|
|
9525
|
+
if (entry.version > op.baseVersion && entry.ops.length > 0) {
|
|
9526
|
+
ops = transformTextOperations(ops, entry.ops, "after");
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
const reverse = this.#invertOperations(ops);
|
|
9530
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9531
|
+
if (this.#inFlightOpId === void 0 && ops.length > 0) {
|
|
9532
|
+
this.#inFlightOpId = nn(op.opId, "Local ops must have an opId");
|
|
9533
|
+
this.#inFlightOps = [...ops];
|
|
9534
|
+
mutableOp.baseVersion = this.#version;
|
|
9535
|
+
mutableOp.ops = [...ops];
|
|
9536
|
+
} else {
|
|
9537
|
+
this.#queuedOps.push(...ops);
|
|
9538
|
+
mutableOp.baseVersion = this.#version;
|
|
9539
|
+
mutableOp.ops = [];
|
|
9540
|
+
}
|
|
9541
|
+
if (changes.length === 0) {
|
|
9542
|
+
return { modified: false };
|
|
9543
|
+
}
|
|
9544
|
+
return {
|
|
9545
|
+
reverse,
|
|
9546
|
+
modified: {
|
|
9547
|
+
type: "LiveText",
|
|
9548
|
+
node: this,
|
|
9549
|
+
version: this.#version,
|
|
9550
|
+
updates: changes
|
|
9551
|
+
}
|
|
9552
|
+
};
|
|
9553
|
+
}
|
|
9554
|
+
/** Server acknowledgement of our in-flight op. */
|
|
9555
|
+
#applyAck(op) {
|
|
9556
|
+
const ackedVersion = _nullishCoalesce(op.version, () => ( Math.max(this.#version, op.baseVersion + 1)));
|
|
9557
|
+
const predicted = this.#inFlightOps;
|
|
9558
|
+
const opId = this.#inFlightOpId;
|
|
9559
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9560
|
+
this.#inFlightOpId = void 0;
|
|
9561
|
+
this.#inFlightOps = [];
|
|
9562
|
+
let appliedOps = [];
|
|
9563
|
+
let result = { modified: false };
|
|
9564
|
+
if (!textOperationsEqual(op.ops, predicted)) {
|
|
9565
|
+
error2(
|
|
9566
|
+
"LiveText: acknowledgement did not match the local prediction; resynchronizing"
|
|
9567
|
+
);
|
|
9568
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9569
|
+
appliedOps = rebuilt.appliedOps;
|
|
9570
|
+
if (rebuilt.changes.length > 0) {
|
|
9571
|
+
result = {
|
|
9572
|
+
reverse: [],
|
|
9573
|
+
modified: {
|
|
9574
|
+
type: "LiveText",
|
|
9575
|
+
node: this,
|
|
9576
|
+
version: ackedVersion,
|
|
9577
|
+
updates: rebuilt.changes
|
|
9578
|
+
}
|
|
9579
|
+
};
|
|
9580
|
+
}
|
|
9581
|
+
}
|
|
9582
|
+
this.#version = Math.max(this.#version, ackedVersion);
|
|
9583
|
+
this.#recordAccepted(ackedVersion, appliedOps, opId);
|
|
9584
|
+
this.#flushQueued();
|
|
9585
|
+
return result;
|
|
9586
|
+
}
|
|
9587
|
+
/** An accepted op from another client (or a server-fabricated fix op). */
|
|
9588
|
+
#applyRemote(op) {
|
|
9589
|
+
const version = _nullishCoalesce(op.version, () => ( this.#version + 1));
|
|
9590
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9591
|
+
const [overInFlight, inFlight] = transformTextOperationsX(
|
|
9592
|
+
op.ops,
|
|
9593
|
+
this.#inFlightOps,
|
|
9594
|
+
"before"
|
|
9595
|
+
);
|
|
9596
|
+
const [applied, queued] = transformTextOperationsX(
|
|
9597
|
+
overInFlight,
|
|
9598
|
+
this.#queuedOps,
|
|
9599
|
+
"before"
|
|
9600
|
+
);
|
|
9601
|
+
this.#inFlightOps = inFlight;
|
|
9602
|
+
this.#queuedOps = queued;
|
|
9603
|
+
this.#recordAccepted(version, applied, op.opId);
|
|
9604
|
+
if (applied.length === 0) {
|
|
9605
|
+
this.#version = Math.max(this.#version, version);
|
|
9606
|
+
return { modified: false };
|
|
9607
|
+
}
|
|
9608
|
+
const reverse = this.#invertOperations(applied);
|
|
9609
|
+
const changes = this.#applyOperationsLocally(applied);
|
|
9610
|
+
this.#version = Math.max(this.#version, version);
|
|
9611
|
+
return {
|
|
9612
|
+
reverse,
|
|
9613
|
+
modified: {
|
|
9614
|
+
type: "LiveText",
|
|
9615
|
+
node: this,
|
|
9616
|
+
version: this.#version,
|
|
9617
|
+
updates: changes
|
|
9618
|
+
}
|
|
9619
|
+
};
|
|
9620
|
+
}
|
|
9621
|
+
/** Send the queued ops as the next in-flight op (after an ack). */
|
|
9622
|
+
#flushQueued() {
|
|
9623
|
+
if (this.#queuedOps.length === 0 || this._pool === void 0 || this._id === void 0) {
|
|
9624
|
+
return;
|
|
9625
|
+
}
|
|
9626
|
+
const opId = this._pool.generateOpId();
|
|
9627
|
+
this.#inFlightOpId = opId;
|
|
9628
|
+
this.#inFlightOps = this.#queuedOps;
|
|
9629
|
+
this.#queuedOps = [];
|
|
9630
|
+
this._pool.dispatch(
|
|
9631
|
+
[
|
|
9632
|
+
{
|
|
9633
|
+
type: OpCode.UPDATE_TEXT,
|
|
9634
|
+
id: this._id,
|
|
9635
|
+
opId,
|
|
9636
|
+
baseVersion: this.#version,
|
|
9637
|
+
ops: [...this.#inFlightOps]
|
|
9638
|
+
}
|
|
9639
|
+
],
|
|
9640
|
+
[],
|
|
9641
|
+
/* @__PURE__ */ new Map(),
|
|
9642
|
+
// The local content was already applied (and made undoable) when the
|
|
9643
|
+
// edits happened; this is purely an outbound flush.
|
|
9644
|
+
{ clearRedoStack: false }
|
|
9645
|
+
);
|
|
9646
|
+
}
|
|
9647
|
+
/**
|
|
9648
|
+
* Rebuild the local document as confirmed ⊕ queued ops, returning the
|
|
9649
|
+
* coarse delta that was applied. Only used by defensive recovery paths.
|
|
9650
|
+
*/
|
|
9651
|
+
#rebuildLocalFromConfirmed() {
|
|
9652
|
+
const before2 = this.#segments;
|
|
9653
|
+
const after2 = applyTextOperationsToSegments(this.#confirmed, [
|
|
9654
|
+
...this.#inFlightOps,
|
|
9655
|
+
...this.#queuedOps
|
|
9656
|
+
]);
|
|
9657
|
+
if (stableStringify(segmentsToData(before2)) === stableStringify(segmentsToData(after2))) {
|
|
9658
|
+
this.#segments = after2;
|
|
9659
|
+
return { appliedOps: [], changes: [] };
|
|
9660
|
+
}
|
|
9661
|
+
const beforeText = before2.map((segment) => segment.text).join("");
|
|
9662
|
+
this.#segments = after2;
|
|
9663
|
+
this.invalidate();
|
|
9664
|
+
const appliedOps = [];
|
|
9665
|
+
const changes = [];
|
|
9666
|
+
if (beforeText.length > 0) {
|
|
9667
|
+
appliedOps.push({ type: "delete", index: 0, length: beforeText.length });
|
|
9668
|
+
changes.push({
|
|
9669
|
+
type: "delete",
|
|
9670
|
+
index: 0,
|
|
9671
|
+
length: beforeText.length,
|
|
9672
|
+
deletedText: beforeText
|
|
9673
|
+
});
|
|
9674
|
+
}
|
|
9675
|
+
let index = 0;
|
|
9676
|
+
for (const segment of after2) {
|
|
9677
|
+
appliedOps.push({
|
|
9678
|
+
type: "insert",
|
|
9679
|
+
index,
|
|
9680
|
+
text: segment.text,
|
|
9681
|
+
attributes: segment.attributes
|
|
9682
|
+
});
|
|
9683
|
+
changes.push({
|
|
9684
|
+
type: "insert",
|
|
9685
|
+
index,
|
|
9686
|
+
text: segment.text,
|
|
9687
|
+
attributes: segment.attributes
|
|
9688
|
+
});
|
|
9689
|
+
index += segment.text.length;
|
|
9690
|
+
}
|
|
9691
|
+
return { appliedOps, changes };
|
|
9692
|
+
}
|
|
9693
|
+
/**
|
|
9694
|
+
* Reconcile this node against an authoritative storage snapshot (e.g.
|
|
9695
|
+
* after a reconnect). The confirmed state and version are replaced by the
|
|
9696
|
+
* snapshot's; pending (in-flight + queued) ops are preserved on top and
|
|
9697
|
+
* will be re-sent by the offline-ops replay.
|
|
9698
|
+
*
|
|
9699
|
+
* @internal
|
|
9700
|
+
*/
|
|
9701
|
+
_resyncText(data, version) {
|
|
9702
|
+
this.#confirmed = dataToSegments(data);
|
|
9703
|
+
this.#version = version;
|
|
9704
|
+
this.#acceptedOps = [];
|
|
9705
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9706
|
+
if (rebuilt.changes.length === 0) {
|
|
9707
|
+
return void 0;
|
|
9708
|
+
}
|
|
9709
|
+
return {
|
|
9710
|
+
type: "LiveText",
|
|
9711
|
+
node: this,
|
|
9712
|
+
version: this.#version,
|
|
9713
|
+
updates: rebuilt.changes
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
/**
|
|
9717
|
+
* Called when the server rejected one of our ops. Drops all pending state
|
|
9718
|
+
* for this node (edits queued behind a rejected op cannot be trusted
|
|
9719
|
+
* either); the room follows up with a storage resync.
|
|
9720
|
+
*
|
|
9721
|
+
* @internal
|
|
9722
|
+
*/
|
|
9723
|
+
_rejectPendingOp(opId) {
|
|
9724
|
+
if (opId !== this.#inFlightOpId) {
|
|
9725
|
+
return;
|
|
9726
|
+
}
|
|
9727
|
+
this.#inFlightOpId = void 0;
|
|
9728
|
+
this.#inFlightOps = [];
|
|
9729
|
+
this.#queuedOps = [];
|
|
9730
|
+
}
|
|
9731
|
+
#recordAccepted(version, ops, opId) {
|
|
9732
|
+
if (this.#acceptedOps.some((entry) => entry.version === version)) {
|
|
9733
|
+
return;
|
|
9734
|
+
}
|
|
9735
|
+
this.#acceptedOps.push({ version, opId, ops: [...ops] });
|
|
9736
|
+
this.#acceptedOps.sort((left, right) => left.version - right.version);
|
|
9737
|
+
if (this.#acceptedOps.length > ACCEPTED_OPS_HISTORY_LIMIT) {
|
|
9738
|
+
this.#acceptedOps.splice(
|
|
9739
|
+
0,
|
|
9740
|
+
this.#acceptedOps.length - ACCEPTED_OPS_HISTORY_LIMIT
|
|
9741
|
+
);
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
#applyOperationsLocally(ops) {
|
|
9745
|
+
const changes = [];
|
|
9746
|
+
for (const op of ops) {
|
|
9747
|
+
if (op.type === "insert") {
|
|
9748
|
+
this.#segments = applyInsert(
|
|
9749
|
+
this.#segments,
|
|
9750
|
+
op.index,
|
|
9751
|
+
op.text,
|
|
9752
|
+
op.attributes
|
|
9753
|
+
);
|
|
9754
|
+
changes.push({
|
|
9755
|
+
type: "insert",
|
|
9756
|
+
index: op.index,
|
|
9757
|
+
text: op.text,
|
|
9758
|
+
attributes: op.attributes
|
|
9759
|
+
});
|
|
9760
|
+
} else if (op.type === "delete") {
|
|
9761
|
+
const result = applyDelete(this.#segments, op.index, op.length);
|
|
9762
|
+
this.#segments = result.segments;
|
|
9763
|
+
changes.push({
|
|
9764
|
+
type: "delete",
|
|
9765
|
+
index: op.index,
|
|
9766
|
+
length: op.length,
|
|
9767
|
+
deletedText: result.deletedText
|
|
9768
|
+
});
|
|
9769
|
+
} else {
|
|
9770
|
+
this.#segments = applyFormat(
|
|
9771
|
+
this.#segments,
|
|
9772
|
+
op.index,
|
|
9773
|
+
op.length,
|
|
9774
|
+
op.attributes
|
|
9775
|
+
);
|
|
9776
|
+
changes.push({
|
|
9777
|
+
type: "format",
|
|
9778
|
+
index: op.index,
|
|
9779
|
+
length: op.length,
|
|
9780
|
+
attributes: op.attributes
|
|
9781
|
+
});
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
this.invalidate();
|
|
9785
|
+
return changes;
|
|
9786
|
+
}
|
|
9787
|
+
#invertOperations(ops) {
|
|
9788
|
+
return [
|
|
9789
|
+
{
|
|
9790
|
+
type: OpCode.UPDATE_TEXT,
|
|
9791
|
+
id: nn(this._id),
|
|
9792
|
+
baseVersion: this.#version,
|
|
9793
|
+
ops: invertTextOperations(this.#segments, ops)
|
|
9794
|
+
}
|
|
9795
|
+
];
|
|
9796
|
+
}
|
|
9797
|
+
/** Returns the plain text content without attributes. Equivalent to joining the text from each segment in {@link LiveText.toJSON}. */
|
|
9798
|
+
toString() {
|
|
9799
|
+
return this.#segments.map((segment) => segment.text).join("");
|
|
9800
|
+
}
|
|
9801
|
+
/**
|
|
9802
|
+
* Returns a JSON-compatible snapshot of the document as a {@link LiveTextData}
|
|
9803
|
+
* array.
|
|
9804
|
+
*
|
|
9805
|
+
* @example
|
|
9806
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]).toJSON();
|
|
9807
|
+
* // [["Hello ", { bold: true }], ["world"]]
|
|
9808
|
+
*/
|
|
9809
|
+
toJSON() {
|
|
9810
|
+
return super.toJSON();
|
|
9811
|
+
}
|
|
9812
|
+
/** @internal */
|
|
9813
|
+
_toJSON() {
|
|
9814
|
+
return segmentsToData(this.#segments);
|
|
9815
|
+
}
|
|
9816
|
+
/** @internal */
|
|
9817
|
+
_toTreeNode(key) {
|
|
9818
|
+
return {
|
|
9819
|
+
type: "LiveText",
|
|
9820
|
+
id: _nullishCoalesce(this._id, () => ( nanoid())),
|
|
9821
|
+
key,
|
|
9822
|
+
payload: [
|
|
9823
|
+
{
|
|
9824
|
+
type: "Json",
|
|
9825
|
+
id: `${_nullishCoalesce(this._id, () => ( nanoid()))}:text`,
|
|
9826
|
+
key: "text",
|
|
9827
|
+
payload: this.toString()
|
|
9828
|
+
}
|
|
9829
|
+
]
|
|
9830
|
+
};
|
|
9831
|
+
}
|
|
9832
|
+
clone() {
|
|
9833
|
+
return new _LiveText(this.toJSON(), this.#version);
|
|
9834
|
+
}
|
|
9835
|
+
};
|
|
9836
|
+
|
|
9837
|
+
// src/crdts/liveblocks-helpers.ts
|
|
9838
|
+
function creationOpToLiveNode(op) {
|
|
9839
|
+
return lsonToLiveNode(creationOpToLson(op));
|
|
9840
|
+
}
|
|
9841
|
+
function creationOpToLson(op) {
|
|
9842
|
+
switch (op.type) {
|
|
9843
|
+
case OpCode.CREATE_REGISTER:
|
|
9844
|
+
return op.data;
|
|
9845
|
+
case OpCode.CREATE_OBJECT:
|
|
9846
|
+
return new LiveObject(op.data);
|
|
9847
|
+
case OpCode.CREATE_MAP:
|
|
9848
|
+
return new LiveMap();
|
|
9849
|
+
case OpCode.CREATE_LIST:
|
|
9850
|
+
return new LiveList([]);
|
|
9851
|
+
case OpCode.CREATE_TEXT:
|
|
9852
|
+
return new LiveText(op.data, op.version);
|
|
9853
|
+
default:
|
|
9854
|
+
return assertNever(op, "Unknown creation Op");
|
|
9855
|
+
}
|
|
9856
|
+
}
|
|
9857
|
+
function isSameNodeOrChildOf(node, parent) {
|
|
9858
|
+
if (node === parent) {
|
|
9859
|
+
return true;
|
|
9860
|
+
}
|
|
9861
|
+
if (node.parent.type === "HasParent") {
|
|
9862
|
+
return isSameNodeOrChildOf(node.parent.node, parent);
|
|
9863
|
+
}
|
|
9864
|
+
return false;
|
|
9865
|
+
}
|
|
9866
|
+
function deserialize(node, parentToChildren, pool) {
|
|
9867
|
+
if (isObjectStorageNode(node)) {
|
|
9868
|
+
return LiveObject._deserialize(node, parentToChildren, pool);
|
|
9869
|
+
} else if (isListStorageNode(node)) {
|
|
9870
|
+
return LiveList._deserialize(node, parentToChildren, pool);
|
|
9871
|
+
} else if (isMapStorageNode(node)) {
|
|
9872
|
+
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
9873
|
+
} else if (isRegisterStorageNode(node)) {
|
|
9874
|
+
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
9875
|
+
} else if (isTextStorageNode(node)) {
|
|
9876
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
9877
|
+
} else {
|
|
9878
|
+
throw new Error("Unexpected CRDT type");
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
function deserializeToLson(node, parentToChildren, pool) {
|
|
9882
|
+
if (isObjectStorageNode(node)) {
|
|
9883
|
+
return LiveObject._deserialize(node, parentToChildren, pool);
|
|
9884
|
+
} else if (isListStorageNode(node)) {
|
|
9885
|
+
return LiveList._deserialize(node, parentToChildren, pool);
|
|
9886
|
+
} else if (isMapStorageNode(node)) {
|
|
9887
|
+
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
9888
|
+
} else if (isRegisterStorageNode(node)) {
|
|
9889
|
+
return node[1].data;
|
|
9890
|
+
} else if (isTextStorageNode(node)) {
|
|
9891
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
9892
|
+
} else {
|
|
9893
|
+
throw new Error("Unexpected CRDT type");
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
function isLiveStructure(value) {
|
|
9897
|
+
return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
|
|
9898
|
+
}
|
|
9899
|
+
function isLiveNode(value) {
|
|
9900
|
+
return isLiveStructure(value) || isLiveRegister(value);
|
|
9901
|
+
}
|
|
9902
|
+
function isLiveList(value) {
|
|
9903
|
+
return value instanceof LiveList;
|
|
9904
|
+
}
|
|
9905
|
+
function isLiveMap(value) {
|
|
9906
|
+
return value instanceof LiveMap;
|
|
9907
|
+
}
|
|
9908
|
+
function isLiveObject(value) {
|
|
9909
|
+
return value instanceof LiveObject;
|
|
9910
|
+
}
|
|
9911
|
+
function isLiveText(value) {
|
|
9912
|
+
return value instanceof LiveText;
|
|
9913
|
+
}
|
|
9914
|
+
function isLiveRegister(value) {
|
|
9915
|
+
return value instanceof LiveRegister;
|
|
9916
|
+
}
|
|
9917
|
+
function cloneLson(value) {
|
|
8501
9918
|
return value === void 0 ? void 0 : isLiveStructure(value) ? value.clone() : deepClone(value);
|
|
8502
9919
|
}
|
|
8503
9920
|
function liveNodeToLson(obj) {
|
|
8504
9921
|
if (obj instanceof LiveRegister) {
|
|
8505
9922
|
return obj.data;
|
|
8506
|
-
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
|
|
9923
|
+
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
|
|
8507
9924
|
return obj;
|
|
8508
9925
|
} else {
|
|
8509
9926
|
return assertNever(obj, "Unknown AbstractCrdt");
|
|
8510
9927
|
}
|
|
8511
9928
|
}
|
|
8512
9929
|
function lsonToLiveNode(value) {
|
|
8513
|
-
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
|
|
9930
|
+
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
|
|
8514
9931
|
return value;
|
|
8515
9932
|
} else {
|
|
8516
9933
|
return new LiveRegister(value);
|
|
@@ -8541,23 +9958,68 @@ function dumpPool(pool) {
|
|
|
8541
9958
|
(r) => ` ${r.id} parent=${r.parentId} key=${r.key || "\u2014"} ${r.value}`
|
|
8542
9959
|
).join("\n");
|
|
8543
9960
|
}
|
|
8544
|
-
function
|
|
9961
|
+
function isJsonEq(a, b) {
|
|
9962
|
+
if (a === b) {
|
|
9963
|
+
return true;
|
|
9964
|
+
}
|
|
9965
|
+
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
|
|
9966
|
+
return false;
|
|
9967
|
+
}
|
|
9968
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
9969
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
|
|
9970
|
+
return false;
|
|
9971
|
+
}
|
|
9972
|
+
for (let i = 0; i < a.length; i++) {
|
|
9973
|
+
if (!isJsonEq(a[i], b[i])) {
|
|
9974
|
+
return false;
|
|
9975
|
+
}
|
|
9976
|
+
}
|
|
9977
|
+
return true;
|
|
9978
|
+
}
|
|
9979
|
+
const aKeys = Object.keys(a);
|
|
9980
|
+
if (aKeys.length !== Object.keys(b).length) {
|
|
9981
|
+
return false;
|
|
9982
|
+
}
|
|
9983
|
+
for (const key of aKeys) {
|
|
9984
|
+
if (!isJsonEq(a[key], b[key])) {
|
|
9985
|
+
return false;
|
|
9986
|
+
}
|
|
9987
|
+
}
|
|
9988
|
+
return true;
|
|
9989
|
+
}
|
|
9990
|
+
function diffNodeMap(prev, next) {
|
|
8545
9991
|
const ops = [];
|
|
8546
|
-
|
|
8547
|
-
if (!
|
|
9992
|
+
prev.forEach((_, id) => {
|
|
9993
|
+
if (!next.get(id)) {
|
|
8548
9994
|
ops.push({ type: OpCode.DELETE_CRDT, id });
|
|
8549
9995
|
}
|
|
8550
9996
|
});
|
|
8551
|
-
|
|
8552
|
-
const currentCrdt =
|
|
9997
|
+
next.forEach((crdt, id) => {
|
|
9998
|
+
const currentCrdt = prev.get(id);
|
|
8553
9999
|
if (currentCrdt) {
|
|
8554
10000
|
if (crdt.type === CrdtType.OBJECT) {
|
|
8555
|
-
if (currentCrdt.type !== CrdtType.OBJECT
|
|
8556
|
-
ops.push({
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
10001
|
+
if (currentCrdt.type !== CrdtType.OBJECT) {
|
|
10002
|
+
ops.push({ type: OpCode.UPDATE_OBJECT, id, data: crdt.data });
|
|
10003
|
+
} else {
|
|
10004
|
+
const changed = /* @__PURE__ */ new Map();
|
|
10005
|
+
for (const key of Object.keys(crdt.data)) {
|
|
10006
|
+
const value = crdt.data[key];
|
|
10007
|
+
if (value !== void 0 && !isJsonEq(value, currentCrdt.data[key])) {
|
|
10008
|
+
changed.set(key, value);
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
if (changed.size > 0) {
|
|
10012
|
+
ops.push({
|
|
10013
|
+
type: OpCode.UPDATE_OBJECT,
|
|
10014
|
+
id,
|
|
10015
|
+
data: Object.fromEntries(changed)
|
|
10016
|
+
});
|
|
10017
|
+
}
|
|
10018
|
+
for (const key of Object.keys(currentCrdt.data)) {
|
|
10019
|
+
if (!(key in crdt.data)) {
|
|
10020
|
+
ops.push({ type: OpCode.DELETE_OBJECT_KEY, id, key });
|
|
10021
|
+
}
|
|
10022
|
+
}
|
|
8561
10023
|
}
|
|
8562
10024
|
}
|
|
8563
10025
|
if (crdt.parentKey !== currentCrdt.parentKey) {
|
|
@@ -8608,6 +10070,16 @@ function getTreesDiffOperations(currentItems, newItems) {
|
|
|
8608
10070
|
parentKey: crdt.parentKey
|
|
8609
10071
|
});
|
|
8610
10072
|
break;
|
|
10073
|
+
case CrdtType.TEXT:
|
|
10074
|
+
ops.push({
|
|
10075
|
+
type: OpCode.CREATE_TEXT,
|
|
10076
|
+
id,
|
|
10077
|
+
parentId: crdt.parentId,
|
|
10078
|
+
parentKey: crdt.parentKey,
|
|
10079
|
+
data: crdt.data,
|
|
10080
|
+
version: crdt.version
|
|
10081
|
+
});
|
|
10082
|
+
break;
|
|
8611
10083
|
}
|
|
8612
10084
|
}
|
|
8613
10085
|
});
|
|
@@ -8640,19 +10112,43 @@ function mergeListStorageUpdates(first, second) {
|
|
|
8640
10112
|
updates: updates.concat(second.updates)
|
|
8641
10113
|
};
|
|
8642
10114
|
}
|
|
10115
|
+
function mergeTextStorageUpdates(first, second) {
|
|
10116
|
+
return {
|
|
10117
|
+
...second,
|
|
10118
|
+
updates: first.updates.concat(second.updates)
|
|
10119
|
+
};
|
|
10120
|
+
}
|
|
8643
10121
|
function mergeStorageUpdates(first, second) {
|
|
8644
10122
|
if (first === void 0) {
|
|
8645
10123
|
return second;
|
|
8646
10124
|
}
|
|
10125
|
+
let merged;
|
|
8647
10126
|
if (first.type === "LiveObject" && second.type === "LiveObject") {
|
|
8648
|
-
|
|
10127
|
+
merged = mergeObjectStorageUpdates(first, second);
|
|
8649
10128
|
} else if (first.type === "LiveMap" && second.type === "LiveMap") {
|
|
8650
|
-
|
|
10129
|
+
merged = mergeMapStorageUpdates(first, second);
|
|
8651
10130
|
} else if (first.type === "LiveList" && second.type === "LiveList") {
|
|
8652
|
-
|
|
10131
|
+
merged = mergeListStorageUpdates(first, second);
|
|
10132
|
+
} else if (first.type === "LiveText" && second.type === "LiveText") {
|
|
10133
|
+
merged = mergeTextStorageUpdates(first, second);
|
|
8653
10134
|
} else {
|
|
10135
|
+
merged = second;
|
|
10136
|
+
}
|
|
10137
|
+
const sa = first[kStorageUpdateSource];
|
|
10138
|
+
const sb = second[kStorageUpdateSource];
|
|
10139
|
+
if (sa !== void 0 || sb !== void 0) {
|
|
10140
|
+
if (_optionalChain([sa, 'optionalAccess', _222 => _222.origin]) === "remote" || _optionalChain([sb, 'optionalAccess', _223 => _223.origin]) === "remote") {
|
|
10141
|
+
merged[kStorageUpdateSource] = { origin: "remote" };
|
|
10142
|
+
} else if (_optionalChain([sa, 'optionalAccess', _224 => _224.via]) === "history" || _optionalChain([sb, 'optionalAccess', _225 => _225.via]) === "history") {
|
|
10143
|
+
const historySource = _optionalChain([sb, 'optionalAccess', _226 => _226.via]) === "history" ? sb : _optionalChain([sa, 'optionalAccess', _227 => _227.via]) === "history" ? sa : void 0;
|
|
10144
|
+
if (_optionalChain([historySource, 'optionalAccess', _228 => _228.via]) === "history") {
|
|
10145
|
+
merged[kStorageUpdateSource] = historySource;
|
|
10146
|
+
}
|
|
10147
|
+
} else {
|
|
10148
|
+
merged[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
10149
|
+
}
|
|
8654
10150
|
}
|
|
8655
|
-
return
|
|
10151
|
+
return merged;
|
|
8656
10152
|
}
|
|
8657
10153
|
|
|
8658
10154
|
// src/devtools/bridge.ts
|
|
@@ -8668,7 +10164,7 @@ function sendToPanel(message, options) {
|
|
|
8668
10164
|
...message,
|
|
8669
10165
|
source: "liveblocks-devtools-client"
|
|
8670
10166
|
};
|
|
8671
|
-
if (!(_optionalChain([options, 'optionalAccess',
|
|
10167
|
+
if (!(_optionalChain([options, 'optionalAccess', _229 => _229.force]) || _bridgeActive)) {
|
|
8672
10168
|
return;
|
|
8673
10169
|
}
|
|
8674
10170
|
window.postMessage(fullMsg, "*");
|
|
@@ -8676,7 +10172,7 @@ function sendToPanel(message, options) {
|
|
|
8676
10172
|
var eventSource = makeEventSource();
|
|
8677
10173
|
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
8678
10174
|
window.addEventListener("message", (event) => {
|
|
8679
|
-
if (event.source === window && _optionalChain([event, 'access',
|
|
10175
|
+
if (event.source === window && _optionalChain([event, 'access', _230 => _230.data, 'optionalAccess', _231 => _231.source]) === "liveblocks-devtools-panel") {
|
|
8680
10176
|
eventSource.notify(event.data);
|
|
8681
10177
|
} else {
|
|
8682
10178
|
}
|
|
@@ -8780,7 +10276,7 @@ function partialSyncConnection(room) {
|
|
|
8780
10276
|
});
|
|
8781
10277
|
}
|
|
8782
10278
|
function partialSyncStorage(room) {
|
|
8783
|
-
const root = room.
|
|
10279
|
+
const root = room.getStorageOrNull();
|
|
8784
10280
|
if (root) {
|
|
8785
10281
|
sendToPanel({
|
|
8786
10282
|
msg: "room::sync::partial",
|
|
@@ -8810,7 +10306,7 @@ function partialSyncOthers(room) {
|
|
|
8810
10306
|
}
|
|
8811
10307
|
}
|
|
8812
10308
|
function fullSync(room) {
|
|
8813
|
-
const root = room.
|
|
10309
|
+
const root = room.getStorageOrNull();
|
|
8814
10310
|
const me = room[kInternal].getSelf_forDevTools();
|
|
8815
10311
|
const others = room[kInternal].getOthers_forDevTools();
|
|
8816
10312
|
room.fetchYDoc("");
|
|
@@ -8818,7 +10314,7 @@ function fullSync(room) {
|
|
|
8818
10314
|
msg: "room::sync::full",
|
|
8819
10315
|
roomId: room.id,
|
|
8820
10316
|
status: room.getStatus(),
|
|
8821
|
-
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess',
|
|
10317
|
+
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _232 => _232.toTreeNode, 'call', _233 => _233("root"), 'access', _234 => _234.payload]), () => ( null)),
|
|
8822
10318
|
me,
|
|
8823
10319
|
others
|
|
8824
10320
|
});
|
|
@@ -9235,15 +10731,15 @@ var ClientMsgCode = Object.freeze({
|
|
|
9235
10731
|
|
|
9236
10732
|
// src/refs/ManagedOthers.ts
|
|
9237
10733
|
function makeUser(conn, presence) {
|
|
9238
|
-
const { connectionId, id, info } = conn;
|
|
9239
|
-
const canWrite =
|
|
10734
|
+
const { connectionId, id, info, access } = conn;
|
|
10735
|
+
const { canWrite, canComment } = access;
|
|
9240
10736
|
return freeze(
|
|
9241
10737
|
compactObject({
|
|
9242
10738
|
connectionId,
|
|
9243
10739
|
id,
|
|
9244
10740
|
info,
|
|
9245
10741
|
canWrite,
|
|
9246
|
-
canComment
|
|
10742
|
+
canComment,
|
|
9247
10743
|
isReadOnly: !canWrite,
|
|
9248
10744
|
// Deprecated, kept for backward-compatibility
|
|
9249
10745
|
presence
|
|
@@ -9314,7 +10810,7 @@ var ManagedOthers = class {
|
|
|
9314
10810
|
* Records a known connection. This records the connection ID and the
|
|
9315
10811
|
* associated metadata.
|
|
9316
10812
|
*/
|
|
9317
|
-
setConnection(connectionId, metaUserId, metaUserInfo,
|
|
10813
|
+
setConnection(connectionId, metaUserId, metaUserInfo, access) {
|
|
9318
10814
|
this.#internal.mutate((state) => {
|
|
9319
10815
|
state.connections.set(
|
|
9320
10816
|
connectionId,
|
|
@@ -9322,7 +10818,7 @@ var ManagedOthers = class {
|
|
|
9322
10818
|
connectionId,
|
|
9323
10819
|
id: metaUserId,
|
|
9324
10820
|
info: metaUserInfo,
|
|
9325
|
-
|
|
10821
|
+
access
|
|
9326
10822
|
})
|
|
9327
10823
|
);
|
|
9328
10824
|
if (!state.presences.has(connectionId)) {
|
|
@@ -9475,6 +10971,14 @@ function defaultMessageFromContext(context) {
|
|
|
9475
10971
|
|
|
9476
10972
|
// src/room.ts
|
|
9477
10973
|
var FEEDS_TIMEOUT = 5e3;
|
|
10974
|
+
function connectionAccessFromScopes(scopes) {
|
|
10975
|
+
const roomPermissions = normalizeRoomPermissions(scopes);
|
|
10976
|
+
const matrix = permissionMatrixFromScopes(roomPermissions);
|
|
10977
|
+
return {
|
|
10978
|
+
canWrite: hasPermissionAccess(matrix, "storage", "write"),
|
|
10979
|
+
canComment: hasPermissionAccess(matrix, "comments", "write")
|
|
10980
|
+
};
|
|
10981
|
+
}
|
|
9478
10982
|
function makeIdFactory(connectionId) {
|
|
9479
10983
|
let count = 0;
|
|
9480
10984
|
return () => `${connectionId}:${count++}`;
|
|
@@ -9497,15 +11001,15 @@ function installBackgroundTabSpy() {
|
|
|
9497
11001
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
9498
11002
|
const inBackgroundSince = { current: null };
|
|
9499
11003
|
function onVisibilityChange() {
|
|
9500
|
-
if (_optionalChain([doc, 'optionalAccess',
|
|
11004
|
+
if (_optionalChain([doc, 'optionalAccess', _235 => _235.visibilityState]) === "hidden") {
|
|
9501
11005
|
inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
|
|
9502
11006
|
} else {
|
|
9503
11007
|
inBackgroundSince.current = null;
|
|
9504
11008
|
}
|
|
9505
11009
|
}
|
|
9506
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11010
|
+
_optionalChain([doc, 'optionalAccess', _236 => _236.addEventListener, 'call', _237 => _237("visibilitychange", onVisibilityChange)]);
|
|
9507
11011
|
const unsub = () => {
|
|
9508
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11012
|
+
_optionalChain([doc, 'optionalAccess', _238 => _238.removeEventListener, 'call', _239 => _239("visibilitychange", onVisibilityChange)]);
|
|
9509
11013
|
};
|
|
9510
11014
|
return [inBackgroundSince, unsub];
|
|
9511
11015
|
}
|
|
@@ -9588,6 +11092,8 @@ function createRoom(options, config) {
|
|
|
9588
11092
|
activeBatch: null,
|
|
9589
11093
|
unacknowledgedOps
|
|
9590
11094
|
};
|
|
11095
|
+
let nextHistoryItemId = 0;
|
|
11096
|
+
let historyDisabled = 0;
|
|
9591
11097
|
const nodeMapBuffer = makeNodeMapBuffer();
|
|
9592
11098
|
const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
|
|
9593
11099
|
let lastTokenKey;
|
|
@@ -9647,12 +11153,13 @@ function createRoom(options, config) {
|
|
|
9647
11153
|
)
|
|
9648
11154
|
};
|
|
9649
11155
|
if (_getStorage$ !== null) {
|
|
9650
|
-
refreshStorage(
|
|
11156
|
+
refreshStorage();
|
|
9651
11157
|
}
|
|
9652
11158
|
flushNowOrSoon();
|
|
9653
11159
|
}
|
|
9654
11160
|
function onDidDisconnect() {
|
|
9655
11161
|
clearTimeout(context.buffer.flushTimerID);
|
|
11162
|
+
context.unacknowledgedOps.markAllAsPossiblyStored();
|
|
9656
11163
|
}
|
|
9657
11164
|
managedSocket.events.onMessage.subscribe(handleServerMessage);
|
|
9658
11165
|
managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
|
|
@@ -9671,7 +11178,10 @@ function createRoom(options, config) {
|
|
|
9671
11178
|
}
|
|
9672
11179
|
}
|
|
9673
11180
|
});
|
|
9674
|
-
function onDispatch(ops, reverse, storageUpdates) {
|
|
11181
|
+
function onDispatch(ops, reverse, storageUpdates, options2) {
|
|
11182
|
+
for (const value of storageUpdates.values()) {
|
|
11183
|
+
value[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
11184
|
+
}
|
|
9675
11185
|
if (context.activeBatch) {
|
|
9676
11186
|
for (const op of ops) {
|
|
9677
11187
|
context.activeBatch.ops.push(op);
|
|
@@ -9690,16 +11200,18 @@ function createRoom(options, config) {
|
|
|
9690
11200
|
if (reverse.length > 0) {
|
|
9691
11201
|
addToUndoStack(reverse);
|
|
9692
11202
|
}
|
|
11203
|
+
if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _240 => _240.clearRedoStack]), () => ( ops.length > 0))) {
|
|
11204
|
+
clearRedoStack();
|
|
11205
|
+
}
|
|
9693
11206
|
if (ops.length > 0) {
|
|
9694
|
-
context.redoStack.length = 0;
|
|
9695
11207
|
dispatchOps(ops);
|
|
9696
11208
|
}
|
|
9697
11209
|
notify({ storageUpdates });
|
|
9698
11210
|
}
|
|
9699
11211
|
}
|
|
9700
11212
|
function isStorageWritable() {
|
|
9701
|
-
const
|
|
9702
|
-
return
|
|
11213
|
+
const permissionMatrix = _optionalChain([context, 'access', _241 => _241.dynamicSessionInfoSig, 'access', _242 => _242.get, 'call', _243 => _243(), 'optionalAccess', _244 => _244.permissionMatrix]);
|
|
11214
|
+
return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
|
|
9703
11215
|
}
|
|
9704
11216
|
const eventHub = {
|
|
9705
11217
|
status: makeEventSource(),
|
|
@@ -9711,6 +11223,7 @@ function createRoom(options, config) {
|
|
|
9711
11223
|
others: makeEventSource(),
|
|
9712
11224
|
storageBatch: makeEventSource(),
|
|
9713
11225
|
history: makeEventSource(),
|
|
11226
|
+
privateHistory: makeEventSource(),
|
|
9714
11227
|
storageDidLoad: makeEventSource(),
|
|
9715
11228
|
storageStatus: makeEventSource(),
|
|
9716
11229
|
ydoc: makeEventSource(),
|
|
@@ -9760,14 +11273,22 @@ function createRoom(options, config) {
|
|
|
9760
11273
|
if (staticSession === null || dynamicSession === null) {
|
|
9761
11274
|
return null;
|
|
9762
11275
|
} else {
|
|
9763
|
-
const canWrite =
|
|
11276
|
+
const canWrite = hasPermissionAccess(
|
|
11277
|
+
dynamicSession.permissionMatrix,
|
|
11278
|
+
"storage",
|
|
11279
|
+
"write"
|
|
11280
|
+
);
|
|
9764
11281
|
return {
|
|
9765
11282
|
connectionId: dynamicSession.actor,
|
|
9766
11283
|
id: staticSession.userId,
|
|
9767
11284
|
info: staticSession.userInfo,
|
|
9768
11285
|
presence: myPresence,
|
|
9769
11286
|
canWrite,
|
|
9770
|
-
canComment:
|
|
11287
|
+
canComment: hasPermissionAccess(
|
|
11288
|
+
dynamicSession.permissionMatrix,
|
|
11289
|
+
"comments",
|
|
11290
|
+
"write"
|
|
11291
|
+
)
|
|
9771
11292
|
};
|
|
9772
11293
|
}
|
|
9773
11294
|
}
|
|
@@ -9793,12 +11314,25 @@ function createRoom(options, config) {
|
|
|
9793
11314
|
for (const [id, crdt] of context.pool.nodes) {
|
|
9794
11315
|
currentItems.set(id, crdt._serialize());
|
|
9795
11316
|
}
|
|
9796
|
-
const ops =
|
|
9797
|
-
const result = applyRemoteOps(
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
11317
|
+
const ops = diffNodeMap(currentItems, nodes);
|
|
11318
|
+
const result = applyRemoteOps(ops);
|
|
11319
|
+
for (const [id, crdt] of nodes) {
|
|
11320
|
+
if (crdt.type === CrdtType.TEXT) {
|
|
11321
|
+
const node = context.pool.nodes.get(id);
|
|
11322
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11323
|
+
const update = node._resyncText(crdt.data, crdt.version);
|
|
11324
|
+
if (update !== void 0) {
|
|
11325
|
+
result.updates.storageUpdates.set(
|
|
11326
|
+
id,
|
|
11327
|
+
mergeStorageUpdates(
|
|
11328
|
+
result.updates.storageUpdates.get(id),
|
|
11329
|
+
update
|
|
11330
|
+
)
|
|
11331
|
+
);
|
|
11332
|
+
}
|
|
11333
|
+
}
|
|
11334
|
+
}
|
|
11335
|
+
}
|
|
9802
11336
|
notify(result.updates);
|
|
9803
11337
|
} else {
|
|
9804
11338
|
context.root = LiveObject._fromItems(
|
|
@@ -9806,7 +11340,7 @@ function createRoom(options, config) {
|
|
|
9806
11340
|
context.pool
|
|
9807
11341
|
);
|
|
9808
11342
|
}
|
|
9809
|
-
const canWrite = _nullishCoalesce(_optionalChain([self, 'access',
|
|
11343
|
+
const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _245 => _245.get, 'call', _246 => _246(), 'optionalAccess', _247 => _247.canWrite]), () => ( true));
|
|
9810
11344
|
const root = context.root;
|
|
9811
11345
|
disableHistory(() => {
|
|
9812
11346
|
for (const key in context.initialStorage) {
|
|
@@ -9822,11 +11356,26 @@ function createRoom(options, config) {
|
|
|
9822
11356
|
}
|
|
9823
11357
|
});
|
|
9824
11358
|
}
|
|
11359
|
+
function notifyPrivateHistory(event) {
|
|
11360
|
+
if (historyDisabled > 0) return;
|
|
11361
|
+
eventHub.privateHistory.notify(event);
|
|
11362
|
+
}
|
|
11363
|
+
function clearRedoStack() {
|
|
11364
|
+
if (context.redoStack.length === 0) return;
|
|
11365
|
+
const ids = context.redoStack.map((item) => item.id);
|
|
11366
|
+
context.redoStack.length = 0;
|
|
11367
|
+
notifyPrivateHistory({ action: "discard", ids });
|
|
11368
|
+
}
|
|
9825
11369
|
function _addToRealUndoStack(frames) {
|
|
9826
11370
|
if (context.undoStack.length >= 50) {
|
|
9827
|
-
context.undoStack.shift();
|
|
11371
|
+
const evicted = context.undoStack.shift();
|
|
11372
|
+
if (evicted !== void 0) {
|
|
11373
|
+
notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
|
|
11374
|
+
}
|
|
9828
11375
|
}
|
|
9829
|
-
|
|
11376
|
+
const id = nextHistoryItemId++;
|
|
11377
|
+
context.undoStack.push({ id, frames });
|
|
11378
|
+
notifyPrivateHistory({ action: "push", id });
|
|
9830
11379
|
onHistoryChange();
|
|
9831
11380
|
}
|
|
9832
11381
|
function addToUndoStack(frames) {
|
|
@@ -9864,7 +11413,7 @@ function createRoom(options, config) {
|
|
|
9864
11413
|
"Internal. Tried to get connection id but connection was never open"
|
|
9865
11414
|
);
|
|
9866
11415
|
}
|
|
9867
|
-
function applyLocalOps(frames) {
|
|
11416
|
+
function applyLocalOps(frames, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
9868
11417
|
const [pframes, ops] = partition(
|
|
9869
11418
|
frames,
|
|
9870
11419
|
(f) => f.type === "presence"
|
|
@@ -9876,20 +11425,20 @@ function createRoom(options, config) {
|
|
|
9876
11425
|
pframes,
|
|
9877
11426
|
opsWithOpIds,
|
|
9878
11427
|
/* isLocal */
|
|
9879
|
-
true
|
|
11428
|
+
true,
|
|
11429
|
+
localStorageUpdateSource
|
|
9880
11430
|
);
|
|
9881
11431
|
return { opsToEmit: opsWithOpIds, reverse, updates };
|
|
9882
11432
|
}
|
|
9883
|
-
function applyRemoteOps(ops
|
|
11433
|
+
function applyRemoteOps(ops) {
|
|
9884
11434
|
return applyOps(
|
|
9885
11435
|
[],
|
|
9886
11436
|
ops,
|
|
9887
11437
|
/* isLocal */
|
|
9888
|
-
false
|
|
9889
|
-
fromSnapshot
|
|
11438
|
+
false
|
|
9890
11439
|
);
|
|
9891
11440
|
}
|
|
9892
|
-
function applyOps(pframes, ops, isLocal,
|
|
11441
|
+
function applyOps(pframes, ops, isLocal, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
9893
11442
|
const output = {
|
|
9894
11443
|
reverse: new Deque(),
|
|
9895
11444
|
storageUpdates: /* @__PURE__ */ new Map(),
|
|
@@ -9925,8 +11474,9 @@ function createRoom(options, config) {
|
|
|
9925
11474
|
} else {
|
|
9926
11475
|
source = 1 /* THEIRS */;
|
|
9927
11476
|
}
|
|
9928
|
-
const applyOpResult = applyOp(op, source
|
|
11477
|
+
const applyOpResult = applyOp(op, source);
|
|
9929
11478
|
if (applyOpResult.modified) {
|
|
11479
|
+
applyOpResult.modified[kStorageUpdateSource] = source === 1 /* THEIRS */ ? { origin: "remote" } : localStorageUpdateSource;
|
|
9930
11480
|
const nodeId = applyOpResult.modified.node._id;
|
|
9931
11481
|
if (!(nodeId && createdNodeIds.has(nodeId))) {
|
|
9932
11482
|
output.storageUpdates.set(
|
|
@@ -9938,7 +11488,7 @@ function createRoom(options, config) {
|
|
|
9938
11488
|
);
|
|
9939
11489
|
output.reverse.pushLeft(applyOpResult.reverse);
|
|
9940
11490
|
}
|
|
9941
|
-
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
|
|
11491
|
+
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
|
|
9942
11492
|
createdNodeIds.add(op.id);
|
|
9943
11493
|
}
|
|
9944
11494
|
}
|
|
@@ -9951,13 +11501,14 @@ function createRoom(options, config) {
|
|
|
9951
11501
|
}
|
|
9952
11502
|
};
|
|
9953
11503
|
}
|
|
9954
|
-
function applyOp(op, source
|
|
11504
|
+
function applyOp(op, source) {
|
|
9955
11505
|
if (isIgnoredOp(op)) {
|
|
9956
11506
|
return { modified: false };
|
|
9957
11507
|
}
|
|
9958
11508
|
switch (op.type) {
|
|
9959
11509
|
case OpCode.DELETE_OBJECT_KEY:
|
|
9960
11510
|
case OpCode.UPDATE_OBJECT:
|
|
11511
|
+
case OpCode.UPDATE_TEXT:
|
|
9961
11512
|
case OpCode.DELETE_CRDT: {
|
|
9962
11513
|
const node = context.pool.nodes.get(op.id);
|
|
9963
11514
|
if (node === void 0) {
|
|
@@ -9982,6 +11533,7 @@ function createRoom(options, config) {
|
|
|
9982
11533
|
case OpCode.CREATE_OBJECT:
|
|
9983
11534
|
case OpCode.CREATE_LIST:
|
|
9984
11535
|
case OpCode.CREATE_MAP:
|
|
11536
|
+
case OpCode.CREATE_TEXT:
|
|
9985
11537
|
case OpCode.CREATE_REGISTER: {
|
|
9986
11538
|
if (op.parentId === void 0) {
|
|
9987
11539
|
return { modified: false };
|
|
@@ -9990,7 +11542,7 @@ function createRoom(options, config) {
|
|
|
9990
11542
|
if (parentNode === void 0) {
|
|
9991
11543
|
return { modified: false };
|
|
9992
11544
|
}
|
|
9993
|
-
return parentNode._attachChild(op, source
|
|
11545
|
+
return parentNode._attachChild(op, source);
|
|
9994
11546
|
}
|
|
9995
11547
|
}
|
|
9996
11548
|
}
|
|
@@ -10012,7 +11564,7 @@ function createRoom(options, config) {
|
|
|
10012
11564
|
}
|
|
10013
11565
|
context.myPresence.patch(patch);
|
|
10014
11566
|
if (context.activeBatch) {
|
|
10015
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11567
|
+
if (_optionalChain([options2, 'optionalAccess', _248 => _248.addToHistory])) {
|
|
10016
11568
|
context.activeBatch.reverseOps.pushLeft({
|
|
10017
11569
|
type: "presence",
|
|
10018
11570
|
data: oldValues
|
|
@@ -10021,7 +11573,7 @@ function createRoom(options, config) {
|
|
|
10021
11573
|
context.activeBatch.updates.presence = true;
|
|
10022
11574
|
} else {
|
|
10023
11575
|
flushNowOrSoon();
|
|
10024
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11576
|
+
if (_optionalChain([options2, 'optionalAccess', _249 => _249.addToHistory])) {
|
|
10025
11577
|
addToUndoStack([{ type: "presence", data: oldValues }]);
|
|
10026
11578
|
}
|
|
10027
11579
|
notify({ presence: true });
|
|
@@ -10061,7 +11613,9 @@ function createRoom(options, config) {
|
|
|
10061
11613
|
context.dynamicSessionInfoSig.set({
|
|
10062
11614
|
actor: message.actor,
|
|
10063
11615
|
nonce: message.nonce,
|
|
10064
|
-
|
|
11616
|
+
permissionMatrix: permissionMatrixFromScopes(
|
|
11617
|
+
normalizeRoomPermissions(message.scopes)
|
|
11618
|
+
),
|
|
10065
11619
|
meta: message.meta
|
|
10066
11620
|
});
|
|
10067
11621
|
context.idFactory = makeIdFactory(message.actor);
|
|
@@ -10082,7 +11636,7 @@ function createRoom(options, config) {
|
|
|
10082
11636
|
connectionId,
|
|
10083
11637
|
user.id,
|
|
10084
11638
|
user.info,
|
|
10085
|
-
user.scopes
|
|
11639
|
+
connectionAccessFromScopes(user.scopes)
|
|
10086
11640
|
);
|
|
10087
11641
|
}
|
|
10088
11642
|
return { type: "reset" };
|
|
@@ -10102,7 +11656,7 @@ function createRoom(options, config) {
|
|
|
10102
11656
|
message.actor,
|
|
10103
11657
|
message.id,
|
|
10104
11658
|
message.info,
|
|
10105
|
-
message.scopes
|
|
11659
|
+
connectionAccessFromScopes(message.scopes)
|
|
10106
11660
|
);
|
|
10107
11661
|
context.buffer.messages.push({
|
|
10108
11662
|
type: ClientMsgCode.UPDATE_PRESENCE,
|
|
@@ -10198,11 +11752,11 @@ function createRoom(options, config) {
|
|
|
10198
11752
|
break;
|
|
10199
11753
|
}
|
|
10200
11754
|
case ServerMsgCode.STORAGE_CHUNK:
|
|
10201
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
11755
|
+
_optionalChain([stopwatch, 'optionalAccess', _250 => _250.lap, 'call', _251 => _251()]);
|
|
10202
11756
|
nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
|
|
10203
11757
|
break;
|
|
10204
11758
|
case ServerMsgCode.STORAGE_STREAM_END: {
|
|
10205
|
-
const timing = _optionalChain([stopwatch, 'optionalAccess',
|
|
11759
|
+
const timing = _optionalChain([stopwatch, 'optionalAccess', _252 => _252.stop, 'call', _253 => _253()]);
|
|
10206
11760
|
if (timing) {
|
|
10207
11761
|
const ms = (v) => `${v.toFixed(1)}ms`;
|
|
10208
11762
|
const rest = timing.laps.slice(1);
|
|
@@ -10229,16 +11783,37 @@ function createRoom(options, config) {
|
|
|
10229
11783
|
}
|
|
10230
11784
|
break;
|
|
10231
11785
|
}
|
|
10232
|
-
// Receiving a RejectedOps message
|
|
10233
|
-
//
|
|
10234
|
-
//
|
|
10235
|
-
//
|
|
11786
|
+
// Receiving a RejectedOps message means the server refused some of
|
|
11787
|
+
// our ops, so our optimistic local state is out of sync with the
|
|
11788
|
+
// server. For LiveText ops this is a normal (if rare) situation —
|
|
11789
|
+
// e.g. a client that was offline long enough to fall outside the
|
|
11790
|
+
// server's retained history window — and we can recover: drop the
|
|
11791
|
+
// rejected pending state and re-fetch the authoritative storage
|
|
11792
|
+
// snapshot. For other ops (e.g. permission rejections), rolling back
|
|
11793
|
+
// particular Ops is hard/impossible, so we keep the old behavior of
|
|
11794
|
+
// accepting the out-of-sync reality and surfacing an error.
|
|
10236
11795
|
case ServerMsgCode.REJECT_STORAGE_OP: {
|
|
10237
11796
|
errorWithTitle(
|
|
10238
11797
|
"Storage mutation rejection error",
|
|
10239
11798
|
message.reason
|
|
10240
11799
|
);
|
|
10241
|
-
|
|
11800
|
+
let needsStorageResync = false;
|
|
11801
|
+
for (const opId of message.opIds) {
|
|
11802
|
+
const rejectedOp = context.unacknowledgedOps.get(opId);
|
|
11803
|
+
context.unacknowledgedOps.delete(opId);
|
|
11804
|
+
context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
|
|
11805
|
+
if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
|
|
11806
|
+
const node = context.pool.nodes.get(rejectedOp.id);
|
|
11807
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11808
|
+
node._rejectPendingOp(opId);
|
|
11809
|
+
needsStorageResync = true;
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
if (needsStorageResync) {
|
|
11814
|
+
refreshStorage();
|
|
11815
|
+
flushNowOrSoon();
|
|
11816
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
10242
11817
|
throw new Error(
|
|
10243
11818
|
`Storage mutations rejected by server: ${message.reason}`
|
|
10244
11819
|
);
|
|
@@ -10337,11 +11912,11 @@ function createRoom(options, config) {
|
|
|
10337
11912
|
} else if (pendingFeedsRequests.has(requestId)) {
|
|
10338
11913
|
const pending = pendingFeedsRequests.get(requestId);
|
|
10339
11914
|
pendingFeedsRequests.delete(requestId);
|
|
10340
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11915
|
+
_optionalChain([pending, 'optionalAccess', _254 => _254.reject, 'call', _255 => _255(err)]);
|
|
10341
11916
|
} else if (pendingFeedMessagesRequests.has(requestId)) {
|
|
10342
11917
|
const pending = pendingFeedMessagesRequests.get(requestId);
|
|
10343
11918
|
pendingFeedMessagesRequests.delete(requestId);
|
|
10344
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11919
|
+
_optionalChain([pending, 'optionalAccess', _256 => _256.reject, 'call', _257 => _257(err)]);
|
|
10345
11920
|
}
|
|
10346
11921
|
eventHub.feeds.notify(message);
|
|
10347
11922
|
break;
|
|
@@ -10495,10 +12070,10 @@ function createRoom(options, config) {
|
|
|
10495
12070
|
timeoutId,
|
|
10496
12071
|
kind,
|
|
10497
12072
|
feedId,
|
|
10498
|
-
messageId: _optionalChain([options2, 'optionalAccess',
|
|
10499
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12073
|
+
messageId: _optionalChain([options2, 'optionalAccess', _258 => _258.messageId]),
|
|
12074
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _259 => _259.expectedClientMessageId])
|
|
10500
12075
|
});
|
|
10501
|
-
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess',
|
|
12076
|
+
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _260 => _260.expectedClientMessageId]) === void 0) {
|
|
10502
12077
|
const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
|
|
10503
12078
|
q.push(requestId);
|
|
10504
12079
|
pendingAddMessageFifoByFeed.set(feedId, q);
|
|
@@ -10549,10 +12124,10 @@ function createRoom(options, config) {
|
|
|
10549
12124
|
}
|
|
10550
12125
|
if (!matched) {
|
|
10551
12126
|
const q = pendingAddMessageFifoByFeed.get(message.feedId);
|
|
10552
|
-
const headId = _optionalChain([q, 'optionalAccess',
|
|
12127
|
+
const headId = _optionalChain([q, 'optionalAccess', _261 => _261[0]]);
|
|
10553
12128
|
if (headId !== void 0) {
|
|
10554
12129
|
const pending = pendingFeedMutations.get(headId);
|
|
10555
|
-
if (_optionalChain([pending, 'optionalAccess',
|
|
12130
|
+
if (_optionalChain([pending, 'optionalAccess', _262 => _262.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
|
|
10556
12131
|
settleFeedMutation(headId, "ok");
|
|
10557
12132
|
}
|
|
10558
12133
|
}
|
|
@@ -10588,33 +12163,22 @@ function createRoom(options, config) {
|
|
|
10588
12163
|
const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
|
|
10589
12164
|
createOrUpdateRootFromMessage(nodes);
|
|
10590
12165
|
applyAndSendOfflineOps(unacknowledgedOps2);
|
|
10591
|
-
_optionalChain([_resolveStoragePromise, 'optionalCall',
|
|
12166
|
+
_optionalChain([_resolveStoragePromise, 'optionalCall', _263 => _263()]);
|
|
10592
12167
|
notifyStorageStatus();
|
|
10593
12168
|
eventHub.storageDidLoad.notify();
|
|
10594
12169
|
}
|
|
10595
|
-
|
|
10596
|
-
if (!managedSocket.authValue) return;
|
|
10597
|
-
const nodes = new Map(
|
|
10598
|
-
await httpClient.streamStorage({ roomId })
|
|
10599
|
-
);
|
|
10600
|
-
processInitialStorage(nodes);
|
|
10601
|
-
}
|
|
10602
|
-
function refreshStorage(options2) {
|
|
12170
|
+
function refreshStorage() {
|
|
10603
12171
|
const messages = context.buffer.messages;
|
|
10604
|
-
if (
|
|
10605
|
-
void streamStorage();
|
|
10606
|
-
} else if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
|
|
12172
|
+
if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
|
|
10607
12173
|
messages.push({ type: ClientMsgCode.FETCH_STORAGE });
|
|
10608
12174
|
nodeMapBuffer.take();
|
|
10609
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
10610
|
-
}
|
|
10611
|
-
if (options2.flush) {
|
|
10612
|
-
flushNowOrSoon();
|
|
12175
|
+
_optionalChain([stopwatch, 'optionalAccess', _264 => _264.start, 'call', _265 => _265()]);
|
|
10613
12176
|
}
|
|
10614
12177
|
}
|
|
10615
12178
|
function startLoadingStorage() {
|
|
10616
12179
|
if (_getStorage$ === null) {
|
|
10617
|
-
refreshStorage(
|
|
12180
|
+
refreshStorage();
|
|
12181
|
+
flushNowOrSoon();
|
|
10618
12182
|
_getStorage$ = new Promise((resolve) => {
|
|
10619
12183
|
_resolveStoragePromise = resolve;
|
|
10620
12184
|
});
|
|
@@ -10622,7 +12186,7 @@ function createRoom(options, config) {
|
|
|
10622
12186
|
}
|
|
10623
12187
|
return _getStorage$;
|
|
10624
12188
|
}
|
|
10625
|
-
function
|
|
12189
|
+
function getStorageOrNull() {
|
|
10626
12190
|
const root = context.root;
|
|
10627
12191
|
if (root !== void 0) {
|
|
10628
12192
|
return root;
|
|
@@ -10662,10 +12226,10 @@ function createRoom(options, config) {
|
|
|
10662
12226
|
const message = {
|
|
10663
12227
|
type: ClientMsgCode.FETCH_FEEDS,
|
|
10664
12228
|
requestId,
|
|
10665
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
10666
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
10667
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
10668
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
12229
|
+
cursor: _optionalChain([options2, 'optionalAccess', _266 => _266.cursor]),
|
|
12230
|
+
since: _optionalChain([options2, 'optionalAccess', _267 => _267.since]),
|
|
12231
|
+
limit: _optionalChain([options2, 'optionalAccess', _268 => _268.limit]),
|
|
12232
|
+
metadata: _optionalChain([options2, 'optionalAccess', _269 => _269.metadata])
|
|
10669
12233
|
};
|
|
10670
12234
|
context.buffer.messages.push(message);
|
|
10671
12235
|
flushNowOrSoon();
|
|
@@ -10685,9 +12249,9 @@ function createRoom(options, config) {
|
|
|
10685
12249
|
type: ClientMsgCode.FETCH_FEED_MESSAGES,
|
|
10686
12250
|
requestId,
|
|
10687
12251
|
feedId,
|
|
10688
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
10689
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
10690
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
12252
|
+
cursor: _optionalChain([options2, 'optionalAccess', _270 => _270.cursor]),
|
|
12253
|
+
since: _optionalChain([options2, 'optionalAccess', _271 => _271.since]),
|
|
12254
|
+
limit: _optionalChain([options2, 'optionalAccess', _272 => _272.limit])
|
|
10691
12255
|
};
|
|
10692
12256
|
context.buffer.messages.push(message);
|
|
10693
12257
|
flushNowOrSoon();
|
|
@@ -10706,8 +12270,8 @@ function createRoom(options, config) {
|
|
|
10706
12270
|
type: ClientMsgCode.ADD_FEED,
|
|
10707
12271
|
requestId,
|
|
10708
12272
|
feedId,
|
|
10709
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
10710
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12273
|
+
metadata: _optionalChain([options2, 'optionalAccess', _273 => _273.metadata]),
|
|
12274
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _274 => _274.createdAt])
|
|
10711
12275
|
};
|
|
10712
12276
|
context.buffer.messages.push(message);
|
|
10713
12277
|
flushNowOrSoon();
|
|
@@ -10741,15 +12305,15 @@ function createRoom(options, config) {
|
|
|
10741
12305
|
function addFeedMessage(feedId, data, options2) {
|
|
10742
12306
|
const requestId = nanoid();
|
|
10743
12307
|
const promise = registerFeedMutation(requestId, "add-message", feedId, {
|
|
10744
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12308
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _275 => _275.id])
|
|
10745
12309
|
});
|
|
10746
12310
|
const message = {
|
|
10747
12311
|
type: ClientMsgCode.ADD_FEED_MESSAGE,
|
|
10748
12312
|
requestId,
|
|
10749
12313
|
feedId,
|
|
10750
12314
|
data,
|
|
10751
|
-
id: _optionalChain([options2, 'optionalAccess',
|
|
10752
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12315
|
+
id: _optionalChain([options2, 'optionalAccess', _276 => _276.id]),
|
|
12316
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _277 => _277.createdAt])
|
|
10753
12317
|
};
|
|
10754
12318
|
context.buffer.messages.push(message);
|
|
10755
12319
|
flushNowOrSoon();
|
|
@@ -10766,7 +12330,7 @@ function createRoom(options, config) {
|
|
|
10766
12330
|
feedId,
|
|
10767
12331
|
messageId,
|
|
10768
12332
|
data,
|
|
10769
|
-
updatedAt: _optionalChain([options2, 'optionalAccess',
|
|
12333
|
+
updatedAt: _optionalChain([options2, 'optionalAccess', _278 => _278.updatedAt])
|
|
10770
12334
|
};
|
|
10771
12335
|
context.buffer.messages.push(message);
|
|
10772
12336
|
flushNowOrSoon();
|
|
@@ -10791,14 +12355,19 @@ function createRoom(options, config) {
|
|
|
10791
12355
|
if (context.activeBatch) {
|
|
10792
12356
|
throw new Error("undo is not allowed during a batch");
|
|
10793
12357
|
}
|
|
10794
|
-
const
|
|
10795
|
-
if (
|
|
12358
|
+
const item = context.undoStack.pop();
|
|
12359
|
+
if (item === void 0) {
|
|
10796
12360
|
return;
|
|
10797
12361
|
}
|
|
10798
12362
|
context.pausedHistory = null;
|
|
10799
|
-
const result = applyLocalOps(frames
|
|
12363
|
+
const result = applyLocalOps(item.frames, {
|
|
12364
|
+
origin: "local",
|
|
12365
|
+
via: "history",
|
|
12366
|
+
action: "undo"
|
|
12367
|
+
});
|
|
12368
|
+
context.redoStack.push({ id: item.id, frames: result.reverse });
|
|
12369
|
+
notifyPrivateHistory({ action: "undo", id: item.id });
|
|
10800
12370
|
notify(result.updates);
|
|
10801
|
-
context.redoStack.push(result.reverse);
|
|
10802
12371
|
onHistoryChange();
|
|
10803
12372
|
for (const op of result.opsToEmit) {
|
|
10804
12373
|
context.buffer.storageOperations.push(op);
|
|
@@ -10809,14 +12378,19 @@ function createRoom(options, config) {
|
|
|
10809
12378
|
if (context.activeBatch) {
|
|
10810
12379
|
throw new Error("redo is not allowed during a batch");
|
|
10811
12380
|
}
|
|
10812
|
-
const
|
|
10813
|
-
if (
|
|
12381
|
+
const item = context.redoStack.pop();
|
|
12382
|
+
if (item === void 0) {
|
|
10814
12383
|
return;
|
|
10815
12384
|
}
|
|
10816
12385
|
context.pausedHistory = null;
|
|
10817
|
-
const result = applyLocalOps(frames
|
|
12386
|
+
const result = applyLocalOps(item.frames, {
|
|
12387
|
+
origin: "local",
|
|
12388
|
+
via: "history",
|
|
12389
|
+
action: "redo"
|
|
12390
|
+
});
|
|
12391
|
+
context.undoStack.push({ id: item.id, frames: result.reverse });
|
|
12392
|
+
notifyPrivateHistory({ action: "redo", id: item.id });
|
|
10818
12393
|
notify(result.updates);
|
|
10819
|
-
context.undoStack.push(result.reverse);
|
|
10820
12394
|
onHistoryChange();
|
|
10821
12395
|
for (const op of result.opsToEmit) {
|
|
10822
12396
|
context.buffer.storageOperations.push(op);
|
|
@@ -10826,6 +12400,8 @@ function createRoom(options, config) {
|
|
|
10826
12400
|
function clear() {
|
|
10827
12401
|
context.undoStack.length = 0;
|
|
10828
12402
|
context.redoStack.length = 0;
|
|
12403
|
+
notifyPrivateHistory({ action: "clear" });
|
|
12404
|
+
onHistoryChange();
|
|
10829
12405
|
}
|
|
10830
12406
|
function batch2(callback) {
|
|
10831
12407
|
if (context.activeBatch) {
|
|
@@ -10854,7 +12430,7 @@ function createRoom(options, config) {
|
|
|
10854
12430
|
commitPausedHistoryToUndoStack();
|
|
10855
12431
|
}
|
|
10856
12432
|
if (currentBatch.ops.length > 0) {
|
|
10857
|
-
|
|
12433
|
+
clearRedoStack();
|
|
10858
12434
|
}
|
|
10859
12435
|
if (currentBatch.ops.length > 0) {
|
|
10860
12436
|
dispatchOps(currentBatch.ops);
|
|
@@ -10883,7 +12459,6 @@ function createRoom(options, config) {
|
|
|
10883
12459
|
}
|
|
10884
12460
|
commitPausedHistoryToUndoStack();
|
|
10885
12461
|
}
|
|
10886
|
-
let historyDisabled = 0;
|
|
10887
12462
|
function disableHistory(fn) {
|
|
10888
12463
|
const origUndo = context.undoStack;
|
|
10889
12464
|
const origRedo = context.redoStack;
|
|
@@ -10936,7 +12511,7 @@ function createRoom(options, config) {
|
|
|
10936
12511
|
}
|
|
10937
12512
|
}
|
|
10938
12513
|
function isStorageReady() {
|
|
10939
|
-
return
|
|
12514
|
+
return getStorageOrNull() !== null;
|
|
10940
12515
|
}
|
|
10941
12516
|
async function waitUntilStorageReady() {
|
|
10942
12517
|
while (!isStorageReady()) {
|
|
@@ -10973,8 +12548,8 @@ function createRoom(options, config) {
|
|
|
10973
12548
|
async function getThreads(options2) {
|
|
10974
12549
|
return httpClient.getThreads({
|
|
10975
12550
|
roomId,
|
|
10976
|
-
query: _optionalChain([options2, 'optionalAccess',
|
|
10977
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
12551
|
+
query: _optionalChain([options2, 'optionalAccess', _279 => _279.query]),
|
|
12552
|
+
cursor: _optionalChain([options2, 'optionalAccess', _280 => _280.cursor])
|
|
10978
12553
|
});
|
|
10979
12554
|
}
|
|
10980
12555
|
async function getThread(threadId) {
|
|
@@ -11096,7 +12671,7 @@ function createRoom(options, config) {
|
|
|
11096
12671
|
function getSubscriptionSettings(options2) {
|
|
11097
12672
|
return httpClient.getSubscriptionSettings({
|
|
11098
12673
|
roomId,
|
|
11099
|
-
signal: _optionalChain([options2, 'optionalAccess',
|
|
12674
|
+
signal: _optionalChain([options2, 'optionalAccess', _281 => _281.signal])
|
|
11100
12675
|
});
|
|
11101
12676
|
}
|
|
11102
12677
|
function updateSubscriptionSettings(settings) {
|
|
@@ -11118,24 +12693,39 @@ function createRoom(options, config) {
|
|
|
11118
12693
|
{
|
|
11119
12694
|
[kInternal]: {
|
|
11120
12695
|
get presenceBuffer() {
|
|
11121
|
-
return deepClone(_nullishCoalesce(_optionalChain([context, 'access',
|
|
12696
|
+
return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _282 => _282.buffer, 'access', _283 => _283.presenceUpdates, 'optionalAccess', _284 => _284.data]), () => ( null)));
|
|
11122
12697
|
},
|
|
11123
12698
|
// prettier-ignore
|
|
11124
12699
|
get undoStack() {
|
|
11125
|
-
return
|
|
12700
|
+
return structuredClone(
|
|
12701
|
+
context.undoStack.map((item) => ({
|
|
12702
|
+
id: item.id,
|
|
12703
|
+
frames: item.frames
|
|
12704
|
+
}))
|
|
12705
|
+
);
|
|
12706
|
+
},
|
|
12707
|
+
// prettier-ignore
|
|
12708
|
+
get redoStack() {
|
|
12709
|
+
return structuredClone(
|
|
12710
|
+
context.redoStack.map((item) => ({
|
|
12711
|
+
id: item.id,
|
|
12712
|
+
frames: item.frames
|
|
12713
|
+
}))
|
|
12714
|
+
);
|
|
11126
12715
|
},
|
|
11127
12716
|
// prettier-ignore
|
|
11128
12717
|
get nodeCount() {
|
|
11129
12718
|
return context.pool.nodes.size;
|
|
11130
12719
|
},
|
|
11131
12720
|
// prettier-ignore
|
|
12721
|
+
history: eventHub.privateHistory.observable,
|
|
11132
12722
|
getYjsProvider() {
|
|
11133
12723
|
return context.yjsProvider;
|
|
11134
12724
|
},
|
|
11135
12725
|
setYjsProvider(newProvider) {
|
|
11136
|
-
_optionalChain([context, 'access',
|
|
12726
|
+
_optionalChain([context, 'access', _285 => _285.yjsProvider, 'optionalAccess', _286 => _286.off, 'call', _287 => _287("status", yjsStatusDidChange)]);
|
|
11137
12727
|
context.yjsProvider = newProvider;
|
|
11138
|
-
_optionalChain([newProvider, 'optionalAccess',
|
|
12728
|
+
_optionalChain([newProvider, 'optionalAccess', _288 => _288.on, 'call', _289 => _289("status", yjsStatusDidChange)]);
|
|
11139
12729
|
context.yjsProviderDidChange.notify();
|
|
11140
12730
|
},
|
|
11141
12731
|
yjsProviderDidChange: context.yjsProviderDidChange.observable,
|
|
@@ -11179,7 +12769,9 @@ function createRoom(options, config) {
|
|
|
11179
12769
|
_dump: () => {
|
|
11180
12770
|
const n = context.pool.nodes.size;
|
|
11181
12771
|
return `Room "${roomId}" (${n} node${n === 1 ? "" : "s"}):
|
|
11182
|
-
${dumpPool(
|
|
12772
|
+
${dumpPool(
|
|
12773
|
+
context.pool
|
|
12774
|
+
)}`;
|
|
11183
12775
|
},
|
|
11184
12776
|
destroy: () => {
|
|
11185
12777
|
pendingFeedsRequests.forEach(
|
|
@@ -11193,7 +12785,7 @@ ${dumpPool(context.pool)}`;
|
|
|
11193
12785
|
source.dispose();
|
|
11194
12786
|
}
|
|
11195
12787
|
eventHub.roomWillDestroy.notify();
|
|
11196
|
-
_optionalChain([context, 'access',
|
|
12788
|
+
_optionalChain([context, 'access', _290 => _290.yjsProvider, 'optionalAccess', _291 => _291.off, 'call', _292 => _292("status", yjsStatusDidChange)]);
|
|
11197
12789
|
syncSourceForStorage.destroy();
|
|
11198
12790
|
syncSourceForYjs.destroy();
|
|
11199
12791
|
uninstallBgTabSpy();
|
|
@@ -11226,7 +12818,9 @@ ${dumpPool(context.pool)}`;
|
|
|
11226
12818
|
updateFeedMessage,
|
|
11227
12819
|
deleteFeedMessage,
|
|
11228
12820
|
getStorage,
|
|
11229
|
-
|
|
12821
|
+
getStorageOrNull,
|
|
12822
|
+
getStorageSnapshot: getStorageOrNull,
|
|
12823
|
+
// Deprecated alias, will be removed in the future
|
|
11230
12824
|
getStorageStatus,
|
|
11231
12825
|
isPresenceReady,
|
|
11232
12826
|
isStorageReady,
|
|
@@ -11353,7 +12947,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
|
|
|
11353
12947
|
}
|
|
11354
12948
|
if (isLiveNode(first)) {
|
|
11355
12949
|
const node = first;
|
|
11356
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
12950
|
+
if (_optionalChain([options, 'optionalAccess', _293 => _293.isDeep])) {
|
|
11357
12951
|
const storageCallback = second;
|
|
11358
12952
|
return subscribeToLiveStructureDeeply(node, storageCallback);
|
|
11359
12953
|
} else {
|
|
@@ -11372,7 +12966,11 @@ function isRoomEventName(value) {
|
|
|
11372
12966
|
}
|
|
11373
12967
|
function makeAuthDelegateForRoom(roomId, authManager) {
|
|
11374
12968
|
return async () => {
|
|
11375
|
-
return authManager.getAuthValue({
|
|
12969
|
+
return authManager.getAuthValue({
|
|
12970
|
+
roomId,
|
|
12971
|
+
resource: "room",
|
|
12972
|
+
access: "read"
|
|
12973
|
+
});
|
|
11376
12974
|
};
|
|
11377
12975
|
}
|
|
11378
12976
|
function makeCreateSocketDelegateForRoom(roomId, baseUrl, WebSocketPolyfill) {
|
|
@@ -11421,7 +13019,7 @@ function getBaseUrl(baseUrl) {
|
|
|
11421
13019
|
function isLocalhost(url2) {
|
|
11422
13020
|
try {
|
|
11423
13021
|
return new URL(url2).hostname === "localhost";
|
|
11424
|
-
} catch (
|
|
13022
|
+
} catch (e11) {
|
|
11425
13023
|
return false;
|
|
11426
13024
|
}
|
|
11427
13025
|
}
|
|
@@ -11439,12 +13037,11 @@ function createClient(options) {
|
|
|
11439
13037
|
const authManager = createAuthManager(options, (token) => {
|
|
11440
13038
|
currentUserId.set(() => token.uid);
|
|
11441
13039
|
});
|
|
11442
|
-
const fetchPolyfill = _optionalChain([clientOptions, 'access',
|
|
11443
|
-
_optionalChain([globalThis, 'access',
|
|
13040
|
+
const fetchPolyfill = _optionalChain([clientOptions, 'access', _294 => _294.polyfills, 'optionalAccess', _295 => _295.fetch]) || /* istanbul ignore next */
|
|
13041
|
+
_optionalChain([globalThis, 'access', _296 => _296.fetch, 'optionalAccess', _297 => _297.bind, 'call', _298 => _298(globalThis)]);
|
|
11444
13042
|
const httpClient = createApiClient({
|
|
11445
13043
|
baseUrl,
|
|
11446
13044
|
fetchPolyfill,
|
|
11447
|
-
currentUserId,
|
|
11448
13045
|
authManager
|
|
11449
13046
|
});
|
|
11450
13047
|
const roomsById = /* @__PURE__ */ new Map();
|
|
@@ -11458,11 +13055,12 @@ function createClient(options) {
|
|
|
11458
13055
|
delegates: {
|
|
11459
13056
|
createSocket: makeCreateSocketDelegateForAi(
|
|
11460
13057
|
baseUrl,
|
|
11461
|
-
_optionalChain([clientOptions, 'access',
|
|
13058
|
+
_optionalChain([clientOptions, 'access', _299 => _299.polyfills, 'optionalAccess', _300 => _300.WebSocket])
|
|
11462
13059
|
),
|
|
11463
13060
|
authenticate: async () => {
|
|
11464
13061
|
const resp = await authManager.getAuthValue({
|
|
11465
|
-
|
|
13062
|
+
resource: "personal",
|
|
13063
|
+
access: "write"
|
|
11466
13064
|
});
|
|
11467
13065
|
if (resp.type === "public") {
|
|
11468
13066
|
throw new StopRetrying(
|
|
@@ -11528,14 +13126,13 @@ function createClient(options) {
|
|
|
11528
13126
|
createSocket: makeCreateSocketDelegateForRoom(
|
|
11529
13127
|
roomId,
|
|
11530
13128
|
baseUrl,
|
|
11531
|
-
_optionalChain([clientOptions, 'access',
|
|
13129
|
+
_optionalChain([clientOptions, 'access', _301 => _301.polyfills, 'optionalAccess', _302 => _302.WebSocket])
|
|
11532
13130
|
),
|
|
11533
13131
|
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
11534
13132
|
})),
|
|
11535
13133
|
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
11536
13134
|
baseUrl,
|
|
11537
13135
|
errorEventSource: liveblocksErrorSource,
|
|
11538
|
-
unstable_streamData: !!clientOptions.unstable_streamData,
|
|
11539
13136
|
roomHttpClient: httpClient,
|
|
11540
13137
|
createSyncSource,
|
|
11541
13138
|
badgeLocation: _nullishCoalesce(clientOptions.badgeLocation, () => ( "bottom-right"))
|
|
@@ -11551,7 +13148,7 @@ function createClient(options) {
|
|
|
11551
13148
|
const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
|
|
11552
13149
|
if (shouldConnect) {
|
|
11553
13150
|
if (typeof atob === "undefined") {
|
|
11554
|
-
if (_optionalChain([clientOptions, 'access',
|
|
13151
|
+
if (_optionalChain([clientOptions, 'access', _303 => _303.polyfills, 'optionalAccess', _304 => _304.atob]) === void 0) {
|
|
11555
13152
|
throw new Error(
|
|
11556
13153
|
"You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
|
|
11557
13154
|
);
|
|
@@ -11563,7 +13160,7 @@ function createClient(options) {
|
|
|
11563
13160
|
return leaseRoom(newRoomDetails);
|
|
11564
13161
|
}
|
|
11565
13162
|
function getRoom(roomId) {
|
|
11566
|
-
const room = _optionalChain([roomsById, 'access',
|
|
13163
|
+
const room = _optionalChain([roomsById, 'access', _305 => _305.get, 'call', _306 => _306(roomId), 'optionalAccess', _307 => _307.room]);
|
|
11567
13164
|
return room ? room : null;
|
|
11568
13165
|
}
|
|
11569
13166
|
function logout() {
|
|
@@ -11579,7 +13176,7 @@ function createClient(options) {
|
|
|
11579
13176
|
const batchedResolveUsers = new Batch(
|
|
11580
13177
|
async (batchedUserIds) => {
|
|
11581
13178
|
const userIds = batchedUserIds.flat();
|
|
11582
|
-
const users = await _optionalChain([resolveUsers, 'optionalCall',
|
|
13179
|
+
const users = await _optionalChain([resolveUsers, 'optionalCall', _308 => _308({ userIds })]);
|
|
11583
13180
|
warnOnceIf(
|
|
11584
13181
|
!resolveUsers,
|
|
11585
13182
|
"Set the resolveUsers option in createClient to specify user info."
|
|
@@ -11596,7 +13193,7 @@ function createClient(options) {
|
|
|
11596
13193
|
const batchedResolveRoomsInfo = new Batch(
|
|
11597
13194
|
async (batchedRoomIds) => {
|
|
11598
13195
|
const roomIds = batchedRoomIds.flat();
|
|
11599
|
-
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall',
|
|
13196
|
+
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _309 => _309({ roomIds })]);
|
|
11600
13197
|
warnOnceIf(
|
|
11601
13198
|
!resolveRoomsInfo,
|
|
11602
13199
|
"Set the resolveRoomsInfo option in createClient to specify room info."
|
|
@@ -11613,7 +13210,7 @@ function createClient(options) {
|
|
|
11613
13210
|
const batchedResolveGroupsInfo = new Batch(
|
|
11614
13211
|
async (batchedGroupIds) => {
|
|
11615
13212
|
const groupIds = batchedGroupIds.flat();
|
|
11616
|
-
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall',
|
|
13213
|
+
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _310 => _310({ groupIds })]);
|
|
11617
13214
|
warnOnceIf(
|
|
11618
13215
|
!resolveGroupsInfo,
|
|
11619
13216
|
"Set the resolveGroupsInfo option in createClient to specify group info."
|
|
@@ -11672,7 +13269,7 @@ function createClient(options) {
|
|
|
11672
13269
|
}
|
|
11673
13270
|
};
|
|
11674
13271
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
11675
|
-
_optionalChain([win, 'optionalAccess',
|
|
13272
|
+
_optionalChain([win, 'optionalAccess', _311 => _311.addEventListener, 'call', _312 => _312("beforeunload", maybePreventClose)]);
|
|
11676
13273
|
}
|
|
11677
13274
|
async function getNotificationSettings(options2) {
|
|
11678
13275
|
const plainSettings = await httpClient.getNotificationSettings(options2);
|
|
@@ -11800,7 +13397,7 @@ var commentBodyElementsTypes = {
|
|
|
11800
13397
|
mention: "inline"
|
|
11801
13398
|
};
|
|
11802
13399
|
function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
11803
|
-
if (!body || !_optionalChain([body, 'optionalAccess',
|
|
13400
|
+
if (!body || !_optionalChain([body, 'optionalAccess', _313 => _313.content])) {
|
|
11804
13401
|
return;
|
|
11805
13402
|
}
|
|
11806
13403
|
const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
|
|
@@ -11810,13 +13407,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
|
11810
13407
|
for (const block of body.content) {
|
|
11811
13408
|
if (type === "all" || type === "block") {
|
|
11812
13409
|
if (guard(block)) {
|
|
11813
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13410
|
+
_optionalChain([visitor, 'optionalCall', _314 => _314(block)]);
|
|
11814
13411
|
}
|
|
11815
13412
|
}
|
|
11816
13413
|
if (type === "all" || type === "inline") {
|
|
11817
13414
|
for (const inline of block.children) {
|
|
11818
13415
|
if (guard(inline)) {
|
|
11819
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13416
|
+
_optionalChain([visitor, 'optionalCall', _315 => _315(inline)]);
|
|
11820
13417
|
}
|
|
11821
13418
|
}
|
|
11822
13419
|
}
|
|
@@ -11986,7 +13583,7 @@ var stringifyCommentBodyPlainElements = {
|
|
|
11986
13583
|
text: ({ element }) => element.text,
|
|
11987
13584
|
link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
|
|
11988
13585
|
mention: ({ element, user, group }) => {
|
|
11989
|
-
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13586
|
+
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _316 => _316.name]), () => ( _optionalChain([group, 'optionalAccess', _317 => _317.name]))), () => ( element.id))}`;
|
|
11990
13587
|
}
|
|
11991
13588
|
};
|
|
11992
13589
|
var stringifyCommentBodyHtmlElements = {
|
|
@@ -12016,7 +13613,7 @@ var stringifyCommentBodyHtmlElements = {
|
|
|
12016
13613
|
return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
|
|
12017
13614
|
},
|
|
12018
13615
|
mention: ({ element, user, group }) => {
|
|
12019
|
-
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess',
|
|
13616
|
+
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _318 => _318.name]) ? html`${_optionalChain([user, 'optionalAccess', _319 => _319.name])}` : _optionalChain([group, 'optionalAccess', _320 => _320.name]) ? html`${_optionalChain([group, 'optionalAccess', _321 => _321.name])}` : element.id}</span>`;
|
|
12020
13617
|
}
|
|
12021
13618
|
};
|
|
12022
13619
|
var stringifyCommentBodyMarkdownElements = {
|
|
@@ -12046,20 +13643,20 @@ var stringifyCommentBodyMarkdownElements = {
|
|
|
12046
13643
|
return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
|
|
12047
13644
|
},
|
|
12048
13645
|
mention: ({ element, user, group }) => {
|
|
12049
|
-
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13646
|
+
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _322 => _322.name]), () => ( _optionalChain([group, 'optionalAccess', _323 => _323.name]))), () => ( element.id))}`;
|
|
12050
13647
|
}
|
|
12051
13648
|
};
|
|
12052
13649
|
async function stringifyCommentBody(body, options) {
|
|
12053
|
-
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
12054
|
-
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13650
|
+
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _324 => _324.format]), () => ( "plain"));
|
|
13651
|
+
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _325 => _325.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
|
|
12055
13652
|
const elements = {
|
|
12056
13653
|
...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
|
|
12057
|
-
..._optionalChain([options, 'optionalAccess',
|
|
13654
|
+
..._optionalChain([options, 'optionalAccess', _326 => _326.elements])
|
|
12058
13655
|
};
|
|
12059
13656
|
const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
|
|
12060
13657
|
body,
|
|
12061
|
-
_optionalChain([options, 'optionalAccess',
|
|
12062
|
-
_optionalChain([options, 'optionalAccess',
|
|
13658
|
+
_optionalChain([options, 'optionalAccess', _327 => _327.resolveUsers]),
|
|
13659
|
+
_optionalChain([options, 'optionalAccess', _328 => _328.resolveGroupsInfo])
|
|
12063
13660
|
);
|
|
12064
13661
|
const blocks = body.content.flatMap((block, blockIndex) => {
|
|
12065
13662
|
switch (block.type) {
|
|
@@ -12141,6 +13738,12 @@ function toPlainLson(lson) {
|
|
|
12141
13738
|
liveblocksType: "LiveList",
|
|
12142
13739
|
data: [...lson].map((item) => toPlainLson(item))
|
|
12143
13740
|
};
|
|
13741
|
+
} else if (lson instanceof LiveText) {
|
|
13742
|
+
return {
|
|
13743
|
+
liveblocksType: "LiveText",
|
|
13744
|
+
data: lson.toJSON(),
|
|
13745
|
+
version: lson.version
|
|
13746
|
+
};
|
|
12144
13747
|
} else {
|
|
12145
13748
|
return lson;
|
|
12146
13749
|
}
|
|
@@ -12194,9 +13797,9 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12194
13797
|
const startTime = performance.now();
|
|
12195
13798
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
12196
13799
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12197
|
-
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13800
|
+
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _329 => _329.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
|
|
12198
13801
|
const context = {
|
|
12199
|
-
inForeground: _optionalChain([doc, 'optionalAccess',
|
|
13802
|
+
inForeground: _optionalChain([doc, 'optionalAccess', _330 => _330.visibilityState]) !== "hidden",
|
|
12200
13803
|
lastSuccessfulPollAt: startTime,
|
|
12201
13804
|
count: 0,
|
|
12202
13805
|
backoff: 0
|
|
@@ -12277,11 +13880,11 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12277
13880
|
pollNowIfStale();
|
|
12278
13881
|
}
|
|
12279
13882
|
function onVisibilityChange() {
|
|
12280
|
-
setInForeground(_optionalChain([doc, 'optionalAccess',
|
|
13883
|
+
setInForeground(_optionalChain([doc, 'optionalAccess', _331 => _331.visibilityState]) !== "hidden");
|
|
12281
13884
|
}
|
|
12282
|
-
_optionalChain([doc, 'optionalAccess',
|
|
12283
|
-
_optionalChain([win, 'optionalAccess',
|
|
12284
|
-
_optionalChain([win, 'optionalAccess',
|
|
13885
|
+
_optionalChain([doc, 'optionalAccess', _332 => _332.addEventListener, 'call', _333 => _333("visibilitychange", onVisibilityChange)]);
|
|
13886
|
+
_optionalChain([win, 'optionalAccess', _334 => _334.addEventListener, 'call', _335 => _335("online", onVisibilityChange)]);
|
|
13887
|
+
_optionalChain([win, 'optionalAccess', _336 => _336.addEventListener, 'call', _337 => _337("focus", pollNowIfStale)]);
|
|
12285
13888
|
fsm.start();
|
|
12286
13889
|
return {
|
|
12287
13890
|
inc,
|
|
@@ -12418,5 +14021,18 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
12418
14021
|
|
|
12419
14022
|
|
|
12420
14023
|
|
|
12421
|
-
|
|
14024
|
+
|
|
14025
|
+
|
|
14026
|
+
|
|
14027
|
+
|
|
14028
|
+
|
|
14029
|
+
|
|
14030
|
+
|
|
14031
|
+
|
|
14032
|
+
|
|
14033
|
+
|
|
14034
|
+
|
|
14035
|
+
|
|
14036
|
+
|
|
14037
|
+
exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveText = LiveText; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.applyLiveTextOperations = applyLiveTextOperations; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.deepLiveify = deepLiveify; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionAccess = hasPermissionAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isTextStorageNode = isTextStorageNode; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.kStorageUpdateSource = kStorageUpdateSource; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.mergeRoomPermissionScopes = mergeRoomPermissionScopes; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccesses = normalizeRoomAccesses; exports.normalizeRoomPermissions = normalizeRoomPermissions; exports.normalizeUpdateRoomAccesses = normalizeUpdateRoomAccesses; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionMatrixFromScopes = permissionMatrixFromScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.transformTextOperations = transformTextOperations; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.validatePermissionsSet = validatePermissionsSet; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
|
|
12422
14038
|
//# sourceMappingURL=index.cjs.map
|