@liveblocks/core 1.8.2 → 1.9.0-example1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "1.8.2";
9
+ var PKG_VERSION = "1.9.0-example1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -1744,1405 +1744,1765 @@ function errorIf(condition, message) {
1744
1744
  }
1745
1745
  }
1746
1746
 
1747
- // src/comments/index.ts
1748
- function getAuthBearerHeaderFromAuthValue(authValue) {
1749
- if (authValue.type === "public") {
1750
- return authValue.publicApiKey;
1751
- } else {
1752
- return authValue.token.raw;
1753
- }
1747
+ // src/comments/comment-body.ts
1748
+ function isCommentBodyParagraph(element) {
1749
+ return "type" in element && element.type === "mention";
1754
1750
  }
1755
- var CommentsApiError = class extends Error {
1756
- constructor(message, status, details) {
1757
- super(message);
1758
- this.message = message;
1759
- this.status = status;
1760
- this.details = details;
1761
- }
1751
+ function isCommentBodyText(element) {
1752
+ return "text" in element && typeof element.text === "string";
1753
+ }
1754
+ function isCommentBodyMention(element) {
1755
+ return "type" in element && element.type === "mention";
1756
+ }
1757
+ function isCommentBodyLink(element) {
1758
+ return "type" in element && element.type === "link";
1759
+ }
1760
+ var commentBodyElementsGuards = {
1761
+ paragraph: isCommentBodyParagraph,
1762
+ text: isCommentBodyText,
1763
+ link: isCommentBodyLink,
1764
+ mention: isCommentBodyMention
1762
1765
  };
1763
- function createCommentsApi(roomId, getAuthValue, config) {
1764
- async function fetchJson(endpoint, options) {
1765
- const response = await fetchApi(roomId, endpoint, options);
1766
- if (!response.ok) {
1767
- if (response.status >= 400 && response.status < 600) {
1768
- let error3;
1769
- try {
1770
- const errorBody = await response.json();
1771
- error3 = new CommentsApiError(
1772
- errorBody.message,
1773
- response.status,
1774
- errorBody
1775
- );
1776
- } catch {
1777
- error3 = new CommentsApiError(response.statusText, response.status);
1778
- }
1779
- throw error3;
1766
+ var commentBodyElementsTypes = {
1767
+ paragraph: "block",
1768
+ text: "inline",
1769
+ link: "inline",
1770
+ mention: "inline"
1771
+ };
1772
+ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
1773
+ if (!body || !body?.content) {
1774
+ return;
1775
+ }
1776
+ const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
1777
+ const type = element ? commentBodyElementsTypes[element] : "all";
1778
+ const guard = element ? commentBodyElementsGuards[element] : () => true;
1779
+ const visitor = typeof elementOrVisitor === "function" ? elementOrVisitor : possiblyVisitor;
1780
+ for (const block of body.content) {
1781
+ if (type === "all" || type === "block") {
1782
+ if (guard(block)) {
1783
+ visitor?.(block);
1780
1784
  }
1781
1785
  }
1782
- let body;
1783
- try {
1784
- body = await response.json();
1785
- } catch {
1786
- body = {};
1787
- }
1788
- return body;
1789
- }
1790
- async function fetchApi(roomId2, endpoint, options) {
1791
- const authValue = await getAuthValue();
1792
- const url = new URL(
1793
- `/v2/c/rooms/${encodeURIComponent(roomId2)}${endpoint}`,
1794
- config.baseUrl
1795
- ).toString();
1796
- return await fetch(url, {
1797
- ...options,
1798
- headers: {
1799
- ...options?.headers,
1800
- Authorization: `Bearer ${getAuthBearerHeaderFromAuthValue(authValue)}`
1786
+ if (type === "all" || type === "inline") {
1787
+ for (const inline of block.children) {
1788
+ if (guard(inline)) {
1789
+ visitor?.(inline);
1790
+ }
1801
1791
  }
1802
- });
1803
- }
1804
- async function getThreads() {
1805
- const response = await fetchApi(roomId, "/threads");
1806
- if (response.ok) {
1807
- const json = await response.json();
1808
- return json.data;
1809
- } else if (response.status === 404) {
1810
- return [];
1811
- } else {
1812
- throw new Error("There was an error while getting threads.");
1813
1792
  }
1814
1793
  }
1815
- function createThread({
1816
- metadata,
1794
+ }
1795
+ function getMentionedIdsFromCommentBody(body) {
1796
+ const mentionedIds = /* @__PURE__ */ new Set();
1797
+ traverseCommentBody(
1817
1798
  body,
1818
- commentId,
1819
- threadId
1820
- }) {
1821
- return fetchJson("/threads", {
1822
- method: "POST",
1823
- headers: {
1824
- "Content-Type": "application/json"
1825
- },
1826
- body: JSON.stringify({
1827
- id: threadId,
1828
- comment: {
1829
- id: commentId,
1830
- body
1831
- },
1832
- metadata
1833
- })
1834
- });
1835
- }
1836
- function editThreadMetadata({
1837
- metadata,
1838
- threadId
1839
- }) {
1840
- return fetchJson(
1841
- `/threads/${encodeURIComponent(threadId)}/metadata`,
1842
- {
1843
- method: "POST",
1844
- headers: {
1845
- "Content-Type": "application/json"
1846
- },
1847
- body: JSON.stringify(metadata)
1848
- }
1849
- );
1799
+ "mention",
1800
+ (mention) => mentionedIds.add(mention.id)
1801
+ );
1802
+ return Array.from(mentionedIds);
1803
+ }
1804
+ async function resolveUsersInCommentBody(body, resolveUsers) {
1805
+ const resolvedUsers = /* @__PURE__ */ new Map();
1806
+ if (!resolveUsers) {
1807
+ return resolvedUsers;
1850
1808
  }
1851
- function createComment({
1852
- threadId,
1853
- commentId,
1854
- body
1855
- }) {
1856
- return fetchJson(
1857
- `/threads/${encodeURIComponent(threadId)}/comments`,
1858
- {
1859
- method: "POST",
1860
- headers: {
1861
- "Content-Type": "application/json"
1862
- },
1863
- body: JSON.stringify({
1864
- id: commentId,
1865
- body
1866
- })
1867
- }
1868
- );
1809
+ const userIds = getMentionedIdsFromCommentBody(body);
1810
+ const users = await resolveUsers({
1811
+ userIds
1812
+ });
1813
+ for (const [index, userId] of userIds.entries()) {
1814
+ const user = users?.[index];
1815
+ if (user) {
1816
+ resolvedUsers.set(userId, user);
1817
+ }
1869
1818
  }
1870
- function editComment({
1871
- threadId,
1872
- commentId,
1873
- body
1874
- }) {
1875
- return fetchJson(
1876
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
1877
- commentId
1878
- )}`,
1879
- {
1880
- method: "POST",
1881
- headers: {
1882
- "Content-Type": "application/json"
1883
- },
1884
- body: JSON.stringify({
1885
- body
1886
- })
1887
- }
1888
- );
1819
+ return resolvedUsers;
1820
+ }
1821
+ var htmlEscapables = {
1822
+ "&": "&amp;",
1823
+ "<": "&lt;",
1824
+ ">": "&gt;",
1825
+ '"': "&quot;",
1826
+ "'": "&#39;"
1827
+ };
1828
+ var htmlEscapablesRegex = new RegExp(
1829
+ Object.keys(htmlEscapables).map((entity) => `\\${entity}`).join("|"),
1830
+ "g"
1831
+ );
1832
+ function htmlSafe(value) {
1833
+ return new HtmlSafeString([String(value)], []);
1834
+ }
1835
+ function joinHtml(strings) {
1836
+ if (strings.length <= 0) {
1837
+ return new HtmlSafeString([""], []);
1889
1838
  }
1890
- async function deleteComment({
1891
- threadId,
1892
- commentId
1893
- }) {
1894
- await fetchJson(
1895
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
1896
- commentId
1897
- )}`,
1898
- {
1899
- method: "DELETE"
1900
- }
1901
- );
1839
+ return new HtmlSafeString(
1840
+ ["", ...Array(strings.length - 1).fill(""), ""],
1841
+ strings
1842
+ );
1843
+ }
1844
+ function escapeHtml(value) {
1845
+ if (value instanceof HtmlSafeString) {
1846
+ return value.toString();
1902
1847
  }
1903
- function addReaction({
1904
- threadId,
1905
- commentId,
1906
- emoji
1907
- }) {
1908
- return fetchJson(
1909
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
1910
- commentId
1911
- )}/reactions`,
1912
- {
1913
- method: "POST",
1914
- headers: {
1915
- "Content-Type": "application/json"
1916
- },
1917
- body: JSON.stringify({ emoji })
1918
- }
1919
- );
1848
+ if (Array.isArray(value)) {
1849
+ return joinHtml(value).toString();
1920
1850
  }
1921
- function removeReaction({
1922
- threadId,
1923
- commentId,
1924
- emoji
1925
- }) {
1926
- return fetchJson(
1927
- `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
1928
- commentId
1929
- )}/reactions/${encodeURIComponent(emoji)}`,
1930
- {
1931
- method: "DELETE"
1932
- }
1933
- );
1851
+ return String(value).replace(
1852
+ htmlEscapablesRegex,
1853
+ (character) => htmlEscapables[character]
1854
+ );
1855
+ }
1856
+ var HtmlSafeString = class {
1857
+ constructor(strings, values) {
1858
+ this._strings = strings;
1859
+ this._values = values;
1934
1860
  }
1935
- return {
1936
- getThreads,
1937
- createThread,
1938
- editThreadMetadata,
1939
- createComment,
1940
- editComment,
1941
- deleteComment,
1942
- addReaction,
1943
- removeReaction
1944
- };
1945
- }
1946
-
1947
- // src/lib/position.ts
1948
- var MIN_CODE = 32;
1949
- var MAX_CODE = 126;
1950
- var NUM_DIGITS = MAX_CODE - MIN_CODE + 1;
1951
- var ZERO = nthDigit(0);
1952
- var ONE = nthDigit(1);
1953
- var ZERO_NINE = ZERO + nthDigit(-1);
1954
- function nthDigit(n) {
1955
- const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n);
1956
- if (code < MIN_CODE || code > MAX_CODE) {
1957
- throw new Error(`Invalid n value: ${n}`);
1861
+ toString() {
1862
+ return this._strings.reduce((result, str, i) => {
1863
+ return result + escapeHtml(nn(this._values[i - 1])) + str;
1864
+ });
1958
1865
  }
1959
- return String.fromCharCode(code);
1866
+ };
1867
+ function html(strings, ...values) {
1868
+ return new HtmlSafeString(strings, values);
1960
1869
  }
1961
- function makePosition(x, y) {
1962
- if (x !== void 0 && y !== void 0) {
1963
- return between(x, y);
1964
- } else if (x !== void 0) {
1965
- return after(x);
1966
- } else if (y !== void 0) {
1967
- return before(y);
1968
- } else {
1969
- return ONE;
1870
+ var markdownEscapables = {
1871
+ _: "\\_",
1872
+ "*": "\\*",
1873
+ "#": "\\#",
1874
+ "`": "\\`",
1875
+ "~": "\\~",
1876
+ "!": "\\!",
1877
+ "|": "\\|",
1878
+ "(": "\\(",
1879
+ ")": "\\)",
1880
+ "{": "\\{",
1881
+ "}": "\\}",
1882
+ "[": "\\[",
1883
+ "]": "\\]"
1884
+ };
1885
+ var markdownEscapablesRegex = new RegExp(
1886
+ Object.keys(markdownEscapables).map((entity) => `\\${entity}`).join("|"),
1887
+ "g"
1888
+ );
1889
+ function joinMarkdown(strings) {
1890
+ if (strings.length <= 0) {
1891
+ return new MarkdownSafeString([""], []);
1970
1892
  }
1893
+ return new MarkdownSafeString(
1894
+ ["", ...Array(strings.length - 1).fill(""), ""],
1895
+ strings
1896
+ );
1971
1897
  }
1972
- function before(pos) {
1973
- const lastIndex = pos.length - 1;
1974
- for (let i = 0; i <= lastIndex; i++) {
1975
- const code = pos.charCodeAt(i);
1976
- if (code <= MIN_CODE) {
1977
- continue;
1978
- }
1979
- if (i === lastIndex) {
1980
- if (code === MIN_CODE + 1) {
1981
- return pos.substring(0, i) + ZERO_NINE;
1982
- } else {
1983
- return pos.substring(0, i) + String.fromCharCode(code - 1);
1984
- }
1985
- } else {
1986
- return pos.substring(0, i + 1);
1987
- }
1898
+ function escapeMarkdown(value) {
1899
+ if (value instanceof MarkdownSafeString) {
1900
+ return value.toString();
1988
1901
  }
1989
- return ONE;
1990
- }
1991
- function after(pos) {
1992
- for (let i = 0; i <= pos.length - 1; i++) {
1993
- const code = pos.charCodeAt(i);
1994
- if (code >= MAX_CODE) {
1995
- continue;
1996
- }
1997
- return pos.substring(0, i) + String.fromCharCode(code + 1);
1902
+ if (Array.isArray(value)) {
1903
+ return joinMarkdown(value).toString();
1998
1904
  }
1999
- return pos + ONE;
1905
+ return String(value).replace(
1906
+ markdownEscapablesRegex,
1907
+ (character) => markdownEscapables[character]
1908
+ );
2000
1909
  }
2001
- function between(lo, hi) {
2002
- if (lo < hi) {
2003
- return _between(lo, hi);
2004
- } else if (lo > hi) {
2005
- return _between(hi, lo);
2006
- } else {
2007
- throw new Error("Cannot compute value between two equal positions");
1910
+ var MarkdownSafeString = class {
1911
+ constructor(strings, values) {
1912
+ this._strings = strings;
1913
+ this._values = values;
2008
1914
  }
2009
- }
2010
- function _between(lo, hi) {
2011
- let index = 0;
2012
- const loLen = lo.length;
2013
- const hiLen = hi.length;
2014
- while (true) {
2015
- const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE;
2016
- const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE;
2017
- if (loCode === hiCode) {
2018
- index++;
2019
- continue;
2020
- }
2021
- if (hiCode - loCode === 1) {
2022
- const size = index + 1;
2023
- let prefix = lo.substring(0, size);
2024
- if (prefix.length < size) {
2025
- prefix += ZERO.repeat(size - prefix.length);
2026
- }
2027
- const suffix = lo.substring(size);
2028
- const nines = "";
2029
- return prefix + _between(suffix, nines);
2030
- } else {
2031
- return takeN(lo, index) + String.fromCharCode(hiCode + loCode >> 1);
2032
- }
1915
+ toString() {
1916
+ return this._strings.reduce((result, str, i) => {
1917
+ return result + escapeMarkdown(nn(this._values[i - 1])) + str;
1918
+ });
2033
1919
  }
1920
+ };
1921
+ function markdown(strings, ...values) {
1922
+ return new MarkdownSafeString(strings, values);
2034
1923
  }
2035
- function takeN(pos, n) {
2036
- return n < pos.length ? pos.substring(0, n) : pos + ZERO.repeat(n - pos.length);
2037
- }
2038
- var MIN_NON_ZERO_CODE = MIN_CODE + 1;
2039
- function isPos(str) {
2040
- if (str === "") {
2041
- return false;
1924
+ function toAbsoluteUrl(url) {
1925
+ if (url.startsWith("http://") || url.startsWith("https://")) {
1926
+ return url;
1927
+ } else if (url.startsWith("www.")) {
1928
+ return "https://" + url;
2042
1929
  }
2043
- const lastIdx = str.length - 1;
2044
- const last = str.charCodeAt(lastIdx);
2045
- if (last < MIN_NON_ZERO_CODE || last > MAX_CODE) {
2046
- return false;
1930
+ return;
1931
+ }
1932
+ var stringifyCommentBodyPlainElements = {
1933
+ paragraph: ({ children }) => children,
1934
+ text: ({ element }) => element.text,
1935
+ link: ({ element }) => element.url,
1936
+ mention: ({ element, user }) => {
1937
+ return `@${user?.name ?? element.id}`;
2047
1938
  }
2048
- for (let i = 0; i < lastIdx; i++) {
2049
- const code = str.charCodeAt(i);
2050
- if (code < MIN_CODE || code > MAX_CODE) {
2051
- return false;
1939
+ };
1940
+ var stringifyCommentBodyHtmlElements = {
1941
+ paragraph: ({ children }) => {
1942
+ return children ? html`<p>${htmlSafe(children)}</p>` : children;
1943
+ },
1944
+ text: ({ element }) => {
1945
+ let children = element.text;
1946
+ if (!children) {
1947
+ return children;
2052
1948
  }
1949
+ if (element.bold) {
1950
+ children = html`<strong>${children}</strong>`;
1951
+ }
1952
+ if (element.italic) {
1953
+ children = html`<em>${children}</em>`;
1954
+ }
1955
+ if (element.strikethrough) {
1956
+ children = html`<s>${children}</s>`;
1957
+ }
1958
+ if (element.code) {
1959
+ children = html`<code>${children}</code>`;
1960
+ }
1961
+ return children;
1962
+ },
1963
+ link: ({ element, href }) => {
1964
+ return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.url}</a>`;
1965
+ },
1966
+ mention: ({ element, user }) => {
1967
+ return html`<span data-mention>@${user?.name ?? element.id}</span>`;
2053
1968
  }
2054
- return true;
2055
- }
2056
- function convertToPos(str) {
2057
- const codes = [];
2058
- for (let i = 0; i < str.length; i++) {
2059
- const code = str.charCodeAt(i);
2060
- codes.push(code < MIN_CODE ? MIN_CODE : code > MAX_CODE ? MAX_CODE : code);
2061
- }
2062
- while (codes.length > 0 && codes[codes.length - 1] === MIN_CODE) {
2063
- codes.length--;
1969
+ };
1970
+ var stringifyCommentBodyMarkdownElements = {
1971
+ paragraph: ({ children }) => {
1972
+ return children;
1973
+ },
1974
+ text: ({ element }) => {
1975
+ let children = element.text;
1976
+ if (!children) {
1977
+ return children;
1978
+ }
1979
+ if (element.bold) {
1980
+ children = markdown`**${children}**`;
1981
+ }
1982
+ if (element.italic) {
1983
+ children = markdown`_${children}_`;
1984
+ }
1985
+ if (element.strikethrough) {
1986
+ children = markdown`~~${children}~~`;
1987
+ }
1988
+ if (element.code) {
1989
+ children = markdown`\`${children}\``;
1990
+ }
1991
+ return children;
1992
+ },
1993
+ link: ({ element, href }) => {
1994
+ return markdown`[${element.url}](${href})`;
1995
+ },
1996
+ mention: ({ element, user }) => {
1997
+ return markdown`@${user?.name ?? element.id}`;
2064
1998
  }
2065
- return codes.length > 0 ? String.fromCharCode(...codes) : (
2066
- // Edge case: the str was a 0-only string, which is invalid. Default back to .1
2067
- ONE
1999
+ };
2000
+ async function stringifyCommentBody(body, options) {
2001
+ const format = options?.format ?? "plain";
2002
+ const separator = options?.separator ?? (format === "markdown" ? "\n\n" : "\n");
2003
+ const elements = {
2004
+ ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
2005
+ ...options?.elements
2006
+ };
2007
+ const resolvedUsers = await resolveUsersInCommentBody(
2008
+ body,
2009
+ options?.resolveUsers
2068
2010
  );
2011
+ const blocks = body.content.flatMap((block, blockIndex) => {
2012
+ switch (block.type) {
2013
+ case "paragraph": {
2014
+ const inlines = block.children.flatMap((inline, inlineIndex) => {
2015
+ if (isCommentBodyMention(inline)) {
2016
+ return inline.id ? [
2017
+ elements.mention(
2018
+ {
2019
+ element: inline,
2020
+ user: resolvedUsers.get(inline.id)
2021
+ },
2022
+ inlineIndex
2023
+ )
2024
+ ] : [];
2025
+ }
2026
+ if (isCommentBodyLink(inline)) {
2027
+ return [
2028
+ elements.link(
2029
+ {
2030
+ element: inline,
2031
+ href: toAbsoluteUrl(inline.url) ?? inline.url
2032
+ },
2033
+ inlineIndex
2034
+ )
2035
+ ];
2036
+ }
2037
+ if (isCommentBodyText(inline)) {
2038
+ return [elements.text({ element: inline }, inlineIndex)];
2039
+ }
2040
+ return [];
2041
+ });
2042
+ return [
2043
+ elements.paragraph(
2044
+ { element: block, children: inlines.join("") },
2045
+ blockIndex
2046
+ )
2047
+ ];
2048
+ }
2049
+ default:
2050
+ return [];
2051
+ }
2052
+ });
2053
+ return blocks.join(separator);
2069
2054
  }
2070
- function asPos(str) {
2071
- return isPos(str) ? str : convertToPos(str);
2072
- }
2073
-
2074
- // src/protocol/Op.ts
2075
- var OpCode = /* @__PURE__ */ ((OpCode2) => {
2076
- OpCode2[OpCode2["INIT"] = 0] = "INIT";
2077
- OpCode2[OpCode2["SET_PARENT_KEY"] = 1] = "SET_PARENT_KEY";
2078
- OpCode2[OpCode2["CREATE_LIST"] = 2] = "CREATE_LIST";
2079
- OpCode2[OpCode2["UPDATE_OBJECT"] = 3] = "UPDATE_OBJECT";
2080
- OpCode2[OpCode2["CREATE_OBJECT"] = 4] = "CREATE_OBJECT";
2081
- OpCode2[OpCode2["DELETE_CRDT"] = 5] = "DELETE_CRDT";
2082
- OpCode2[OpCode2["DELETE_OBJECT_KEY"] = 6] = "DELETE_OBJECT_KEY";
2083
- OpCode2[OpCode2["CREATE_MAP"] = 7] = "CREATE_MAP";
2084
- OpCode2[OpCode2["CREATE_REGISTER"] = 8] = "CREATE_REGISTER";
2085
- return OpCode2;
2086
- })(OpCode || {});
2087
- function isAckOp(op) {
2088
- return op.type === 5 /* DELETE_CRDT */ && op.id === "ACK";
2089
- }
2090
-
2091
- // src/crdts/AbstractCrdt.ts
2092
- function crdtAsLiveNode(value) {
2093
- return value;
2055
+ function convertToCommentData(data) {
2056
+ const editedAt = data.editedAt ? new Date(data.editedAt) : void 0;
2057
+ const createdAt = new Date(data.createdAt);
2058
+ const reactions = data.reactions.map((reaction) => ({
2059
+ ...reaction,
2060
+ createdAt: new Date(reaction.createdAt)
2061
+ }));
2062
+ if (data.body) {
2063
+ return {
2064
+ ...data,
2065
+ reactions,
2066
+ createdAt,
2067
+ editedAt
2068
+ };
2069
+ } else {
2070
+ const deletedAt = new Date(data.deletedAt);
2071
+ return {
2072
+ ...data,
2073
+ reactions,
2074
+ createdAt,
2075
+ editedAt,
2076
+ deletedAt
2077
+ };
2078
+ }
2094
2079
  }
2095
- function HasParent(node, key, pos = asPos(key)) {
2096
- return Object.freeze({ type: "HasParent", node, key, pos });
2080
+ function convertToThreadData(data) {
2081
+ const updatedAt = data.updatedAt ? new Date(data.updatedAt) : void 0;
2082
+ const createdAt = new Date(data.createdAt);
2083
+ const comments = data.comments.map(
2084
+ (comment) => convertToCommentData(comment)
2085
+ );
2086
+ return {
2087
+ ...data,
2088
+ createdAt,
2089
+ updatedAt,
2090
+ comments
2091
+ };
2097
2092
  }
2098
- var NoParent = Object.freeze({ type: "NoParent" });
2099
- function Orphaned(oldKey, oldPos = asPos(oldKey)) {
2100
- return Object.freeze({ type: "Orphaned", oldKey, oldPos });
2093
+ function convertToCommentUserReaction(data) {
2094
+ return {
2095
+ ...data,
2096
+ createdAt: new Date(data.createdAt)
2097
+ };
2101
2098
  }
2102
- var AbstractCrdt = class {
2103
- constructor() {
2104
- /** @internal */
2105
- this._parent = NoParent;
2106
- }
2107
- /** @internal */
2108
- _getParentKeyOrThrow() {
2109
- switch (this.parent.type) {
2110
- case "HasParent":
2111
- return this.parent.key;
2112
- case "NoParent":
2113
- throw new Error("Parent key is missing");
2114
- case "Orphaned":
2115
- return this.parent.oldKey;
2116
- default:
2117
- return assertNever(this.parent, "Unknown state");
2118
- }
2119
- }
2120
- /** @internal */
2121
- get _parentPos() {
2122
- switch (this.parent.type) {
2123
- case "HasParent":
2124
- return this.parent.pos;
2125
- case "NoParent":
2126
- throw new Error("Parent key is missing");
2127
- case "Orphaned":
2128
- return this.parent.oldPos;
2129
- default:
2130
- return assertNever(this.parent, "Unknown state");
2131
- }
2132
- }
2133
- /** @internal */
2134
- get _pool() {
2135
- return this.__pool;
2136
- }
2137
- get roomId() {
2138
- return this.__pool ? this.__pool.roomId : null;
2139
- }
2140
- /** @internal */
2141
- get _id() {
2142
- return this.__id;
2143
- }
2144
- /** @internal */
2145
- get parent() {
2146
- return this._parent;
2099
+
2100
+ // src/comments/index.ts
2101
+ function getAuthBearerHeaderFromAuthValue(authValue) {
2102
+ if (authValue.type === "public") {
2103
+ return authValue.publicApiKey;
2104
+ } else {
2105
+ return authValue.token.raw;
2147
2106
  }
2148
- /** @internal */
2149
- get _parentKey() {
2150
- switch (this.parent.type) {
2151
- case "HasParent":
2152
- return this.parent.key;
2153
- case "NoParent":
2154
- return null;
2155
- case "Orphaned":
2156
- return this.parent.oldKey;
2157
- default:
2158
- return assertNever(this.parent, "Unknown state");
2159
- }
2107
+ }
2108
+ var CommentsApiError = class extends Error {
2109
+ constructor(message, status, details) {
2110
+ super(message);
2111
+ this.message = message;
2112
+ this.status = status;
2113
+ this.details = details;
2160
2114
  }
2161
- /** @internal */
2162
- _apply(op, _isLocal) {
2163
- switch (op.type) {
2164
- case 5 /* DELETE_CRDT */: {
2165
- if (this.parent.type === "HasParent") {
2166
- return this.parent.node._detachChild(crdtAsLiveNode(this));
2115
+ };
2116
+ function createCommentsApi(roomId, getAuthValue, config) {
2117
+ async function fetchJson(endpoint, options) {
2118
+ const response = await fetchApi(roomId, endpoint, options);
2119
+ if (!response.ok) {
2120
+ if (response.status >= 400 && response.status < 600) {
2121
+ let error3;
2122
+ try {
2123
+ const errorBody = await response.json();
2124
+ error3 = new CommentsApiError(
2125
+ errorBody.message,
2126
+ response.status,
2127
+ errorBody
2128
+ );
2129
+ } catch {
2130
+ error3 = new CommentsApiError(response.statusText, response.status);
2167
2131
  }
2168
- return { modified: false };
2132
+ throw error3;
2169
2133
  }
2170
2134
  }
2171
- return { modified: false };
2135
+ let body;
2136
+ try {
2137
+ body = await response.json();
2138
+ } catch {
2139
+ body = {};
2140
+ }
2141
+ return body;
2172
2142
  }
2173
- /** @internal */
2174
- _setParentLink(newParentNode, newParentKey) {
2175
- switch (this.parent.type) {
2176
- case "HasParent":
2177
- if (this.parent.node !== newParentNode) {
2178
- throw new Error("Cannot set parent: node already has a parent");
2179
- } else {
2180
- this._parent = HasParent(newParentNode, newParentKey);
2181
- return;
2182
- }
2183
- case "Orphaned":
2184
- case "NoParent": {
2185
- this._parent = HasParent(newParentNode, newParentKey);
2186
- return;
2143
+ async function fetchApi(roomId2, endpoint, options) {
2144
+ const authValue = await getAuthValue();
2145
+ const url = new URL(
2146
+ `/v2/c/rooms/${encodeURIComponent(roomId2)}${endpoint}`,
2147
+ config.baseUrl
2148
+ ).toString();
2149
+ return await fetch(url, {
2150
+ ...options,
2151
+ headers: {
2152
+ ...options?.headers,
2153
+ Authorization: `Bearer ${getAuthBearerHeaderFromAuthValue(authValue)}`
2187
2154
  }
2188
- default:
2189
- return assertNever(this.parent, "Unknown state");
2190
- }
2155
+ });
2191
2156
  }
2192
- /** @internal */
2193
- _attach(id, pool) {
2194
- if (this.__id || this.__pool) {
2195
- throw new Error("Cannot attach node: already attached");
2157
+ async function getThreads() {
2158
+ const response = await fetchApi(roomId, "/threads");
2159
+ if (response.ok) {
2160
+ const json = await response.json();
2161
+ return json.data.map((thread) => convertToThreadData(thread));
2162
+ } else if (response.status === 404) {
2163
+ return [];
2164
+ } else {
2165
+ throw new Error("There was an error while getting threads.");
2196
2166
  }
2197
- pool.addNode(id, crdtAsLiveNode(this));
2198
- this.__id = id;
2199
- this.__pool = pool;
2200
2167
  }
2201
- /** @internal */
2202
- _detach() {
2203
- if (this.__pool && this.__id) {
2204
- this.__pool.deleteNode(this.__id);
2205
- }
2206
- switch (this.parent.type) {
2207
- case "HasParent": {
2208
- this._parent = Orphaned(this.parent.key, this.parent.pos);
2209
- break;
2210
- }
2211
- case "NoParent": {
2212
- this._parent = NoParent;
2213
- break;
2214
- }
2215
- case "Orphaned": {
2216
- break;
2168
+ async function createThread({
2169
+ metadata,
2170
+ body,
2171
+ commentId,
2172
+ threadId
2173
+ }) {
2174
+ const thread = await fetchJson(
2175
+ "/threads",
2176
+ {
2177
+ method: "POST",
2178
+ headers: {
2179
+ "Content-Type": "application/json"
2180
+ },
2181
+ body: JSON.stringify({
2182
+ id: threadId,
2183
+ comment: {
2184
+ id: commentId,
2185
+ body
2186
+ },
2187
+ metadata
2188
+ })
2217
2189
  }
2218
- default:
2219
- assertNever(this.parent, "Unknown state");
2220
- }
2221
- this.__pool = void 0;
2190
+ );
2191
+ return convertToThreadData(thread);
2222
2192
  }
2223
- /**
2224
- * @internal
2225
- *
2226
- * Clear the Immutable cache, so that the next call to `.toImmutable()` will
2227
- * recompute the equivalent Immutable value again. Call this after every
2228
- * mutation to the Live node.
2229
- */
2230
- invalidate() {
2231
- if (this._cachedImmutable !== void 0 || this._cachedTreeNode !== void 0) {
2232
- this._cachedImmutable = void 0;
2233
- this._cachedTreeNode = void 0;
2234
- if (this.parent.type === "HasParent") {
2235
- this.parent.node.invalidate();
2193
+ async function editThreadMetadata({
2194
+ metadata,
2195
+ threadId
2196
+ }) {
2197
+ return await fetchJson(
2198
+ `/threads/${encodeURIComponent(threadId)}/metadata`,
2199
+ {
2200
+ method: "POST",
2201
+ headers: {
2202
+ "Content-Type": "application/json"
2203
+ },
2204
+ body: JSON.stringify(metadata)
2236
2205
  }
2237
- }
2206
+ );
2238
2207
  }
2239
- /**
2240
- * @internal
2241
- *
2242
- * Return an snapshot of this Live tree for use in DevTools.
2243
- */
2244
- toTreeNode(key) {
2245
- if (this._cachedTreeNode === void 0 || this._cachedTreeNodeKey !== key) {
2246
- this._cachedTreeNodeKey = key;
2247
- this._cachedTreeNode = this._toTreeNode(key);
2248
- }
2249
- return this._cachedTreeNode;
2208
+ async function createComment({
2209
+ threadId,
2210
+ commentId,
2211
+ body
2212
+ }) {
2213
+ const comment = await fetchJson(
2214
+ `/threads/${encodeURIComponent(threadId)}/comments`,
2215
+ {
2216
+ method: "POST",
2217
+ headers: {
2218
+ "Content-Type": "application/json"
2219
+ },
2220
+ body: JSON.stringify({
2221
+ id: commentId,
2222
+ body
2223
+ })
2224
+ }
2225
+ );
2226
+ return convertToCommentData(comment);
2250
2227
  }
2251
- /**
2252
- * Return an immutable snapshot of this Live node and its children.
2253
- */
2254
- toImmutable() {
2255
- if (this._cachedImmutable === void 0) {
2256
- this._cachedImmutable = this._toImmutable();
2257
- }
2258
- return this._cachedImmutable;
2228
+ async function editComment({
2229
+ threadId,
2230
+ commentId,
2231
+ body
2232
+ }) {
2233
+ const comment = await fetchJson(
2234
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
2235
+ commentId
2236
+ )}`,
2237
+ {
2238
+ method: "POST",
2239
+ headers: {
2240
+ "Content-Type": "application/json"
2241
+ },
2242
+ body: JSON.stringify({
2243
+ body
2244
+ })
2245
+ }
2246
+ );
2247
+ return convertToCommentData(comment);
2259
2248
  }
2260
- };
2261
-
2262
- // src/protocol/SerializedCrdt.ts
2263
- var CrdtType = /* @__PURE__ */ ((CrdtType2) => {
2264
- CrdtType2[CrdtType2["OBJECT"] = 0] = "OBJECT";
2265
- CrdtType2[CrdtType2["LIST"] = 1] = "LIST";
2266
- CrdtType2[CrdtType2["MAP"] = 2] = "MAP";
2267
- CrdtType2[CrdtType2["REGISTER"] = 3] = "REGISTER";
2268
- return CrdtType2;
2269
- })(CrdtType || {});
2270
- function isRootCrdt(crdt) {
2271
- return crdt.type === 0 /* OBJECT */ && !isChildCrdt(crdt);
2272
- }
2273
- function isChildCrdt(crdt) {
2274
- return crdt.parentId !== void 0 && crdt.parentKey !== void 0;
2275
- }
2276
-
2277
- // src/lib/nanoid.ts
2278
- function nanoid(length = 7) {
2279
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;[]~!@#$%&*()_+=-";
2280
- const len = alphabet.length;
2281
- return Array.from(
2282
- { length },
2283
- () => alphabet.charAt(Math.floor(Math.random() * len))
2284
- ).join("");
2249
+ async function deleteComment({
2250
+ threadId,
2251
+ commentId
2252
+ }) {
2253
+ await fetchJson(
2254
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
2255
+ commentId
2256
+ )}`,
2257
+ {
2258
+ method: "DELETE"
2259
+ }
2260
+ );
2261
+ }
2262
+ async function addReaction({
2263
+ threadId,
2264
+ commentId,
2265
+ emoji
2266
+ }) {
2267
+ const reaction = await fetchJson(
2268
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
2269
+ commentId
2270
+ )}/reactions`,
2271
+ {
2272
+ method: "POST",
2273
+ headers: {
2274
+ "Content-Type": "application/json"
2275
+ },
2276
+ body: JSON.stringify({ emoji })
2277
+ }
2278
+ );
2279
+ return convertToCommentUserReaction(reaction);
2280
+ }
2281
+ async function removeReaction({
2282
+ threadId,
2283
+ commentId,
2284
+ emoji
2285
+ }) {
2286
+ await fetchJson(
2287
+ `/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(
2288
+ commentId
2289
+ )}/reactions/${encodeURIComponent(emoji)}`,
2290
+ {
2291
+ method: "DELETE"
2292
+ }
2293
+ );
2294
+ }
2295
+ return {
2296
+ getThreads,
2297
+ createThread,
2298
+ editThreadMetadata,
2299
+ createComment,
2300
+ editComment,
2301
+ deleteComment,
2302
+ addReaction,
2303
+ removeReaction
2304
+ };
2285
2305
  }
2286
2306
 
2287
- // src/crdts/LiveRegister.ts
2288
- var LiveRegister = class _LiveRegister extends AbstractCrdt {
2289
- constructor(data) {
2290
- super();
2291
- this._data = data;
2292
- }
2293
- get data() {
2294
- return this._data;
2307
+ // src/lib/position.ts
2308
+ var MIN_CODE = 32;
2309
+ var MAX_CODE = 126;
2310
+ var NUM_DIGITS = MAX_CODE - MIN_CODE + 1;
2311
+ var ZERO = nthDigit(0);
2312
+ var ONE = nthDigit(1);
2313
+ var ZERO_NINE = ZERO + nthDigit(-1);
2314
+ function nthDigit(n) {
2315
+ const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n);
2316
+ if (code < MIN_CODE || code > MAX_CODE) {
2317
+ throw new Error(`Invalid n value: ${n}`);
2295
2318
  }
2296
- /** @internal */
2297
- static _deserialize([id, item], _parentToChildren, pool) {
2298
- const register = new _LiveRegister(item.data);
2299
- register._attach(id, pool);
2300
- return register;
2319
+ return String.fromCharCode(code);
2320
+ }
2321
+ function makePosition(x, y) {
2322
+ if (x !== void 0 && y !== void 0) {
2323
+ return between(x, y);
2324
+ } else if (x !== void 0) {
2325
+ return after(x);
2326
+ } else if (y !== void 0) {
2327
+ return before(y);
2328
+ } else {
2329
+ return ONE;
2301
2330
  }
2302
- /** @internal */
2303
- _toOps(parentId, parentKey, pool) {
2304
- if (this._id === void 0) {
2305
- throw new Error(
2306
- "Cannot serialize register if parentId or parentKey is undefined"
2307
- );
2331
+ }
2332
+ function before(pos) {
2333
+ const lastIndex = pos.length - 1;
2334
+ for (let i = 0; i <= lastIndex; i++) {
2335
+ const code = pos.charCodeAt(i);
2336
+ if (code <= MIN_CODE) {
2337
+ continue;
2308
2338
  }
2309
- return [
2310
- {
2311
- type: 8 /* CREATE_REGISTER */,
2312
- opId: pool?.generateOpId(),
2313
- id: this._id,
2314
- parentId,
2315
- parentKey,
2316
- data: this.data
2339
+ if (i === lastIndex) {
2340
+ if (code === MIN_CODE + 1) {
2341
+ return pos.substring(0, i) + ZERO_NINE;
2342
+ } else {
2343
+ return pos.substring(0, i) + String.fromCharCode(code - 1);
2317
2344
  }
2318
- ];
2319
- }
2320
- /** @internal */
2321
- _serialize() {
2322
- if (this.parent.type !== "HasParent") {
2323
- throw new Error("Cannot serialize LiveRegister if parent is missing");
2345
+ } else {
2346
+ return pos.substring(0, i + 1);
2324
2347
  }
2325
- return {
2326
- type: 3 /* REGISTER */,
2327
- parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
2328
- parentKey: this.parent.key,
2329
- data: this.data
2330
- };
2331
2348
  }
2332
- /** @internal */
2333
- _attachChild(_op) {
2334
- throw new Error("Method not implemented.");
2349
+ return ONE;
2350
+ }
2351
+ function after(pos) {
2352
+ for (let i = 0; i <= pos.length - 1; i++) {
2353
+ const code = pos.charCodeAt(i);
2354
+ if (code >= MAX_CODE) {
2355
+ continue;
2356
+ }
2357
+ return pos.substring(0, i) + String.fromCharCode(code + 1);
2335
2358
  }
2336
- /** @internal */
2337
- _detachChild(_crdt) {
2338
- throw new Error("Method not implemented.");
2359
+ return pos + ONE;
2360
+ }
2361
+ function between(lo, hi) {
2362
+ if (lo < hi) {
2363
+ return _between(lo, hi);
2364
+ } else if (lo > hi) {
2365
+ return _between(hi, lo);
2366
+ } else {
2367
+ throw new Error("Cannot compute value between two equal positions");
2339
2368
  }
2340
- /** @internal */
2341
- _apply(op, isLocal) {
2342
- return super._apply(op, isLocal);
2369
+ }
2370
+ function _between(lo, hi) {
2371
+ let index = 0;
2372
+ const loLen = lo.length;
2373
+ const hiLen = hi.length;
2374
+ while (true) {
2375
+ const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE;
2376
+ const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE;
2377
+ if (loCode === hiCode) {
2378
+ index++;
2379
+ continue;
2380
+ }
2381
+ if (hiCode - loCode === 1) {
2382
+ const size = index + 1;
2383
+ let prefix = lo.substring(0, size);
2384
+ if (prefix.length < size) {
2385
+ prefix += ZERO.repeat(size - prefix.length);
2386
+ }
2387
+ const suffix = lo.substring(size);
2388
+ const nines = "";
2389
+ return prefix + _between(suffix, nines);
2390
+ } else {
2391
+ return takeN(lo, index) + String.fromCharCode(hiCode + loCode >> 1);
2392
+ }
2343
2393
  }
2344
- /** @internal */
2345
- _toTreeNode(key) {
2346
- return {
2347
- type: "Json",
2348
- id: this._id ?? nanoid(),
2349
- key,
2350
- payload: this._data
2351
- };
2394
+ }
2395
+ function takeN(pos, n) {
2396
+ return n < pos.length ? pos.substring(0, n) : pos + ZERO.repeat(n - pos.length);
2397
+ }
2398
+ var MIN_NON_ZERO_CODE = MIN_CODE + 1;
2399
+ function isPos(str) {
2400
+ if (str === "") {
2401
+ return false;
2352
2402
  }
2353
- /** @internal */
2354
- _toImmutable() {
2355
- return this._data;
2403
+ const lastIdx = str.length - 1;
2404
+ const last = str.charCodeAt(lastIdx);
2405
+ if (last < MIN_NON_ZERO_CODE || last > MAX_CODE) {
2406
+ return false;
2356
2407
  }
2357
- clone() {
2358
- return deepClone(this.data);
2408
+ for (let i = 0; i < lastIdx; i++) {
2409
+ const code = str.charCodeAt(i);
2410
+ if (code < MIN_CODE || code > MAX_CODE) {
2411
+ return false;
2412
+ }
2359
2413
  }
2360
- };
2414
+ return true;
2415
+ }
2416
+ function convertToPos(str) {
2417
+ const codes = [];
2418
+ for (let i = 0; i < str.length; i++) {
2419
+ const code = str.charCodeAt(i);
2420
+ codes.push(code < MIN_CODE ? MIN_CODE : code > MAX_CODE ? MAX_CODE : code);
2421
+ }
2422
+ while (codes.length > 0 && codes[codes.length - 1] === MIN_CODE) {
2423
+ codes.length--;
2424
+ }
2425
+ return codes.length > 0 ? String.fromCharCode(...codes) : (
2426
+ // Edge case: the str was a 0-only string, which is invalid. Default back to .1
2427
+ ONE
2428
+ );
2429
+ }
2430
+ function asPos(str) {
2431
+ return isPos(str) ? str : convertToPos(str);
2432
+ }
2361
2433
 
2362
- // src/crdts/LiveList.ts
2363
- function compareNodePosition(itemA, itemB) {
2364
- const posA = itemA._parentPos;
2365
- const posB = itemB._parentPos;
2366
- return posA === posB ? 0 : posA < posB ? -1 : 1;
2434
+ // src/protocol/Op.ts
2435
+ var OpCode = /* @__PURE__ */ ((OpCode2) => {
2436
+ OpCode2[OpCode2["INIT"] = 0] = "INIT";
2437
+ OpCode2[OpCode2["SET_PARENT_KEY"] = 1] = "SET_PARENT_KEY";
2438
+ OpCode2[OpCode2["CREATE_LIST"] = 2] = "CREATE_LIST";
2439
+ OpCode2[OpCode2["UPDATE_OBJECT"] = 3] = "UPDATE_OBJECT";
2440
+ OpCode2[OpCode2["CREATE_OBJECT"] = 4] = "CREATE_OBJECT";
2441
+ OpCode2[OpCode2["DELETE_CRDT"] = 5] = "DELETE_CRDT";
2442
+ OpCode2[OpCode2["DELETE_OBJECT_KEY"] = 6] = "DELETE_OBJECT_KEY";
2443
+ OpCode2[OpCode2["CREATE_MAP"] = 7] = "CREATE_MAP";
2444
+ OpCode2[OpCode2["CREATE_REGISTER"] = 8] = "CREATE_REGISTER";
2445
+ return OpCode2;
2446
+ })(OpCode || {});
2447
+ function isAckOp(op) {
2448
+ return op.type === 5 /* DELETE_CRDT */ && op.id === "ACK";
2367
2449
  }
2368
- var LiveList = class _LiveList extends AbstractCrdt {
2369
- constructor(items = []) {
2370
- super();
2371
- this._items = [];
2372
- this._implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
2373
- this._unacknowledgedSets = /* @__PURE__ */ new Map();
2374
- let position = void 0;
2375
- for (const item of items) {
2376
- const newPosition = makePosition(position);
2377
- const node = lsonToLiveNode(item);
2378
- node._setParentLink(this, newPosition);
2379
- this._items.push(node);
2380
- position = newPosition;
2381
- }
2450
+
2451
+ // src/crdts/AbstractCrdt.ts
2452
+ function crdtAsLiveNode(value) {
2453
+ return value;
2454
+ }
2455
+ function HasParent(node, key, pos = asPos(key)) {
2456
+ return Object.freeze({ type: "HasParent", node, key, pos });
2457
+ }
2458
+ var NoParent = Object.freeze({ type: "NoParent" });
2459
+ function Orphaned(oldKey, oldPos = asPos(oldKey)) {
2460
+ return Object.freeze({ type: "Orphaned", oldKey, oldPos });
2461
+ }
2462
+ var AbstractCrdt = class {
2463
+ constructor() {
2464
+ /** @internal */
2465
+ this._parent = NoParent;
2382
2466
  }
2383
2467
  /** @internal */
2384
- static _deserialize([id], parentToChildren, pool) {
2385
- const list = new _LiveList();
2386
- list._attach(id, pool);
2387
- const children = parentToChildren.get(id);
2388
- if (children === void 0) {
2389
- return list;
2390
- }
2391
- for (const [id2, crdt] of children) {
2392
- const child = deserialize([id2, crdt], parentToChildren, pool);
2393
- child._setParentLink(list, crdt.parentKey);
2394
- list._insertAndSort(child);
2468
+ _getParentKeyOrThrow() {
2469
+ switch (this.parent.type) {
2470
+ case "HasParent":
2471
+ return this.parent.key;
2472
+ case "NoParent":
2473
+ throw new Error("Parent key is missing");
2474
+ case "Orphaned":
2475
+ return this.parent.oldKey;
2476
+ default:
2477
+ return assertNever(this.parent, "Unknown state");
2395
2478
  }
2396
- return list;
2397
2479
  }
2398
- /**
2399
- * @internal
2400
- * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
2401
- * so we mutate _unacknowledgedSets to avoid potential flickering
2402
- * https://github.com/liveblocks/liveblocks/pull/1177
2403
- *
2404
- * This is quite unintuitive and should disappear as soon as
2405
- * we introduce an explicit LiveList.Set operation
2406
- */
2407
- _toOps(parentId, parentKey, pool) {
2408
- if (this._id === void 0) {
2409
- throw new Error("Cannot serialize item is not attached");
2410
- }
2411
- const ops = [];
2412
- const op = {
2413
- id: this._id,
2414
- opId: pool?.generateOpId(),
2415
- type: 2 /* CREATE_LIST */,
2416
- parentId,
2417
- parentKey
2418
- };
2419
- ops.push(op);
2420
- for (const item of this._items) {
2421
- const parentKey2 = item._getParentKeyOrThrow();
2422
- const childOps = HACK_addIntentAndDeletedIdToOperation(
2423
- item._toOps(this._id, parentKey2, pool),
2424
- void 0
2425
- );
2426
- const childOpId = childOps[0].opId;
2427
- if (childOpId !== void 0) {
2428
- this._unacknowledgedSets.set(parentKey2, childOpId);
2429
- }
2430
- ops.push(...childOps);
2480
+ /** @internal */
2481
+ get _parentPos() {
2482
+ switch (this.parent.type) {
2483
+ case "HasParent":
2484
+ return this.parent.pos;
2485
+ case "NoParent":
2486
+ throw new Error("Parent key is missing");
2487
+ case "Orphaned":
2488
+ return this.parent.oldPos;
2489
+ default:
2490
+ return assertNever(this.parent, "Unknown state");
2431
2491
  }
2432
- return ops;
2433
2492
  }
2434
- /**
2435
- * @internal
2436
- *
2437
- * Adds a new item into the sorted list, in the correct position.
2438
- */
2439
- _insertAndSort(item) {
2440
- this._items.push(item);
2441
- this._sortItems();
2493
+ /** @internal */
2494
+ get _pool() {
2495
+ return this.__pool;
2496
+ }
2497
+ get roomId() {
2498
+ return this.__pool ? this.__pool.roomId : null;
2442
2499
  }
2443
2500
  /** @internal */
2444
- _sortItems() {
2445
- this._items.sort(compareNodePosition);
2446
- this.invalidate();
2501
+ get _id() {
2502
+ return this.__id;
2447
2503
  }
2448
2504
  /** @internal */
2449
- _indexOfPosition(position) {
2450
- return this._items.findIndex(
2451
- (item) => item._getParentKeyOrThrow() === position
2452
- );
2505
+ get parent() {
2506
+ return this._parent;
2453
2507
  }
2454
2508
  /** @internal */
2455
- _attach(id, pool) {
2456
- super._attach(id, pool);
2457
- for (const item of this._items) {
2458
- item._attach(pool.generateId(), pool);
2509
+ get _parentKey() {
2510
+ switch (this.parent.type) {
2511
+ case "HasParent":
2512
+ return this.parent.key;
2513
+ case "NoParent":
2514
+ return null;
2515
+ case "Orphaned":
2516
+ return this.parent.oldKey;
2517
+ default:
2518
+ return assertNever(this.parent, "Unknown state");
2459
2519
  }
2460
2520
  }
2461
2521
  /** @internal */
2462
- _detach() {
2463
- super._detach();
2464
- for (const item of this._items) {
2465
- item._detach();
2522
+ _apply(op, _isLocal) {
2523
+ switch (op.type) {
2524
+ case 5 /* DELETE_CRDT */: {
2525
+ if (this.parent.type === "HasParent") {
2526
+ return this.parent.node._detachChild(crdtAsLiveNode(this));
2527
+ }
2528
+ return { modified: false };
2529
+ }
2466
2530
  }
2531
+ return { modified: false };
2467
2532
  }
2468
2533
  /** @internal */
2469
- _applySetRemote(op) {
2470
- if (this._pool === void 0) {
2471
- throw new Error("Can't attach child if managed pool is not present");
2472
- }
2473
- const { id, parentKey: key } = op;
2474
- const child = creationOpToLiveNode(op);
2475
- child._attach(id, this._pool);
2476
- child._setParentLink(this, key);
2477
- const deletedId = op.deletedId;
2478
- const indexOfItemWithSamePosition = this._indexOfPosition(key);
2479
- if (indexOfItemWithSamePosition !== -1) {
2480
- const itemWithSamePosition = this._items[indexOfItemWithSamePosition];
2481
- if (itemWithSamePosition._id === deletedId) {
2482
- itemWithSamePosition._detach();
2483
- this._items[indexOfItemWithSamePosition] = child;
2484
- return {
2485
- modified: makeUpdate(this, [
2486
- setDelta(indexOfItemWithSamePosition, child)
2487
- ]),
2488
- reverse: []
2489
- };
2490
- } else {
2491
- this._implicitlyDeletedItems.add(itemWithSamePosition);
2492
- this._items[indexOfItemWithSamePosition] = child;
2493
- const delta = [
2494
- setDelta(indexOfItemWithSamePosition, child)
2495
- ];
2496
- const deleteDelta2 = this._detachItemAssociatedToSetOperation(
2497
- op.deletedId
2498
- );
2499
- if (deleteDelta2) {
2500
- delta.push(deleteDelta2);
2534
+ _setParentLink(newParentNode, newParentKey) {
2535
+ switch (this.parent.type) {
2536
+ case "HasParent":
2537
+ if (this.parent.node !== newParentNode) {
2538
+ throw new Error("Cannot set parent: node already has a parent");
2539
+ } else {
2540
+ this._parent = HasParent(newParentNode, newParentKey);
2541
+ return;
2501
2542
  }
2502
- return {
2503
- modified: makeUpdate(this, delta),
2504
- reverse: []
2505
- };
2506
- }
2507
- } else {
2508
- const updates = [];
2509
- const deleteDelta2 = this._detachItemAssociatedToSetOperation(
2510
- op.deletedId
2511
- );
2512
- if (deleteDelta2) {
2513
- updates.push(deleteDelta2);
2543
+ case "Orphaned":
2544
+ case "NoParent": {
2545
+ this._parent = HasParent(newParentNode, newParentKey);
2546
+ return;
2514
2547
  }
2515
- this._insertAndSort(child);
2516
- updates.push(insertDelta(this._indexOfPosition(key), child));
2517
- return {
2518
- reverse: [],
2519
- modified: makeUpdate(this, updates)
2520
- };
2548
+ default:
2549
+ return assertNever(this.parent, "Unknown state");
2521
2550
  }
2522
2551
  }
2523
2552
  /** @internal */
2524
- _applySetAck(op) {
2525
- if (this._pool === void 0) {
2526
- throw new Error("Can't attach child if managed pool is not present");
2527
- }
2528
- const delta = [];
2529
- const deletedDelta = this._detachItemAssociatedToSetOperation(op.deletedId);
2530
- if (deletedDelta) {
2531
- delta.push(deletedDelta);
2553
+ _attach(id, pool) {
2554
+ if (this.__id || this.__pool) {
2555
+ throw new Error("Cannot attach node: already attached");
2532
2556
  }
2533
- const unacknowledgedOpId = this._unacknowledgedSets.get(op.parentKey);
2534
- if (unacknowledgedOpId !== void 0) {
2535
- if (unacknowledgedOpId !== op.opId) {
2536
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
2537
- } else {
2538
- this._unacknowledgedSets.delete(op.parentKey);
2539
- }
2557
+ pool.addNode(id, crdtAsLiveNode(this));
2558
+ this.__id = id;
2559
+ this.__pool = pool;
2560
+ }
2561
+ /** @internal */
2562
+ _detach() {
2563
+ if (this.__pool && this.__id) {
2564
+ this.__pool.deleteNode(this.__id);
2540
2565
  }
2541
- const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
2542
- const existingItem = this._items.find((item) => item._id === op.id);
2543
- if (existingItem !== void 0) {
2544
- if (existingItem._parentKey === op.parentKey) {
2545
- return {
2546
- modified: delta.length > 0 ? makeUpdate(this, delta) : false,
2547
- reverse: []
2548
- };
2549
- }
2550
- if (indexOfItemWithSamePosition !== -1) {
2551
- this._implicitlyDeletedItems.add(
2552
- this._items[indexOfItemWithSamePosition]
2553
- );
2554
- this._items.splice(indexOfItemWithSamePosition, 1);
2555
- delta.push(deleteDelta(indexOfItemWithSamePosition));
2566
+ switch (this.parent.type) {
2567
+ case "HasParent": {
2568
+ this._parent = Orphaned(this.parent.key, this.parent.pos);
2569
+ break;
2556
2570
  }
2557
- const previousIndex = this._items.indexOf(existingItem);
2558
- existingItem._setParentLink(this, op.parentKey);
2559
- this._sortItems();
2560
- const newIndex = this._items.indexOf(existingItem);
2561
- if (newIndex !== previousIndex) {
2562
- delta.push(moveDelta(previousIndex, newIndex, existingItem));
2571
+ case "NoParent": {
2572
+ this._parent = NoParent;
2573
+ break;
2563
2574
  }
2564
- return {
2565
- modified: delta.length > 0 ? makeUpdate(this, delta) : false,
2566
- reverse: []
2567
- };
2568
- } else {
2569
- const orphan = this._pool.getNode(op.id);
2570
- if (orphan && this._implicitlyDeletedItems.has(orphan)) {
2571
- orphan._setParentLink(this, op.parentKey);
2572
- this._implicitlyDeletedItems.delete(orphan);
2573
- this._insertAndSort(orphan);
2574
- const recreatedItemIndex = this._items.indexOf(orphan);
2575
- return {
2576
- modified: makeUpdate(this, [
2577
- // If there is an item at this position, update is a set, else it's an insert
2578
- indexOfItemWithSamePosition === -1 ? insertDelta(recreatedItemIndex, orphan) : setDelta(recreatedItemIndex, orphan),
2579
- ...delta
2580
- ]),
2581
- reverse: []
2582
- };
2583
- } else {
2584
- if (indexOfItemWithSamePosition !== -1) {
2585
- this._items.splice(indexOfItemWithSamePosition, 1);
2586
- }
2587
- const { newItem, newIndex } = this._createAttachItemAndSort(
2588
- op,
2589
- op.parentKey
2590
- );
2591
- return {
2592
- modified: makeUpdate(this, [
2593
- // If there is an item at this position, update is a set, else it's an insert
2594
- indexOfItemWithSamePosition === -1 ? insertDelta(newIndex, newItem) : setDelta(newIndex, newItem),
2595
- ...delta
2596
- ]),
2597
- reverse: []
2598
- };
2575
+ case "Orphaned": {
2576
+ break;
2599
2577
  }
2578
+ default:
2579
+ assertNever(this.parent, "Unknown state");
2600
2580
  }
2581
+ this.__pool = void 0;
2601
2582
  }
2602
2583
  /**
2603
- * Returns the update delta of the deletion or null
2604
2584
  * @internal
2585
+ *
2586
+ * Clear the Immutable cache, so that the next call to `.toImmutable()` will
2587
+ * recompute the equivalent Immutable value again. Call this after every
2588
+ * mutation to the Live node.
2605
2589
  */
2606
- _detachItemAssociatedToSetOperation(deletedId) {
2607
- if (deletedId === void 0 || this._pool === void 0) {
2608
- return null;
2590
+ invalidate() {
2591
+ if (this._cachedImmutable !== void 0 || this._cachedTreeNode !== void 0) {
2592
+ this._cachedImmutable = void 0;
2593
+ this._cachedTreeNode = void 0;
2594
+ if (this.parent.type === "HasParent") {
2595
+ this.parent.node.invalidate();
2596
+ }
2609
2597
  }
2610
- const deletedItem = this._pool.getNode(deletedId);
2611
- if (deletedItem === void 0) {
2612
- return null;
2598
+ }
2599
+ /**
2600
+ * @internal
2601
+ *
2602
+ * Return an snapshot of this Live tree for use in DevTools.
2603
+ */
2604
+ toTreeNode(key) {
2605
+ if (this._cachedTreeNode === void 0 || this._cachedTreeNodeKey !== key) {
2606
+ this._cachedTreeNodeKey = key;
2607
+ this._cachedTreeNode = this._toTreeNode(key);
2613
2608
  }
2614
- const result = this._detachChild(deletedItem);
2615
- if (result.modified === false) {
2616
- return null;
2609
+ return this._cachedTreeNode;
2610
+ }
2611
+ /**
2612
+ * Return an immutable snapshot of this Live node and its children.
2613
+ */
2614
+ toImmutable() {
2615
+ if (this._cachedImmutable === void 0) {
2616
+ this._cachedImmutable = this._toImmutable();
2617
2617
  }
2618
- return result.modified.updates[0];
2618
+ return this._cachedImmutable;
2619
+ }
2620
+ };
2621
+
2622
+ // src/protocol/SerializedCrdt.ts
2623
+ var CrdtType = /* @__PURE__ */ ((CrdtType2) => {
2624
+ CrdtType2[CrdtType2["OBJECT"] = 0] = "OBJECT";
2625
+ CrdtType2[CrdtType2["LIST"] = 1] = "LIST";
2626
+ CrdtType2[CrdtType2["MAP"] = 2] = "MAP";
2627
+ CrdtType2[CrdtType2["REGISTER"] = 3] = "REGISTER";
2628
+ return CrdtType2;
2629
+ })(CrdtType || {});
2630
+ function isRootCrdt(crdt) {
2631
+ return crdt.type === 0 /* OBJECT */ && !isChildCrdt(crdt);
2632
+ }
2633
+ function isChildCrdt(crdt) {
2634
+ return crdt.parentId !== void 0 && crdt.parentKey !== void 0;
2635
+ }
2636
+
2637
+ // src/lib/nanoid.ts
2638
+ function nanoid(length = 7) {
2639
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;[]~!@#$%&*()_+=-";
2640
+ const len = alphabet.length;
2641
+ return Array.from(
2642
+ { length },
2643
+ () => alphabet.charAt(Math.floor(Math.random() * len))
2644
+ ).join("");
2645
+ }
2646
+
2647
+ // src/crdts/LiveRegister.ts
2648
+ var LiveRegister = class _LiveRegister extends AbstractCrdt {
2649
+ constructor(data) {
2650
+ super();
2651
+ this._data = data;
2652
+ }
2653
+ get data() {
2654
+ return this._data;
2619
2655
  }
2620
2656
  /** @internal */
2621
- _applyRemoteInsert(op) {
2622
- if (this._pool === void 0) {
2623
- throw new Error("Can't attach child if managed pool is not present");
2624
- }
2625
- const key = asPos(op.parentKey);
2626
- const existingItemIndex = this._indexOfPosition(key);
2627
- if (existingItemIndex !== -1) {
2628
- this._shiftItemPosition(existingItemIndex, key);
2629
- }
2630
- const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
2631
- return {
2632
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
2633
- reverse: []
2634
- };
2657
+ static _deserialize([id, item], _parentToChildren, pool) {
2658
+ const register = new _LiveRegister(item.data);
2659
+ register._attach(id, pool);
2660
+ return register;
2635
2661
  }
2636
2662
  /** @internal */
2637
- _applyInsertAck(op) {
2638
- const existingItem = this._items.find((item) => item._id === op.id);
2639
- const key = asPos(op.parentKey);
2640
- const itemIndexAtPosition = this._indexOfPosition(key);
2641
- if (existingItem) {
2642
- if (existingItem._parentKey === key) {
2643
- return {
2644
- modified: false
2645
- };
2646
- } else {
2647
- const oldPositionIndex = this._items.indexOf(existingItem);
2648
- if (itemIndexAtPosition !== -1) {
2649
- this._shiftItemPosition(itemIndexAtPosition, key);
2650
- }
2651
- existingItem._setParentLink(this, key);
2652
- this._sortItems();
2653
- const newIndex = this._indexOfPosition(key);
2654
- if (newIndex === oldPositionIndex) {
2655
- return { modified: false };
2656
- }
2657
- return {
2658
- modified: makeUpdate(this, [
2659
- moveDelta(oldPositionIndex, newIndex, existingItem)
2660
- ]),
2661
- reverse: []
2662
- };
2663
- }
2664
- } else {
2665
- const orphan = nn(this._pool).getNode(op.id);
2666
- if (orphan && this._implicitlyDeletedItems.has(orphan)) {
2667
- orphan._setParentLink(this, key);
2668
- this._implicitlyDeletedItems.delete(orphan);
2669
- this._insertAndSort(orphan);
2670
- const newIndex = this._indexOfPosition(key);
2671
- return {
2672
- modified: makeUpdate(this, [insertDelta(newIndex, orphan)]),
2673
- reverse: []
2674
- };
2675
- } else {
2676
- if (itemIndexAtPosition !== -1) {
2677
- this._shiftItemPosition(itemIndexAtPosition, key);
2678
- }
2679
- const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
2680
- return {
2681
- modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
2682
- reverse: []
2683
- };
2684
- }
2663
+ _toOps(parentId, parentKey, pool) {
2664
+ if (this._id === void 0) {
2665
+ throw new Error(
2666
+ "Cannot serialize register if parentId or parentKey is undefined"
2667
+ );
2685
2668
  }
2669
+ return [
2670
+ {
2671
+ type: 8 /* CREATE_REGISTER */,
2672
+ opId: pool?.generateOpId(),
2673
+ id: this._id,
2674
+ parentId,
2675
+ parentKey,
2676
+ data: this.data
2677
+ }
2678
+ ];
2686
2679
  }
2687
2680
  /** @internal */
2688
- _applyInsertUndoRedo(op) {
2689
- const { id, parentKey: key } = op;
2690
- const child = creationOpToLiveNode(op);
2691
- if (this._pool?.getNode(id) !== void 0) {
2692
- return { modified: false };
2693
- }
2694
- child._attach(id, nn(this._pool));
2695
- child._setParentLink(this, key);
2696
- const existingItemIndex = this._indexOfPosition(key);
2697
- let newKey = key;
2698
- if (existingItemIndex !== -1) {
2699
- const before2 = this._items[existingItemIndex]?._parentPos;
2700
- const after2 = this._items[existingItemIndex + 1]?._parentPos;
2701
- newKey = makePosition(before2, after2);
2702
- child._setParentLink(this, newKey);
2681
+ _serialize() {
2682
+ if (this.parent.type !== "HasParent") {
2683
+ throw new Error("Cannot serialize LiveRegister if parent is missing");
2703
2684
  }
2704
- this._insertAndSort(child);
2705
- const newIndex = this._indexOfPosition(newKey);
2706
2685
  return {
2707
- modified: makeUpdate(this, [insertDelta(newIndex, child)]),
2708
- reverse: [{ type: 5 /* DELETE_CRDT */, id }]
2686
+ type: 3 /* REGISTER */,
2687
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
2688
+ parentKey: this.parent.key,
2689
+ data: this.data
2709
2690
  };
2710
2691
  }
2711
2692
  /** @internal */
2712
- _applySetUndoRedo(op) {
2713
- const { id, parentKey: key } = op;
2714
- const child = creationOpToLiveNode(op);
2715
- if (this._pool?.getNode(id) !== void 0) {
2716
- return { modified: false };
2717
- }
2718
- this._unacknowledgedSets.set(key, nn(op.opId));
2719
- const indexOfItemWithSameKey = this._indexOfPosition(key);
2720
- child._attach(id, nn(this._pool));
2721
- child._setParentLink(this, key);
2722
- const newKey = key;
2723
- if (indexOfItemWithSameKey !== -1) {
2724
- const existingItem = this._items[indexOfItemWithSameKey];
2725
- existingItem._detach();
2726
- this._items[indexOfItemWithSameKey] = child;
2727
- const reverse = HACK_addIntentAndDeletedIdToOperation(
2728
- existingItem._toOps(nn(this._id), key, this._pool),
2729
- op.id
2730
- );
2731
- const delta = [setDelta(indexOfItemWithSameKey, child)];
2732
- const deletedDelta = this._detachItemAssociatedToSetOperation(
2733
- op.deletedId
2734
- );
2735
- if (deletedDelta) {
2736
- delta.push(deletedDelta);
2737
- }
2738
- return {
2739
- modified: makeUpdate(this, delta),
2740
- reverse
2741
- };
2742
- } else {
2743
- this._insertAndSort(child);
2744
- this._detachItemAssociatedToSetOperation(op.deletedId);
2745
- const newIndex = this._indexOfPosition(newKey);
2746
- return {
2747
- reverse: [{ type: 5 /* DELETE_CRDT */, id }],
2748
- modified: makeUpdate(this, [insertDelta(newIndex, child)])
2749
- };
2750
- }
2693
+ _attachChild(_op) {
2694
+ throw new Error("Method not implemented.");
2751
2695
  }
2752
2696
  /** @internal */
2753
- _attachChild(op, source) {
2754
- if (this._pool === void 0) {
2755
- throw new Error("Can't attach child if managed pool is not present");
2756
- }
2757
- let result;
2758
- if (op.intent === "set") {
2759
- if (source === 1 /* REMOTE */) {
2760
- result = this._applySetRemote(op);
2761
- } else if (source === 2 /* ACK */) {
2762
- result = this._applySetAck(op);
2763
- } else {
2764
- result = this._applySetUndoRedo(op);
2765
- }
2766
- } else {
2767
- if (source === 1 /* REMOTE */) {
2768
- result = this._applyRemoteInsert(op);
2769
- } else if (source === 2 /* ACK */) {
2770
- result = this._applyInsertAck(op);
2771
- } else {
2772
- result = this._applyInsertUndoRedo(op);
2773
- }
2774
- }
2775
- if (result.modified !== false) {
2776
- this.invalidate();
2777
- }
2778
- return result;
2697
+ _detachChild(_crdt) {
2698
+ throw new Error("Method not implemented.");
2779
2699
  }
2780
2700
  /** @internal */
2781
- _detachChild(child) {
2782
- if (child) {
2783
- const parentKey = nn(child._parentKey);
2784
- const reverse = child._toOps(nn(this._id), parentKey, this._pool);
2785
- const indexToDelete = this._items.indexOf(child);
2786
- if (indexToDelete === -1) {
2787
- return {
2788
- modified: false
2789
- };
2790
- }
2791
- this._items.splice(indexToDelete, 1);
2792
- this.invalidate();
2793
- child._detach();
2794
- return {
2795
- modified: makeUpdate(this, [deleteDelta(indexToDelete)]),
2796
- reverse
2797
- };
2798
- }
2799
- return { modified: false };
2701
+ _apply(op, isLocal) {
2702
+ return super._apply(op, isLocal);
2800
2703
  }
2801
2704
  /** @internal */
2802
- _applySetChildKeyRemote(newKey, child) {
2803
- if (this._implicitlyDeletedItems.has(child)) {
2804
- this._implicitlyDeletedItems.delete(child);
2805
- child._setParentLink(this, newKey);
2806
- this._insertAndSort(child);
2807
- const newIndex = this._items.indexOf(child);
2808
- return {
2809
- modified: makeUpdate(this, [insertDelta(newIndex, child)]),
2810
- reverse: []
2811
- };
2812
- }
2813
- const previousKey = child._parentKey;
2814
- if (newKey === previousKey) {
2815
- return {
2816
- modified: false
2817
- };
2818
- }
2819
- const existingItemIndex = this._indexOfPosition(newKey);
2820
- if (existingItemIndex === -1) {
2821
- const previousIndex = this._items.indexOf(child);
2822
- child._setParentLink(this, newKey);
2823
- this._sortItems();
2824
- const newIndex = this._items.indexOf(child);
2825
- if (newIndex === previousIndex) {
2826
- return {
2827
- modified: false
2828
- };
2829
- }
2830
- return {
2831
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
2832
- reverse: []
2833
- };
2834
- } else {
2835
- this._items[existingItemIndex]._setParentLink(
2836
- this,
2837
- makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
2838
- );
2839
- const previousIndex = this._items.indexOf(child);
2840
- child._setParentLink(this, newKey);
2841
- this._sortItems();
2842
- const newIndex = this._items.indexOf(child);
2843
- if (newIndex === previousIndex) {
2844
- return {
2845
- modified: false
2846
- };
2847
- }
2848
- return {
2849
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
2850
- reverse: []
2851
- };
2705
+ _toTreeNode(key) {
2706
+ return {
2707
+ type: "Json",
2708
+ id: this._id ?? nanoid(),
2709
+ key,
2710
+ payload: this._data
2711
+ };
2712
+ }
2713
+ /** @internal */
2714
+ _toImmutable() {
2715
+ return this._data;
2716
+ }
2717
+ clone() {
2718
+ return deepClone(this.data);
2719
+ }
2720
+ };
2721
+
2722
+ // src/crdts/LiveList.ts
2723
+ function compareNodePosition(itemA, itemB) {
2724
+ const posA = itemA._parentPos;
2725
+ const posB = itemB._parentPos;
2726
+ return posA === posB ? 0 : posA < posB ? -1 : 1;
2727
+ }
2728
+ var LiveList = class _LiveList extends AbstractCrdt {
2729
+ constructor(items = []) {
2730
+ super();
2731
+ this._items = [];
2732
+ this._implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
2733
+ this._unacknowledgedSets = /* @__PURE__ */ new Map();
2734
+ let position = void 0;
2735
+ for (const item of items) {
2736
+ const newPosition = makePosition(position);
2737
+ const node = lsonToLiveNode(item);
2738
+ node._setParentLink(this, newPosition);
2739
+ this._items.push(node);
2740
+ position = newPosition;
2852
2741
  }
2853
2742
  }
2854
2743
  /** @internal */
2855
- _applySetChildKeyAck(newKey, child) {
2856
- const previousKey = nn(child._parentKey);
2857
- if (this._implicitlyDeletedItems.has(child)) {
2858
- const existingItemIndex = this._indexOfPosition(newKey);
2859
- this._implicitlyDeletedItems.delete(child);
2860
- if (existingItemIndex !== -1) {
2861
- this._items[existingItemIndex]._setParentLink(
2862
- this,
2863
- makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
2744
+ static _deserialize([id], parentToChildren, pool) {
2745
+ const list = new _LiveList();
2746
+ list._attach(id, pool);
2747
+ const children = parentToChildren.get(id);
2748
+ if (children === void 0) {
2749
+ return list;
2750
+ }
2751
+ for (const [id2, crdt] of children) {
2752
+ const child = deserialize([id2, crdt], parentToChildren, pool);
2753
+ child._setParentLink(list, crdt.parentKey);
2754
+ list._insertAndSort(child);
2755
+ }
2756
+ return list;
2757
+ }
2758
+ /**
2759
+ * @internal
2760
+ * This function assumes that the resulting ops will be sent to the server if they have an 'opId'
2761
+ * so we mutate _unacknowledgedSets to avoid potential flickering
2762
+ * https://github.com/liveblocks/liveblocks/pull/1177
2763
+ *
2764
+ * This is quite unintuitive and should disappear as soon as
2765
+ * we introduce an explicit LiveList.Set operation
2766
+ */
2767
+ _toOps(parentId, parentKey, pool) {
2768
+ if (this._id === void 0) {
2769
+ throw new Error("Cannot serialize item is not attached");
2770
+ }
2771
+ const ops = [];
2772
+ const op = {
2773
+ id: this._id,
2774
+ opId: pool?.generateOpId(),
2775
+ type: 2 /* CREATE_LIST */,
2776
+ parentId,
2777
+ parentKey
2778
+ };
2779
+ ops.push(op);
2780
+ for (const item of this._items) {
2781
+ const parentKey2 = item._getParentKeyOrThrow();
2782
+ const childOps = HACK_addIntentAndDeletedIdToOperation(
2783
+ item._toOps(this._id, parentKey2, pool),
2784
+ void 0
2785
+ );
2786
+ const childOpId = childOps[0].opId;
2787
+ if (childOpId !== void 0) {
2788
+ this._unacknowledgedSets.set(parentKey2, childOpId);
2789
+ }
2790
+ ops.push(...childOps);
2791
+ }
2792
+ return ops;
2793
+ }
2794
+ /**
2795
+ * @internal
2796
+ *
2797
+ * Adds a new item into the sorted list, in the correct position.
2798
+ */
2799
+ _insertAndSort(item) {
2800
+ this._items.push(item);
2801
+ this._sortItems();
2802
+ }
2803
+ /** @internal */
2804
+ _sortItems() {
2805
+ this._items.sort(compareNodePosition);
2806
+ this.invalidate();
2807
+ }
2808
+ /** @internal */
2809
+ _indexOfPosition(position) {
2810
+ return this._items.findIndex(
2811
+ (item) => item._getParentKeyOrThrow() === position
2812
+ );
2813
+ }
2814
+ /** @internal */
2815
+ _attach(id, pool) {
2816
+ super._attach(id, pool);
2817
+ for (const item of this._items) {
2818
+ item._attach(pool.generateId(), pool);
2819
+ }
2820
+ }
2821
+ /** @internal */
2822
+ _detach() {
2823
+ super._detach();
2824
+ for (const item of this._items) {
2825
+ item._detach();
2826
+ }
2827
+ }
2828
+ /** @internal */
2829
+ _applySetRemote(op) {
2830
+ if (this._pool === void 0) {
2831
+ throw new Error("Can't attach child if managed pool is not present");
2832
+ }
2833
+ const { id, parentKey: key } = op;
2834
+ const child = creationOpToLiveNode(op);
2835
+ child._attach(id, this._pool);
2836
+ child._setParentLink(this, key);
2837
+ const deletedId = op.deletedId;
2838
+ const indexOfItemWithSamePosition = this._indexOfPosition(key);
2839
+ if (indexOfItemWithSamePosition !== -1) {
2840
+ const itemWithSamePosition = this._items[indexOfItemWithSamePosition];
2841
+ if (itemWithSamePosition._id === deletedId) {
2842
+ itemWithSamePosition._detach();
2843
+ this._items[indexOfItemWithSamePosition] = child;
2844
+ return {
2845
+ modified: makeUpdate(this, [
2846
+ setDelta(indexOfItemWithSamePosition, child)
2847
+ ]),
2848
+ reverse: []
2849
+ };
2850
+ } else {
2851
+ this._implicitlyDeletedItems.add(itemWithSamePosition);
2852
+ this._items[indexOfItemWithSamePosition] = child;
2853
+ const delta = [
2854
+ setDelta(indexOfItemWithSamePosition, child)
2855
+ ];
2856
+ const deleteDelta2 = this._detachItemAssociatedToSetOperation(
2857
+ op.deletedId
2864
2858
  );
2859
+ if (deleteDelta2) {
2860
+ delta.push(deleteDelta2);
2861
+ }
2862
+ return {
2863
+ modified: makeUpdate(this, delta),
2864
+ reverse: []
2865
+ };
2866
+ }
2867
+ } else {
2868
+ const updates = [];
2869
+ const deleteDelta2 = this._detachItemAssociatedToSetOperation(
2870
+ op.deletedId
2871
+ );
2872
+ if (deleteDelta2) {
2873
+ updates.push(deleteDelta2);
2865
2874
  }
2866
- child._setParentLink(this, newKey);
2867
2875
  this._insertAndSort(child);
2876
+ updates.push(insertDelta(this._indexOfPosition(key), child));
2868
2877
  return {
2869
- modified: false
2878
+ reverse: [],
2879
+ modified: makeUpdate(this, updates)
2870
2880
  };
2871
- } else {
2872
- if (newKey === previousKey) {
2881
+ }
2882
+ }
2883
+ /** @internal */
2884
+ _applySetAck(op) {
2885
+ if (this._pool === void 0) {
2886
+ throw new Error("Can't attach child if managed pool is not present");
2887
+ }
2888
+ const delta = [];
2889
+ const deletedDelta = this._detachItemAssociatedToSetOperation(op.deletedId);
2890
+ if (deletedDelta) {
2891
+ delta.push(deletedDelta);
2892
+ }
2893
+ const unacknowledgedOpId = this._unacknowledgedSets.get(op.parentKey);
2894
+ if (unacknowledgedOpId !== void 0) {
2895
+ if (unacknowledgedOpId !== op.opId) {
2896
+ return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
2897
+ } else {
2898
+ this._unacknowledgedSets.delete(op.parentKey);
2899
+ }
2900
+ }
2901
+ const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
2902
+ const existingItem = this._items.find((item) => item._id === op.id);
2903
+ if (existingItem !== void 0) {
2904
+ if (existingItem._parentKey === op.parentKey) {
2873
2905
  return {
2874
- modified: false
2906
+ modified: delta.length > 0 ? makeUpdate(this, delta) : false,
2907
+ reverse: []
2875
2908
  };
2876
2909
  }
2877
- const previousIndex = this._items.indexOf(child);
2878
- const existingItemIndex = this._indexOfPosition(newKey);
2879
- if (existingItemIndex !== -1) {
2880
- this._items[existingItemIndex]._setParentLink(
2881
- this,
2882
- makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
2910
+ if (indexOfItemWithSamePosition !== -1) {
2911
+ this._implicitlyDeletedItems.add(
2912
+ this._items[indexOfItemWithSamePosition]
2883
2913
  );
2914
+ this._items.splice(indexOfItemWithSamePosition, 1);
2915
+ delta.push(deleteDelta(indexOfItemWithSamePosition));
2884
2916
  }
2885
- child._setParentLink(this, newKey);
2917
+ const previousIndex = this._items.indexOf(existingItem);
2918
+ existingItem._setParentLink(this, op.parentKey);
2886
2919
  this._sortItems();
2887
- const newIndex = this._items.indexOf(child);
2888
- if (previousIndex === newIndex) {
2920
+ const newIndex = this._items.indexOf(existingItem);
2921
+ if (newIndex !== previousIndex) {
2922
+ delta.push(moveDelta(previousIndex, newIndex, existingItem));
2923
+ }
2924
+ return {
2925
+ modified: delta.length > 0 ? makeUpdate(this, delta) : false,
2926
+ reverse: []
2927
+ };
2928
+ } else {
2929
+ const orphan = this._pool.getNode(op.id);
2930
+ if (orphan && this._implicitlyDeletedItems.has(orphan)) {
2931
+ orphan._setParentLink(this, op.parentKey);
2932
+ this._implicitlyDeletedItems.delete(orphan);
2933
+ this._insertAndSort(orphan);
2934
+ const recreatedItemIndex = this._items.indexOf(orphan);
2889
2935
  return {
2890
- modified: false
2936
+ modified: makeUpdate(this, [
2937
+ // If there is an item at this position, update is a set, else it's an insert
2938
+ indexOfItemWithSamePosition === -1 ? insertDelta(recreatedItemIndex, orphan) : setDelta(recreatedItemIndex, orphan),
2939
+ ...delta
2940
+ ]),
2941
+ reverse: []
2891
2942
  };
2892
2943
  } else {
2944
+ if (indexOfItemWithSamePosition !== -1) {
2945
+ this._items.splice(indexOfItemWithSamePosition, 1);
2946
+ }
2947
+ const { newItem, newIndex } = this._createAttachItemAndSort(
2948
+ op,
2949
+ op.parentKey
2950
+ );
2893
2951
  return {
2894
2952
  modified: makeUpdate(this, [
2895
- moveDelta(previousIndex, newIndex, child)
2953
+ // If there is an item at this position, update is a set, else it's an insert
2954
+ indexOfItemWithSamePosition === -1 ? insertDelta(newIndex, newItem) : setDelta(newIndex, newItem),
2955
+ ...delta
2896
2956
  ]),
2897
2957
  reverse: []
2898
2958
  };
2899
2959
  }
2900
2960
  }
2901
2961
  }
2962
+ /**
2963
+ * Returns the update delta of the deletion or null
2964
+ * @internal
2965
+ */
2966
+ _detachItemAssociatedToSetOperation(deletedId) {
2967
+ if (deletedId === void 0 || this._pool === void 0) {
2968
+ return null;
2969
+ }
2970
+ const deletedItem = this._pool.getNode(deletedId);
2971
+ if (deletedItem === void 0) {
2972
+ return null;
2973
+ }
2974
+ const result = this._detachChild(deletedItem);
2975
+ if (result.modified === false) {
2976
+ return null;
2977
+ }
2978
+ return result.modified.updates[0];
2979
+ }
2902
2980
  /** @internal */
2903
- _applySetChildKeyUndoRedo(newKey, child) {
2904
- const previousKey = nn(child._parentKey);
2905
- const previousIndex = this._items.indexOf(child);
2906
- const existingItemIndex = this._indexOfPosition(newKey);
2981
+ _applyRemoteInsert(op) {
2982
+ if (this._pool === void 0) {
2983
+ throw new Error("Can't attach child if managed pool is not present");
2984
+ }
2985
+ const key = asPos(op.parentKey);
2986
+ const existingItemIndex = this._indexOfPosition(key);
2907
2987
  if (existingItemIndex !== -1) {
2908
- this._items[existingItemIndex]._setParentLink(
2909
- this,
2910
- makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
2911
- );
2912
- }
2913
- child._setParentLink(this, newKey);
2914
- this._sortItems();
2915
- const newIndex = this._items.indexOf(child);
2916
- if (previousIndex === newIndex) {
2917
- return {
2918
- modified: false
2919
- };
2988
+ this._shiftItemPosition(existingItemIndex, key);
2920
2989
  }
2990
+ const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
2921
2991
  return {
2922
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
2923
- reverse: [
2924
- {
2925
- type: 1 /* SET_PARENT_KEY */,
2926
- id: nn(child._id),
2927
- parentKey: previousKey
2928
- }
2929
- ]
2992
+ modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
2993
+ reverse: []
2930
2994
  };
2931
2995
  }
2932
2996
  /** @internal */
2933
- _setChildKey(newKey, child, source) {
2934
- if (source === 1 /* REMOTE */) {
2935
- return this._applySetChildKeyRemote(newKey, child);
2936
- } else if (source === 2 /* ACK */) {
2937
- return this._applySetChildKeyAck(newKey, child);
2997
+ _applyInsertAck(op) {
2998
+ const existingItem = this._items.find((item) => item._id === op.id);
2999
+ const key = asPos(op.parentKey);
3000
+ const itemIndexAtPosition = this._indexOfPosition(key);
3001
+ if (existingItem) {
3002
+ if (existingItem._parentKey === key) {
3003
+ return {
3004
+ modified: false
3005
+ };
3006
+ } else {
3007
+ const oldPositionIndex = this._items.indexOf(existingItem);
3008
+ if (itemIndexAtPosition !== -1) {
3009
+ this._shiftItemPosition(itemIndexAtPosition, key);
3010
+ }
3011
+ existingItem._setParentLink(this, key);
3012
+ this._sortItems();
3013
+ const newIndex = this._indexOfPosition(key);
3014
+ if (newIndex === oldPositionIndex) {
3015
+ return { modified: false };
3016
+ }
3017
+ return {
3018
+ modified: makeUpdate(this, [
3019
+ moveDelta(oldPositionIndex, newIndex, existingItem)
3020
+ ]),
3021
+ reverse: []
3022
+ };
3023
+ }
2938
3024
  } else {
2939
- return this._applySetChildKeyUndoRedo(newKey, child);
3025
+ const orphan = nn(this._pool).getNode(op.id);
3026
+ if (orphan && this._implicitlyDeletedItems.has(orphan)) {
3027
+ orphan._setParentLink(this, key);
3028
+ this._implicitlyDeletedItems.delete(orphan);
3029
+ this._insertAndSort(orphan);
3030
+ const newIndex = this._indexOfPosition(key);
3031
+ return {
3032
+ modified: makeUpdate(this, [insertDelta(newIndex, orphan)]),
3033
+ reverse: []
3034
+ };
3035
+ } else {
3036
+ if (itemIndexAtPosition !== -1) {
3037
+ this._shiftItemPosition(itemIndexAtPosition, key);
3038
+ }
3039
+ const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
3040
+ return {
3041
+ modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
3042
+ reverse: []
3043
+ };
3044
+ }
2940
3045
  }
2941
3046
  }
2942
3047
  /** @internal */
2943
- _apply(op, isLocal) {
2944
- return super._apply(op, isLocal);
2945
- }
2946
- /** @internal */
2947
- _serialize() {
2948
- if (this.parent.type !== "HasParent") {
2949
- throw new Error("Cannot serialize LiveList if parent is missing");
3048
+ _applyInsertUndoRedo(op) {
3049
+ const { id, parentKey: key } = op;
3050
+ const child = creationOpToLiveNode(op);
3051
+ if (this._pool?.getNode(id) !== void 0) {
3052
+ return { modified: false };
3053
+ }
3054
+ child._attach(id, nn(this._pool));
3055
+ child._setParentLink(this, key);
3056
+ const existingItemIndex = this._indexOfPosition(key);
3057
+ let newKey = key;
3058
+ if (existingItemIndex !== -1) {
3059
+ const before2 = this._items[existingItemIndex]?._parentPos;
3060
+ const after2 = this._items[existingItemIndex + 1]?._parentPos;
3061
+ newKey = makePosition(before2, after2);
3062
+ child._setParentLink(this, newKey);
2950
3063
  }
3064
+ this._insertAndSort(child);
3065
+ const newIndex = this._indexOfPosition(newKey);
2951
3066
  return {
2952
- type: 1 /* LIST */,
2953
- parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
2954
- parentKey: this.parent.key
3067
+ modified: makeUpdate(this, [insertDelta(newIndex, child)]),
3068
+ reverse: [{ type: 5 /* DELETE_CRDT */, id }]
2955
3069
  };
2956
3070
  }
2957
- /**
2958
- * Returns the number of elements.
2959
- */
2960
- get length() {
2961
- return this._items.length;
2962
- }
2963
- /**
2964
- * Adds one element to the end of the LiveList.
2965
- * @param element The element to add to the end of the LiveList.
2966
- */
2967
- push(element) {
2968
- this._pool?.assertStorageIsWritable();
2969
- return this.insert(element, this.length);
2970
- }
2971
- /**
2972
- * Inserts one element at a specified index.
2973
- * @param element The element to insert.
2974
- * @param index The index at which you want to insert the element.
2975
- */
2976
- insert(element, index) {
2977
- this._pool?.assertStorageIsWritable();
2978
- if (index < 0 || index > this._items.length) {
2979
- throw new Error(
2980
- `Cannot insert list item at index "${index}". index should be between 0 and ${this._items.length}`
2981
- );
3071
+ /** @internal */
3072
+ _applySetUndoRedo(op) {
3073
+ const { id, parentKey: key } = op;
3074
+ const child = creationOpToLiveNode(op);
3075
+ if (this._pool?.getNode(id) !== void 0) {
3076
+ return { modified: false };
2982
3077
  }
2983
- const before2 = this._items[index - 1] ? this._items[index - 1]._parentPos : void 0;
2984
- const after2 = this._items[index] ? this._items[index]._parentPos : void 0;
2985
- const position = makePosition(before2, after2);
2986
- const value = lsonToLiveNode(element);
2987
- value._setParentLink(this, position);
2988
- this._insertAndSort(value);
2989
- if (this._pool && this._id) {
2990
- const id = this._pool.generateId();
2991
- value._attach(id, this._pool);
2992
- this._pool.dispatch(
2993
- value._toOps(this._id, position, this._pool),
2994
- [{ type: 5 /* DELETE_CRDT */, id }],
2995
- /* @__PURE__ */ new Map([
2996
- [this._id, makeUpdate(this, [insertDelta(index, value)])]
2997
- ])
3078
+ this._unacknowledgedSets.set(key, nn(op.opId));
3079
+ const indexOfItemWithSameKey = this._indexOfPosition(key);
3080
+ child._attach(id, nn(this._pool));
3081
+ child._setParentLink(this, key);
3082
+ const newKey = key;
3083
+ if (indexOfItemWithSameKey !== -1) {
3084
+ const existingItem = this._items[indexOfItemWithSameKey];
3085
+ existingItem._detach();
3086
+ this._items[indexOfItemWithSameKey] = child;
3087
+ const reverse = HACK_addIntentAndDeletedIdToOperation(
3088
+ existingItem._toOps(nn(this._id), key, this._pool),
3089
+ op.id
2998
3090
  );
2999
- }
3000
- }
3001
- /**
3002
- * Move one element from one index to another.
3003
- * @param index The index of the element to move
3004
- * @param targetIndex The index where the element should be after moving.
3005
- */
3006
- move(index, targetIndex) {
3007
- this._pool?.assertStorageIsWritable();
3008
- if (targetIndex < 0) {
3009
- throw new Error("targetIndex cannot be less than 0");
3010
- }
3011
- if (targetIndex >= this._items.length) {
3012
- throw new Error(
3013
- "targetIndex cannot be greater or equal than the list length"
3091
+ const delta = [setDelta(indexOfItemWithSameKey, child)];
3092
+ const deletedDelta = this._detachItemAssociatedToSetOperation(
3093
+ op.deletedId
3014
3094
  );
3095
+ if (deletedDelta) {
3096
+ delta.push(deletedDelta);
3097
+ }
3098
+ return {
3099
+ modified: makeUpdate(this, delta),
3100
+ reverse
3101
+ };
3102
+ } else {
3103
+ this._insertAndSort(child);
3104
+ this._detachItemAssociatedToSetOperation(op.deletedId);
3105
+ const newIndex = this._indexOfPosition(newKey);
3106
+ return {
3107
+ reverse: [{ type: 5 /* DELETE_CRDT */, id }],
3108
+ modified: makeUpdate(this, [insertDelta(newIndex, child)])
3109
+ };
3015
3110
  }
3016
- if (index < 0) {
3017
- throw new Error("index cannot be less than 0");
3018
- }
3019
- if (index >= this._items.length) {
3020
- throw new Error("index cannot be greater or equal than the list length");
3111
+ }
3112
+ /** @internal */
3113
+ _attachChild(op, source) {
3114
+ if (this._pool === void 0) {
3115
+ throw new Error("Can't attach child if managed pool is not present");
3021
3116
  }
3022
- let beforePosition = null;
3023
- let afterPosition = null;
3024
- if (index < targetIndex) {
3025
- afterPosition = targetIndex === this._items.length - 1 ? void 0 : this._items[targetIndex + 1]._parentPos;
3026
- beforePosition = this._items[targetIndex]._parentPos;
3117
+ let result;
3118
+ if (op.intent === "set") {
3119
+ if (source === 1 /* REMOTE */) {
3120
+ result = this._applySetRemote(op);
3121
+ } else if (source === 2 /* ACK */) {
3122
+ result = this._applySetAck(op);
3123
+ } else {
3124
+ result = this._applySetUndoRedo(op);
3125
+ }
3027
3126
  } else {
3028
- afterPosition = this._items[targetIndex]._parentPos;
3029
- beforePosition = targetIndex === 0 ? void 0 : this._items[targetIndex - 1]._parentPos;
3127
+ if (source === 1 /* REMOTE */) {
3128
+ result = this._applyRemoteInsert(op);
3129
+ } else if (source === 2 /* ACK */) {
3130
+ result = this._applyInsertAck(op);
3131
+ } else {
3132
+ result = this._applyInsertUndoRedo(op);
3133
+ }
3030
3134
  }
3031
- const position = makePosition(beforePosition, afterPosition);
3032
- const item = this._items[index];
3033
- const previousPosition = item._getParentKeyOrThrow();
3034
- item._setParentLink(this, position);
3035
- this._sortItems();
3036
- if (this._pool && this._id) {
3037
- const storageUpdates = /* @__PURE__ */ new Map([
3038
- [this._id, makeUpdate(this, [moveDelta(index, targetIndex, item)])]
3039
- ]);
3040
- this._pool.dispatch(
3041
- [
3042
- {
3043
- type: 1 /* SET_PARENT_KEY */,
3044
- id: nn(item._id),
3045
- opId: this._pool.generateOpId(),
3046
- parentKey: position
3047
- }
3048
- ],
3049
- [
3050
- {
3051
- type: 1 /* SET_PARENT_KEY */,
3052
- id: nn(item._id),
3053
- parentKey: previousPosition
3054
- }
3055
- ],
3056
- storageUpdates
3057
- );
3135
+ if (result.modified !== false) {
3136
+ this.invalidate();
3058
3137
  }
3138
+ return result;
3059
3139
  }
3060
- /**
3061
- * Deletes an element at the specified index
3062
- * @param index The index of the element to delete
3063
- */
3064
- delete(index) {
3065
- this._pool?.assertStorageIsWritable();
3066
- if (index < 0 || index >= this._items.length) {
3067
- throw new Error(
3068
- `Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
3140
+ /** @internal */
3141
+ _detachChild(child) {
3142
+ if (child) {
3143
+ const parentKey = nn(child._parentKey);
3144
+ const reverse = child._toOps(nn(this._id), parentKey, this._pool);
3145
+ const indexToDelete = this._items.indexOf(child);
3146
+ if (indexToDelete === -1) {
3147
+ return {
3148
+ modified: false
3149
+ };
3150
+ }
3151
+ this._items.splice(indexToDelete, 1);
3152
+ this.invalidate();
3153
+ child._detach();
3154
+ return {
3155
+ modified: makeUpdate(this, [deleteDelta(indexToDelete)]),
3156
+ reverse
3157
+ };
3158
+ }
3159
+ return { modified: false };
3160
+ }
3161
+ /** @internal */
3162
+ _applySetChildKeyRemote(newKey, child) {
3163
+ if (this._implicitlyDeletedItems.has(child)) {
3164
+ this._implicitlyDeletedItems.delete(child);
3165
+ child._setParentLink(this, newKey);
3166
+ this._insertAndSort(child);
3167
+ const newIndex = this._items.indexOf(child);
3168
+ return {
3169
+ modified: makeUpdate(this, [insertDelta(newIndex, child)]),
3170
+ reverse: []
3171
+ };
3172
+ }
3173
+ const previousKey = child._parentKey;
3174
+ if (newKey === previousKey) {
3175
+ return {
3176
+ modified: false
3177
+ };
3178
+ }
3179
+ const existingItemIndex = this._indexOfPosition(newKey);
3180
+ if (existingItemIndex === -1) {
3181
+ const previousIndex = this._items.indexOf(child);
3182
+ child._setParentLink(this, newKey);
3183
+ this._sortItems();
3184
+ const newIndex = this._items.indexOf(child);
3185
+ if (newIndex === previousIndex) {
3186
+ return {
3187
+ modified: false
3188
+ };
3189
+ }
3190
+ return {
3191
+ modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
3192
+ reverse: []
3193
+ };
3194
+ } else {
3195
+ this._items[existingItemIndex]._setParentLink(
3196
+ this,
3197
+ makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
3069
3198
  );
3199
+ const previousIndex = this._items.indexOf(child);
3200
+ child._setParentLink(this, newKey);
3201
+ this._sortItems();
3202
+ const newIndex = this._items.indexOf(child);
3203
+ if (newIndex === previousIndex) {
3204
+ return {
3205
+ modified: false
3206
+ };
3207
+ }
3208
+ return {
3209
+ modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
3210
+ reverse: []
3211
+ };
3070
3212
  }
3071
- const item = this._items[index];
3072
- item._detach();
3073
- this._items.splice(index, 1);
3074
- this.invalidate();
3075
- if (this._pool) {
3076
- const childRecordId = item._id;
3077
- if (childRecordId) {
3078
- const storageUpdates = /* @__PURE__ */ new Map();
3079
- storageUpdates.set(
3080
- nn(this._id),
3081
- makeUpdate(this, [deleteDelta(index)])
3213
+ }
3214
+ /** @internal */
3215
+ _applySetChildKeyAck(newKey, child) {
3216
+ const previousKey = nn(child._parentKey);
3217
+ if (this._implicitlyDeletedItems.has(child)) {
3218
+ const existingItemIndex = this._indexOfPosition(newKey);
3219
+ this._implicitlyDeletedItems.delete(child);
3220
+ if (existingItemIndex !== -1) {
3221
+ this._items[existingItemIndex]._setParentLink(
3222
+ this,
3223
+ makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
3082
3224
  );
3083
- this._pool.dispatch(
3084
- [
3085
- {
3086
- id: childRecordId,
3087
- opId: this._pool.generateOpId(),
3088
- type: 5 /* DELETE_CRDT */
3089
- }
3090
- ],
3091
- item._toOps(nn(this._id), item._getParentKeyOrThrow()),
3092
- storageUpdates
3225
+ }
3226
+ child._setParentLink(this, newKey);
3227
+ this._insertAndSort(child);
3228
+ return {
3229
+ modified: false
3230
+ };
3231
+ } else {
3232
+ if (newKey === previousKey) {
3233
+ return {
3234
+ modified: false
3235
+ };
3236
+ }
3237
+ const previousIndex = this._items.indexOf(child);
3238
+ const existingItemIndex = this._indexOfPosition(newKey);
3239
+ if (existingItemIndex !== -1) {
3240
+ this._items[existingItemIndex]._setParentLink(
3241
+ this,
3242
+ makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
3093
3243
  );
3094
3244
  }
3245
+ child._setParentLink(this, newKey);
3246
+ this._sortItems();
3247
+ const newIndex = this._items.indexOf(child);
3248
+ if (previousIndex === newIndex) {
3249
+ return {
3250
+ modified: false
3251
+ };
3252
+ } else {
3253
+ return {
3254
+ modified: makeUpdate(this, [
3255
+ moveDelta(previousIndex, newIndex, child)
3256
+ ]),
3257
+ reverse: []
3258
+ };
3259
+ }
3095
3260
  }
3096
3261
  }
3097
- clear() {
3098
- this._pool?.assertStorageIsWritable();
3099
- if (this._pool) {
3100
- const ops = [];
3101
- const reverseOps = [];
3102
- const updateDelta = [];
3103
- for (const item of this._items) {
3104
- item._detach();
3105
- const childId = item._id;
3106
- if (childId) {
3107
- ops.push({
3108
- type: 5 /* DELETE_CRDT */,
3109
- id: childId,
3110
- opId: this._pool.generateOpId()
3111
- });
3112
- reverseOps.push(
3113
- ...item._toOps(nn(this._id), item._getParentKeyOrThrow())
3114
- );
3115
- updateDelta.push(deleteDelta(0));
3262
+ /** @internal */
3263
+ _applySetChildKeyUndoRedo(newKey, child) {
3264
+ const previousKey = nn(child._parentKey);
3265
+ const previousIndex = this._items.indexOf(child);
3266
+ const existingItemIndex = this._indexOfPosition(newKey);
3267
+ if (existingItemIndex !== -1) {
3268
+ this._items[existingItemIndex]._setParentLink(
3269
+ this,
3270
+ makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
3271
+ );
3272
+ }
3273
+ child._setParentLink(this, newKey);
3274
+ this._sortItems();
3275
+ const newIndex = this._items.indexOf(child);
3276
+ if (previousIndex === newIndex) {
3277
+ return {
3278
+ modified: false
3279
+ };
3280
+ }
3281
+ return {
3282
+ modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
3283
+ reverse: [
3284
+ {
3285
+ type: 1 /* SET_PARENT_KEY */,
3286
+ id: nn(child._id),
3287
+ parentKey: previousKey
3116
3288
  }
3117
- }
3118
- this._items = [];
3119
- this.invalidate();
3120
- const storageUpdates = /* @__PURE__ */ new Map();
3121
- storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta));
3122
- this._pool.dispatch(ops, reverseOps, storageUpdates);
3289
+ ]
3290
+ };
3291
+ }
3292
+ /** @internal */
3293
+ _setChildKey(newKey, child, source) {
3294
+ if (source === 1 /* REMOTE */) {
3295
+ return this._applySetChildKeyRemote(newKey, child);
3296
+ } else if (source === 2 /* ACK */) {
3297
+ return this._applySetChildKeyAck(newKey, child);
3123
3298
  } else {
3124
- for (const item of this._items) {
3125
- item._detach();
3126
- }
3127
- this._items = [];
3128
- this.invalidate();
3299
+ return this._applySetChildKeyUndoRedo(newKey, child);
3129
3300
  }
3130
3301
  }
3131
- set(index, item) {
3302
+ /** @internal */
3303
+ _apply(op, isLocal) {
3304
+ return super._apply(op, isLocal);
3305
+ }
3306
+ /** @internal */
3307
+ _serialize() {
3308
+ if (this.parent.type !== "HasParent") {
3309
+ throw new Error("Cannot serialize LiveList if parent is missing");
3310
+ }
3311
+ return {
3312
+ type: 1 /* LIST */,
3313
+ parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
3314
+ parentKey: this.parent.key
3315
+ };
3316
+ }
3317
+ /**
3318
+ * Returns the number of elements.
3319
+ */
3320
+ get length() {
3321
+ return this._items.length;
3322
+ }
3323
+ /**
3324
+ * Adds one element to the end of the LiveList.
3325
+ * @param element The element to add to the end of the LiveList.
3326
+ */
3327
+ push(element) {
3132
3328
  this._pool?.assertStorageIsWritable();
3133
- if (index < 0 || index >= this._items.length) {
3329
+ return this.insert(element, this.length);
3330
+ }
3331
+ /**
3332
+ * Inserts one element at a specified index.
3333
+ * @param element The element to insert.
3334
+ * @param index The index at which you want to insert the element.
3335
+ */
3336
+ insert(element, index) {
3337
+ this._pool?.assertStorageIsWritable();
3338
+ if (index < 0 || index > this._items.length) {
3134
3339
  throw new Error(
3135
- `Cannot set list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
3340
+ `Cannot insert list item at index "${index}". index should be between 0 and ${this._items.length}`
3136
3341
  );
3137
3342
  }
3138
- const existingItem = this._items[index];
3139
- const position = existingItem._getParentKeyOrThrow();
3140
- const existingId = existingItem._id;
3141
- existingItem._detach();
3142
- const value = lsonToLiveNode(item);
3343
+ const before2 = this._items[index - 1] ? this._items[index - 1]._parentPos : void 0;
3344
+ const after2 = this._items[index] ? this._items[index]._parentPos : void 0;
3345
+ const position = makePosition(before2, after2);
3346
+ const value = lsonToLiveNode(element);
3143
3347
  value._setParentLink(this, position);
3144
- this._items[index] = value;
3145
- this.invalidate();
3348
+ this._insertAndSort(value);
3349
+ if (this._pool && this._id) {
3350
+ const id = this._pool.generateId();
3351
+ value._attach(id, this._pool);
3352
+ this._pool.dispatch(
3353
+ value._toOps(this._id, position, this._pool),
3354
+ [{ type: 5 /* DELETE_CRDT */, id }],
3355
+ /* @__PURE__ */ new Map([
3356
+ [this._id, makeUpdate(this, [insertDelta(index, value)])]
3357
+ ])
3358
+ );
3359
+ }
3360
+ }
3361
+ /**
3362
+ * Move one element from one index to another.
3363
+ * @param index The index of the element to move
3364
+ * @param targetIndex The index where the element should be after moving.
3365
+ */
3366
+ move(index, targetIndex) {
3367
+ this._pool?.assertStorageIsWritable();
3368
+ if (targetIndex < 0) {
3369
+ throw new Error("targetIndex cannot be less than 0");
3370
+ }
3371
+ if (targetIndex >= this._items.length) {
3372
+ throw new Error(
3373
+ "targetIndex cannot be greater or equal than the list length"
3374
+ );
3375
+ }
3376
+ if (index < 0) {
3377
+ throw new Error("index cannot be less than 0");
3378
+ }
3379
+ if (index >= this._items.length) {
3380
+ throw new Error("index cannot be greater or equal than the list length");
3381
+ }
3382
+ let beforePosition = null;
3383
+ let afterPosition = null;
3384
+ if (index < targetIndex) {
3385
+ afterPosition = targetIndex === this._items.length - 1 ? void 0 : this._items[targetIndex + 1]._parentPos;
3386
+ beforePosition = this._items[targetIndex]._parentPos;
3387
+ } else {
3388
+ afterPosition = this._items[targetIndex]._parentPos;
3389
+ beforePosition = targetIndex === 0 ? void 0 : this._items[targetIndex - 1]._parentPos;
3390
+ }
3391
+ const position = makePosition(beforePosition, afterPosition);
3392
+ const item = this._items[index];
3393
+ const previousPosition = item._getParentKeyOrThrow();
3394
+ item._setParentLink(this, position);
3395
+ this._sortItems();
3396
+ if (this._pool && this._id) {
3397
+ const storageUpdates = /* @__PURE__ */ new Map([
3398
+ [this._id, makeUpdate(this, [moveDelta(index, targetIndex, item)])]
3399
+ ]);
3400
+ this._pool.dispatch(
3401
+ [
3402
+ {
3403
+ type: 1 /* SET_PARENT_KEY */,
3404
+ id: nn(item._id),
3405
+ opId: this._pool.generateOpId(),
3406
+ parentKey: position
3407
+ }
3408
+ ],
3409
+ [
3410
+ {
3411
+ type: 1 /* SET_PARENT_KEY */,
3412
+ id: nn(item._id),
3413
+ parentKey: previousPosition
3414
+ }
3415
+ ],
3416
+ storageUpdates
3417
+ );
3418
+ }
3419
+ }
3420
+ /**
3421
+ * Deletes an element at the specified index
3422
+ * @param index The index of the element to delete
3423
+ */
3424
+ delete(index) {
3425
+ this._pool?.assertStorageIsWritable();
3426
+ if (index < 0 || index >= this._items.length) {
3427
+ throw new Error(
3428
+ `Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
3429
+ );
3430
+ }
3431
+ const item = this._items[index];
3432
+ item._detach();
3433
+ this._items.splice(index, 1);
3434
+ this.invalidate();
3435
+ if (this._pool) {
3436
+ const childRecordId = item._id;
3437
+ if (childRecordId) {
3438
+ const storageUpdates = /* @__PURE__ */ new Map();
3439
+ storageUpdates.set(
3440
+ nn(this._id),
3441
+ makeUpdate(this, [deleteDelta(index)])
3442
+ );
3443
+ this._pool.dispatch(
3444
+ [
3445
+ {
3446
+ id: childRecordId,
3447
+ opId: this._pool.generateOpId(),
3448
+ type: 5 /* DELETE_CRDT */
3449
+ }
3450
+ ],
3451
+ item._toOps(nn(this._id), item._getParentKeyOrThrow()),
3452
+ storageUpdates
3453
+ );
3454
+ }
3455
+ }
3456
+ }
3457
+ clear() {
3458
+ this._pool?.assertStorageIsWritable();
3459
+ if (this._pool) {
3460
+ const ops = [];
3461
+ const reverseOps = [];
3462
+ const updateDelta = [];
3463
+ for (const item of this._items) {
3464
+ item._detach();
3465
+ const childId = item._id;
3466
+ if (childId) {
3467
+ ops.push({
3468
+ type: 5 /* DELETE_CRDT */,
3469
+ id: childId,
3470
+ opId: this._pool.generateOpId()
3471
+ });
3472
+ reverseOps.push(
3473
+ ...item._toOps(nn(this._id), item._getParentKeyOrThrow())
3474
+ );
3475
+ updateDelta.push(deleteDelta(0));
3476
+ }
3477
+ }
3478
+ this._items = [];
3479
+ this.invalidate();
3480
+ const storageUpdates = /* @__PURE__ */ new Map();
3481
+ storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta));
3482
+ this._pool.dispatch(ops, reverseOps, storageUpdates);
3483
+ } else {
3484
+ for (const item of this._items) {
3485
+ item._detach();
3486
+ }
3487
+ this._items = [];
3488
+ this.invalidate();
3489
+ }
3490
+ }
3491
+ set(index, item) {
3492
+ this._pool?.assertStorageIsWritable();
3493
+ if (index < 0 || index >= this._items.length) {
3494
+ throw new Error(
3495
+ `Cannot set list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
3496
+ );
3497
+ }
3498
+ const existingItem = this._items[index];
3499
+ const position = existingItem._getParentKeyOrThrow();
3500
+ const existingId = existingItem._id;
3501
+ existingItem._detach();
3502
+ const value = lsonToLiveNode(item);
3503
+ value._setParentLink(this, position);
3504
+ this._items[index] = value;
3505
+ this.invalidate();
3146
3506
  if (this._pool && this._id) {
3147
3507
  const id = this._pool.generateId();
3148
3508
  value._attach(id, this._pool);
@@ -4995,9 +5355,13 @@ function createRoom(options, config) {
4995
5355
  ydoc: makeEventSource(),
4996
5356
  comments: makeEventSource()
4997
5357
  };
4998
- async function httpSend(authTokenOrPublicApiKey, roomId, nonce, messages) {
5358
+ async function httpPostToRoom(endpoint, body) {
5359
+ if (!managedSocket.authValue) {
5360
+ throw new Error("Not authorized");
5361
+ }
5362
+ const authTokenOrPublicApiKey = managedSocket.authValue.type === "public" ? managedSocket.authValue.publicApiKey : managedSocket.authValue.token.raw;
4999
5363
  const url = new URL(
5000
- `/v2/c/rooms/${encodeURIComponent(roomId)}/send-message`,
5364
+ `/v2/c/rooms/${encodeURIComponent(config.roomId)}${endpoint}`,
5001
5365
  config.baseUrl
5002
5366
  ).toString();
5003
5367
  const fetcher = config.polyfills?.fetch || /* istanbul ignore next */
@@ -5008,25 +5372,22 @@ function createRoom(options, config) {
5008
5372
  "Content-Type": "application/json",
5009
5373
  Authorization: `Bearer ${authTokenOrPublicApiKey}`
5010
5374
  },
5011
- body: JSON.stringify({ nonce, messages })
5375
+ body: JSON.stringify(body)
5012
5376
  });
5013
5377
  }
5014
5378
  function sendMessages(messages) {
5015
5379
  const serializedPayload = JSON.stringify(messages);
5016
5380
  const nonce = context.dynamicSessionInfo.current?.nonce;
5017
- if (config.unstable_fallbackToHTTP && managedSocket.authValue && nonce) {
5381
+ if (config.unstable_fallbackToHTTP && nonce) {
5018
5382
  const size = new TextEncoder().encode(serializedPayload).length;
5019
5383
  if (size > MAX_SOCKET_MESSAGE_SIZE) {
5020
- void httpSend(
5021
- managedSocket.authValue.type === "public" ? managedSocket.authValue.publicApiKey : managedSocket.authValue.token.raw,
5022
- config.roomId,
5023
- nonce,
5024
- messages
5025
- ).then((resp) => {
5026
- if (!resp.ok && resp.status === 403) {
5027
- managedSocket.reconnect();
5384
+ void httpPostToRoom("/send-message", { nonce, messages }).then(
5385
+ (resp) => {
5386
+ if (!resp.ok && resp.status === 403) {
5387
+ managedSocket.reconnect();
5388
+ }
5028
5389
  }
5029
- });
5390
+ );
5030
5391
  warn(
5031
5392
  "Message was too large for websockets and sent over HTTP instead"
5032
5393
  );
@@ -5539,7 +5900,7 @@ ${Array.from(traces).join("\n\n")}`
5539
5900
  }
5540
5901
  const now = Date.now();
5541
5902
  const elapsedMillis = now - context.buffer.lastFlushedAt;
5542
- if (elapsedMillis > config.throttleDelay) {
5903
+ if (elapsedMillis >= config.throttleDelay) {
5543
5904
  const messagesToFlush = serializeBuffer();
5544
5905
  if (messagesToFlush.length === 0) {
5545
5906
  return;
@@ -5906,1161 +6267,852 @@ function makeClassicSubscribeFn(events) {
5906
6267
  const cb = callback;
5907
6268
  return events.others.subscribe((event) => {
5908
6269
  const { others, ...internalEvent } = event;
5909
- return cb(others, internalEvent);
5910
- });
5911
- }
5912
- case "error":
5913
- return events.error.subscribe(callback);
5914
- case "connection": {
5915
- const cb = callback;
5916
- return events.status.subscribe(
5917
- (status) => cb(newToLegacyStatus(status))
5918
- );
5919
- }
5920
- case "status":
5921
- return events.status.subscribe(callback);
5922
- case "lost-connection":
5923
- return events.lostConnection.subscribe(
5924
- callback
5925
- );
5926
- case "history":
5927
- return events.history.subscribe(callback);
5928
- case "storage-status":
5929
- return events.storageStatus.subscribe(
5930
- callback
5931
- );
5932
- default:
5933
- return assertNever(
5934
- first,
5935
- `"${String(first)}" is not a valid event name`
5936
- );
5937
- }
5938
- }
5939
- if (second === void 0 || typeof first === "function") {
5940
- if (typeof first === "function") {
5941
- const storageCallback = first;
5942
- return events.storage.subscribe(storageCallback);
5943
- } else {
5944
- throw new Error("Please specify a listener callback");
5945
- }
5946
- }
5947
- if (isLiveNode(first)) {
5948
- const node = first;
5949
- if (options?.isDeep) {
5950
- const storageCallback = second;
5951
- return subscribeToLiveStructureDeeply(node, storageCallback);
5952
- } else {
5953
- const nodeCallback = second;
5954
- return subscribeToLiveStructureShallowly(node, nodeCallback);
5955
- }
5956
- }
5957
- throw new Error(
5958
- `${String(first)} is not a value that can be subscribed to.`
5959
- );
5960
- }
5961
- return subscribe;
5962
- }
5963
- function isRoomEventName(value) {
5964
- return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
5965
- }
5966
- function makeAuthDelegateForRoom(roomId, authManager) {
5967
- return async () => {
5968
- return authManager.getAuthValue("room:read", roomId);
5969
- };
5970
- }
5971
- function makeCreateSocketDelegateForRoom(roomId, baseUrl, WebSocketPolyfill) {
5972
- return (authValue) => {
5973
- const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
5974
- if (ws === void 0) {
5975
- throw new StopRetrying(
5976
- "To use Liveblocks client in a non-DOM environment, you need to provide a WebSocket polyfill."
5977
- );
5978
- }
5979
- const url = new URL(baseUrl);
5980
- url.protocol = url.protocol === "http:" ? "ws" : "wss";
5981
- url.pathname = "/v7";
5982
- url.searchParams.set("roomId", roomId);
5983
- if (authValue.type === "secret") {
5984
- url.searchParams.set("tok", authValue.token.raw);
5985
- } else if (authValue.type === "public") {
5986
- url.searchParams.set("pubkey", authValue.publicApiKey);
5987
- } else {
5988
- return assertNever(authValue, "Unhandled case");
5989
- }
5990
- url.searchParams.set("version", PKG_VERSION || "dev");
5991
- return new ws(url.toString());
5992
- };
5993
- }
5994
-
5995
- // src/client.ts
5996
- var MIN_THROTTLE = 16;
5997
- var MAX_THROTTLE = 1e3;
5998
- var DEFAULT_THROTTLE = 100;
5999
- var MIN_BACKGROUND_KEEP_ALIVE_TIMEOUT = 15e3;
6000
- var MIN_LOST_CONNECTION_TIMEOUT = 200;
6001
- var RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT = 1e3;
6002
- var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
6003
- var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
6004
- function getBaseUrlFromClientOptions(clientOptions) {
6005
- if ("liveblocksServer" in clientOptions) {
6006
- throw new Error("Client option no longer supported");
6007
- }
6008
- if (typeof clientOptions.baseUrl === "string" && clientOptions.baseUrl.startsWith("http")) {
6009
- return clientOptions.baseUrl;
6010
- } else {
6011
- return DEFAULT_BASE_URL;
6012
- }
6013
- }
6014
- function createClient(options) {
6015
- const clientOptions = options;
6016
- const throttleDelay = getThrottle(clientOptions.throttle ?? DEFAULT_THROTTLE);
6017
- const lostConnectionTimeout = getLostConnectionTimeout(
6018
- clientOptions.lostConnectionTimeout ?? DEFAULT_LOST_CONNECTION_TIMEOUT
6019
- );
6020
- const backgroundKeepAliveTimeout = getBackgroundKeepAliveTimeout(
6021
- clientOptions.backgroundKeepAliveTimeout
6022
- );
6023
- const authManager = createAuthManager(options);
6024
- const roomsById = /* @__PURE__ */ new Map();
6025
- function teardownRoom(room) {
6026
- unlinkDevTools(room.id);
6027
- roomsById.delete(room.id);
6028
- room.destroy();
6029
- }
6030
- function leaseRoom(info) {
6031
- const leave = () => {
6032
- const self = leave;
6033
- if (!info.unsubs.delete(self)) {
6034
- warn(
6035
- "This leave function was already called. Calling it more than once has no effect."
6036
- );
6037
- } else {
6038
- if (info.unsubs.size === 0) {
6039
- teardownRoom(info.room);
6040
- }
6041
- }
6042
- };
6043
- info.unsubs.add(leave);
6044
- return {
6045
- room: info.room,
6046
- leave
6047
- };
6048
- }
6049
- function enterRoom(roomId, options2) {
6050
- const existing = roomsById.get(roomId);
6051
- if (existing !== void 0) {
6052
- return leaseRoom(existing);
6053
- }
6054
- deprecateIf(
6055
- options2.initialPresence === null || options2.initialPresence === void 0,
6056
- "Please provide an initial presence value for the current user when entering the room."
6057
- );
6058
- const baseUrl = getBaseUrlFromClientOptions(clientOptions);
6059
- const newRoom = createRoom(
6060
- {
6061
- initialPresence: options2.initialPresence ?? {},
6062
- initialStorage: options2.initialStorage
6063
- },
6064
- {
6065
- roomId,
6066
- throttleDelay,
6067
- lostConnectionTimeout,
6068
- backgroundKeepAliveTimeout,
6069
- polyfills: clientOptions.polyfills,
6070
- delegates: clientOptions.mockedDelegates ?? {
6071
- createSocket: makeCreateSocketDelegateForRoom(
6072
- roomId,
6073
- baseUrl,
6074
- clientOptions.polyfills?.WebSocket
6075
- ),
6076
- authenticate: makeAuthDelegateForRoom(roomId, authManager)
6077
- },
6078
- enableDebugLogging: clientOptions.enableDebugLogging,
6079
- unstable_batchedUpdates: options2?.unstable_batchedUpdates,
6080
- baseUrl,
6081
- unstable_fallbackToHTTP: !!clientOptions.unstable_fallbackToHTTP
6082
- }
6083
- );
6084
- const newRoomInfo = {
6085
- room: newRoom,
6086
- unsubs: /* @__PURE__ */ new Set()
6087
- };
6088
- roomsById.set(roomId, newRoomInfo);
6089
- setupDevTools(() => Array.from(roomsById.keys()));
6090
- linkDevTools(roomId, newRoom);
6091
- const shouldConnect = options2.autoConnect ?? options2.shouldInitiallyConnect ?? true;
6092
- if (shouldConnect) {
6093
- if (typeof atob === "undefined") {
6094
- if (clientOptions.polyfills?.atob === void 0) {
6095
- throw new Error(
6096
- "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"
6097
- );
6098
- }
6099
- global.atob = clientOptions.polyfills.atob;
6100
- }
6101
- newRoom.connect();
6102
- }
6103
- return leaseRoom(newRoomInfo);
6104
- }
6105
- function enter(roomId, options2) {
6106
- const { room, leave: _ } = enterRoom(roomId, options2);
6107
- return room;
6108
- }
6109
- function getRoom(roomId) {
6110
- const room = roomsById.get(roomId)?.room;
6111
- return room ? room : null;
6112
- }
6113
- function forceLeave(roomId) {
6114
- const unsubs = roomsById.get(roomId)?.unsubs ?? /* @__PURE__ */ new Set();
6115
- for (const unsub of unsubs) {
6116
- unsub();
6117
- }
6118
- }
6119
- function logout() {
6120
- authManager.reset();
6121
- for (const { room } of roomsById.values()) {
6122
- if (!isIdle(room.getStatus())) {
6123
- room.reconnect();
6124
- }
6125
- }
6126
- }
6127
- return {
6128
- logout,
6129
- // Old, deprecated APIs
6130
- enter,
6131
- getRoom,
6132
- leave: forceLeave,
6133
- // New, preferred API
6134
- enterRoom
6135
- };
6136
- }
6137
- function checkBounds(option, value, min, max, recommendedMin) {
6138
- if (typeof value !== "number" || value < min || max !== void 0 && value > max) {
6139
- throw new Error(
6140
- max !== void 0 ? `${option} should be between ${recommendedMin ?? min} and ${max}.` : `${option} should be at least ${recommendedMin ?? min}.`
6141
- );
6142
- }
6143
- return value;
6144
- }
6145
- function getBackgroundKeepAliveTimeout(value) {
6146
- if (value === void 0)
6147
- return void 0;
6148
- return checkBounds(
6149
- "backgroundKeepAliveTimeout",
6150
- value,
6151
- MIN_BACKGROUND_KEEP_ALIVE_TIMEOUT
6152
- );
6153
- }
6154
- function getThrottle(value) {
6155
- return checkBounds("throttle", value, MIN_THROTTLE, MAX_THROTTLE);
6156
- }
6157
- function getLostConnectionTimeout(value) {
6158
- return checkBounds(
6159
- "lostConnectionTimeout",
6160
- value,
6161
- MIN_LOST_CONNECTION_TIMEOUT,
6162
- MAX_LOST_CONNECTION_TIMEOUT,
6163
- RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
6164
- );
6165
- }
6166
-
6167
- // src/crdts/utils.ts
6168
- function toPlainLson(lson) {
6169
- if (lson instanceof LiveObject) {
6170
- return {
6171
- liveblocksType: "LiveObject",
6172
- data: Object.fromEntries(
6173
- Object.entries(lson.toObject()).flatMap(
6174
- ([key, value]) => value !== void 0 ? [[key, toPlainLson(value)]] : []
6175
- )
6176
- )
6177
- };
6178
- } else if (lson instanceof LiveMap) {
6179
- return {
6180
- liveblocksType: "LiveMap",
6181
- data: Object.fromEntries(
6182
- [...lson].map(([key, value]) => [key, toPlainLson(value)])
6183
- )
6184
- };
6185
- } else if (lson instanceof LiveList) {
6186
- return {
6187
- liveblocksType: "LiveList",
6188
- data: [...lson].map((item) => toPlainLson(item))
6189
- };
6190
- } else {
6191
- return lson;
6192
- }
6193
- }
6194
-
6195
- // src/immutable.ts
6196
- function lsonObjectToJson(obj) {
6197
- const result = {};
6198
- for (const key in obj) {
6199
- const val = obj[key];
6200
- if (val !== void 0) {
6201
- result[key] = lsonToJson(val);
6202
- }
6203
- }
6204
- return result;
6205
- }
6206
- function liveObjectToJson(liveObject) {
6207
- return lsonObjectToJson(liveObject.toObject());
6208
- }
6209
- function liveMapToJson(map) {
6210
- const result = {};
6211
- for (const [key, value] of map.entries()) {
6212
- result[key] = lsonToJson(value);
6213
- }
6214
- return result;
6215
- }
6216
- function lsonListToJson(value) {
6217
- return value.map(lsonToJson);
6218
- }
6219
- function liveListToJson(value) {
6220
- return lsonListToJson(value.toArray());
6221
- }
6222
- function lsonToJson(value) {
6223
- if (value instanceof LiveObject) {
6224
- return liveObjectToJson(value);
6225
- } else if (value instanceof LiveList) {
6226
- return liveListToJson(value);
6227
- } else if (value instanceof LiveMap) {
6228
- return liveMapToJson(value);
6229
- } else if (value instanceof LiveRegister) {
6230
- return value.data;
6231
- }
6232
- if (Array.isArray(value)) {
6233
- return lsonListToJson(value);
6234
- } else if (isPlainObject(value)) {
6235
- return lsonObjectToJson(value);
6236
- }
6237
- return value;
6238
- }
6239
- function deepLiveify(value) {
6240
- if (Array.isArray(value)) {
6241
- return new LiveList(value.map(deepLiveify));
6242
- } else if (isPlainObject(value)) {
6243
- const init = {};
6244
- for (const key in value) {
6245
- const val = value[key];
6246
- if (val === void 0) {
6247
- continue;
6248
- }
6249
- init[key] = deepLiveify(val);
6250
- }
6251
- return new LiveObject(init);
6252
- } else {
6253
- return value;
6254
- }
6255
- }
6256
- function patchLiveList(liveList, prev, next) {
6257
- let i = 0;
6258
- let prevEnd = prev.length - 1;
6259
- let nextEnd = next.length - 1;
6260
- let prevNode = prev[0];
6261
- let nextNode = next[0];
6262
- outer: {
6263
- while (prevNode === nextNode) {
6264
- ++i;
6265
- if (i > prevEnd || i > nextEnd) {
6266
- break outer;
6267
- }
6268
- prevNode = prev[i];
6269
- nextNode = next[i];
6270
- }
6271
- prevNode = prev[prevEnd];
6272
- nextNode = next[nextEnd];
6273
- while (prevNode === nextNode) {
6274
- prevEnd--;
6275
- nextEnd--;
6276
- if (i > prevEnd || i > nextEnd) {
6277
- break outer;
6278
- }
6279
- prevNode = prev[prevEnd];
6280
- nextNode = next[nextEnd];
6281
- }
6282
- }
6283
- if (i > prevEnd) {
6284
- if (i <= nextEnd) {
6285
- while (i <= nextEnd) {
6286
- liveList.insert(deepLiveify(next[i]), i);
6287
- i++;
6288
- }
6289
- }
6290
- } else if (i > nextEnd) {
6291
- let localI = i;
6292
- while (localI <= prevEnd) {
6293
- liveList.delete(i);
6294
- localI++;
6295
- }
6296
- } else {
6297
- while (i <= prevEnd && i <= nextEnd) {
6298
- prevNode = prev[i];
6299
- nextNode = next[i];
6300
- const liveListNode = liveList.get(i);
6301
- if (isLiveObject(liveListNode) && isPlainObject(prevNode) && isPlainObject(nextNode)) {
6302
- patchLiveObject(liveListNode, prevNode, nextNode);
6303
- } else {
6304
- liveList.set(i, deepLiveify(nextNode));
6305
- }
6306
- i++;
6307
- }
6308
- while (i <= nextEnd) {
6309
- liveList.insert(deepLiveify(next[i]), i);
6310
- i++;
6311
- }
6312
- let localI = i;
6313
- while (localI <= prevEnd) {
6314
- liveList.delete(i);
6315
- localI++;
6316
- }
6317
- }
6318
- }
6319
- function patchLiveObjectKey(liveObject, key, prev, next) {
6320
- if (process.env.NODE_ENV !== "production") {
6321
- const nonSerializableValue = findNonSerializableValue(next);
6322
- if (nonSerializableValue) {
6323
- error2(
6324
- `New state path: '${nonSerializableValue.path}' value: '${String(
6325
- nonSerializableValue.value
6326
- )}' is not serializable.
6327
- Only serializable value can be synced with Liveblocks.`
6328
- );
6329
- return;
6330
- }
6331
- }
6332
- const value = liveObject.get(key);
6333
- if (next === void 0) {
6334
- liveObject.delete(key);
6335
- } else if (value === void 0) {
6336
- liveObject.set(key, deepLiveify(next));
6337
- } else if (prev === next) {
6338
- return;
6339
- } else if (isLiveList(value) && Array.isArray(prev) && Array.isArray(next)) {
6340
- patchLiveList(value, prev, next);
6341
- } else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
6342
- patchLiveObject(value, prev, next);
6343
- } else {
6344
- liveObject.set(key, deepLiveify(next));
6345
- }
6346
- }
6347
- function patchLiveObject(root, prev, next) {
6348
- const updates = {};
6349
- for (const key in next) {
6350
- patchLiveObjectKey(root, key, prev[key], next[key]);
6351
- }
6352
- for (const key in prev) {
6353
- if (next[key] === void 0) {
6354
- root.delete(key);
6355
- }
6356
- }
6357
- if (Object.keys(updates).length > 0) {
6358
- root.update(updates);
6359
- }
6360
- }
6361
- function getParentsPath(node) {
6362
- const path = [];
6363
- while (node.parent.type === "HasParent") {
6364
- if (isLiveList(node.parent.node)) {
6365
- path.push(node.parent.node._indexOfPosition(node.parent.key));
6366
- } else {
6367
- path.push(node.parent.key);
6368
- }
6369
- node = node.parent.node;
6370
- }
6371
- return path;
6372
- }
6373
- function legacy_patchImmutableObject(state, updates) {
6374
- return updates.reduce(
6375
- (state2, update) => legacy_patchImmutableObjectWithUpdate(state2, update),
6376
- state
6377
- );
6378
- }
6379
- function legacy_patchImmutableObjectWithUpdate(state, update) {
6380
- const path = getParentsPath(update.node);
6381
- return legacy_patchImmutableNode(state, path, update);
6382
- }
6383
- function legacy_patchImmutableNode(state, path, update) {
6384
- const pathItem = path.pop();
6385
- if (pathItem === void 0) {
6386
- switch (update.type) {
6387
- case "LiveObject": {
6388
- if (!isJsonObject(state)) {
6389
- throw new Error(
6390
- "Internal: received update on LiveObject but state was not an object"
6391
- );
6392
- }
6393
- const newState = Object.assign({}, state);
6394
- for (const key in update.updates) {
6395
- if (update.updates[key]?.type === "update") {
6396
- const val = update.node.get(key);
6397
- if (val !== void 0) {
6398
- newState[key] = lsonToJson(val);
6399
- }
6400
- } else if (update.updates[key]?.type === "delete") {
6401
- delete newState[key];
6402
- }
6403
- }
6404
- return newState;
6405
- }
6406
- case "LiveList": {
6407
- if (!Array.isArray(state)) {
6408
- throw new Error(
6409
- "Internal: received update on LiveList but state was not an array"
6410
- );
6411
- }
6412
- let newState = state.map((x) => x);
6413
- for (const listUpdate of update.updates) {
6414
- if (listUpdate.type === "set") {
6415
- newState = newState.map(
6416
- (item, index) => index === listUpdate.index ? lsonToJson(listUpdate.item) : item
6417
- );
6418
- } else if (listUpdate.type === "insert") {
6419
- if (listUpdate.index === newState.length) {
6420
- newState.push(lsonToJson(listUpdate.item));
6421
- } else {
6422
- newState = [
6423
- ...newState.slice(0, listUpdate.index),
6424
- lsonToJson(listUpdate.item),
6425
- ...newState.slice(listUpdate.index)
6426
- ];
6427
- }
6428
- } else if (listUpdate.type === "delete") {
6429
- newState.splice(listUpdate.index, 1);
6430
- } else if (listUpdate.type === "move") {
6431
- if (listUpdate.previousIndex > listUpdate.index) {
6432
- newState = [
6433
- ...newState.slice(0, listUpdate.index),
6434
- lsonToJson(listUpdate.item),
6435
- ...newState.slice(listUpdate.index, listUpdate.previousIndex),
6436
- ...newState.slice(listUpdate.previousIndex + 1)
6437
- ];
6438
- } else {
6439
- newState = [
6440
- ...newState.slice(0, listUpdate.previousIndex),
6441
- ...newState.slice(
6442
- listUpdate.previousIndex + 1,
6443
- listUpdate.index + 1
6444
- ),
6445
- lsonToJson(listUpdate.item),
6446
- ...newState.slice(listUpdate.index + 1)
6447
- ];
6448
- }
6449
- }
6450
- }
6451
- return newState;
6452
- }
6453
- case "LiveMap": {
6454
- if (!isJsonObject(state)) {
6455
- throw new Error(
6456
- "Internal: received update on LiveMap but state was not an object"
6457
- );
6270
+ return cb(others, internalEvent);
6271
+ });
6458
6272
  }
6459
- const newState = Object.assign({}, state);
6460
- for (const key in update.updates) {
6461
- if (update.updates[key]?.type === "update") {
6462
- const value = update.node.get(key);
6463
- if (value !== void 0) {
6464
- newState[key] = lsonToJson(value);
6465
- }
6466
- } else if (update.updates[key]?.type === "delete") {
6467
- delete newState[key];
6468
- }
6273
+ case "error":
6274
+ return events.error.subscribe(callback);
6275
+ case "connection": {
6276
+ const cb = callback;
6277
+ return events.status.subscribe(
6278
+ (status) => cb(newToLegacyStatus(status))
6279
+ );
6469
6280
  }
6470
- return newState;
6281
+ case "status":
6282
+ return events.status.subscribe(callback);
6283
+ case "lost-connection":
6284
+ return events.lostConnection.subscribe(
6285
+ callback
6286
+ );
6287
+ case "history":
6288
+ return events.history.subscribe(callback);
6289
+ case "storage-status":
6290
+ return events.storageStatus.subscribe(
6291
+ callback
6292
+ );
6293
+ default:
6294
+ return assertNever(
6295
+ first,
6296
+ `"${String(first)}" is not a valid event name`
6297
+ );
6471
6298
  }
6472
6299
  }
6473
- }
6474
- if (Array.isArray(state)) {
6475
- const newArray = [...state];
6476
- newArray[pathItem] = legacy_patchImmutableNode(
6477
- state[pathItem],
6478
- path,
6479
- update
6480
- );
6481
- return newArray;
6482
- } else if (isJsonObject(state)) {
6483
- const node = state[pathItem];
6484
- if (node === void 0) {
6485
- return state;
6486
- } else {
6487
- const stateAsObj = state;
6488
- return {
6489
- ...stateAsObj,
6490
- [pathItem]: legacy_patchImmutableNode(node, path, update)
6491
- };
6300
+ if (second === void 0 || typeof first === "function") {
6301
+ if (typeof first === "function") {
6302
+ const storageCallback = first;
6303
+ return events.storage.subscribe(storageCallback);
6304
+ } else {
6305
+ throw new Error("Please specify a listener callback");
6306
+ }
6492
6307
  }
6493
- } else {
6494
- return state;
6495
- }
6496
- }
6497
-
6498
- // src/lib/shallow.ts
6499
- function shallowArray(xs, ys) {
6500
- if (xs.length !== ys.length) {
6501
- return false;
6502
- }
6503
- for (let i = 0; i < xs.length; i++) {
6504
- if (!Object.is(xs[i], ys[i])) {
6505
- return false;
6308
+ if (isLiveNode(first)) {
6309
+ const node = first;
6310
+ if (options?.isDeep) {
6311
+ const storageCallback = second;
6312
+ return subscribeToLiveStructureDeeply(node, storageCallback);
6313
+ } else {
6314
+ const nodeCallback = second;
6315
+ return subscribeToLiveStructureShallowly(node, nodeCallback);
6316
+ }
6506
6317
  }
6318
+ throw new Error(
6319
+ `${String(first)} is not a value that can be subscribed to.`
6320
+ );
6507
6321
  }
6508
- return true;
6322
+ return subscribe;
6509
6323
  }
6510
- function shallowObj(objA, objB) {
6511
- if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null || Object.prototype.toString.call(objA) !== "[object Object]" || Object.prototype.toString.call(objB) !== "[object Object]") {
6512
- return false;
6513
- }
6514
- const keysA = Object.keys(objA);
6515
- if (keysA.length !== Object.keys(objB).length) {
6516
- return false;
6517
- }
6518
- return keysA.every(
6519
- (key) => Object.prototype.hasOwnProperty.call(objB, key) && Object.is(objA[key], objB[key])
6520
- );
6324
+ function isRoomEventName(value) {
6325
+ return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
6521
6326
  }
6522
- function shallow(a, b) {
6523
- if (Object.is(a, b)) {
6524
- return true;
6525
- }
6526
- const isArrayA = Array.isArray(a);
6527
- const isArrayB = Array.isArray(b);
6528
- if (isArrayA || isArrayB) {
6529
- if (!isArrayA || !isArrayB) {
6530
- return false;
6327
+ function makeAuthDelegateForRoom(roomId, authManager) {
6328
+ return async () => {
6329
+ return authManager.getAuthValue("room:read", roomId);
6330
+ };
6331
+ }
6332
+ function makeCreateSocketDelegateForRoom(roomId, baseUrl, WebSocketPolyfill) {
6333
+ return (authValue) => {
6334
+ const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
6335
+ if (ws === void 0) {
6336
+ throw new StopRetrying(
6337
+ "To use Liveblocks client in a non-DOM environment, you need to provide a WebSocket polyfill."
6338
+ );
6531
6339
  }
6532
- return shallowArray(a, b);
6533
- }
6534
- return shallowObj(a, b);
6340
+ const url = new URL(baseUrl);
6341
+ url.protocol = url.protocol === "http:" ? "ws" : "wss";
6342
+ url.pathname = "/v7";
6343
+ url.searchParams.set("roomId", roomId);
6344
+ if (authValue.type === "secret") {
6345
+ url.searchParams.set("tok", authValue.token.raw);
6346
+ } else if (authValue.type === "public") {
6347
+ url.searchParams.set("pubkey", authValue.publicApiKey);
6348
+ } else {
6349
+ return assertNever(authValue, "Unhandled case");
6350
+ }
6351
+ url.searchParams.set("version", PKG_VERSION || "dev");
6352
+ return new ws(url.toString());
6353
+ };
6535
6354
  }
6536
6355
 
6537
- // src/lib/AsyncCache.ts
6538
- var noop = () => {
6539
- };
6540
- function isShallowEqual(a, b) {
6541
- if (a.isLoading !== b.isLoading || a.data === void 0 !== (b.data === void 0) || a.error === void 0 !== (b.error === void 0)) {
6542
- return false;
6356
+ // src/client.ts
6357
+ var MIN_THROTTLE = 16;
6358
+ var MAX_THROTTLE = 1e3;
6359
+ var DEFAULT_THROTTLE = 100;
6360
+ var MIN_BACKGROUND_KEEP_ALIVE_TIMEOUT = 15e3;
6361
+ var MIN_LOST_CONNECTION_TIMEOUT = 200;
6362
+ var RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT = 1e3;
6363
+ var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
6364
+ var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
6365
+ function getBaseUrlFromClientOptions(clientOptions) {
6366
+ if ("liveblocksServer" in clientOptions) {
6367
+ throw new Error("Client option no longer supported");
6368
+ }
6369
+ if (typeof clientOptions.baseUrl === "string" && clientOptions.baseUrl.startsWith("http")) {
6370
+ return clientOptions.baseUrl;
6543
6371
  } else {
6544
- return shallow(a.data, b.data) && shallow(a.error, b.error);
6372
+ return DEFAULT_BASE_URL;
6545
6373
  }
6546
6374
  }
6547
- function createCacheItem(key, asyncFunction, options) {
6548
- const $asyncFunction = async () => asyncFunction(key);
6549
- const context = {
6550
- isInvalid: true
6551
- };
6552
- let state = { isLoading: false };
6553
- let previousState = { isLoading: false };
6554
- const eventSource2 = makeEventSource();
6555
- function notify() {
6556
- const isEqual = options?.isStateEqual ?? isShallowEqual;
6557
- if (!isEqual(previousState, state)) {
6558
- previousState = state;
6559
- eventSource2.notify(state);
6375
+ function createClient(options) {
6376
+ const clientOptions = options;
6377
+ const throttleDelay = getThrottle(clientOptions.throttle ?? DEFAULT_THROTTLE);
6378
+ const lostConnectionTimeout = getLostConnectionTimeout(
6379
+ clientOptions.lostConnectionTimeout ?? DEFAULT_LOST_CONNECTION_TIMEOUT
6380
+ );
6381
+ const backgroundKeepAliveTimeout = getBackgroundKeepAliveTimeout(
6382
+ clientOptions.backgroundKeepAliveTimeout
6383
+ );
6384
+ const authManager = createAuthManager(options);
6385
+ const roomsById = /* @__PURE__ */ new Map();
6386
+ function teardownRoom(room) {
6387
+ unlinkDevTools(room.id);
6388
+ roomsById.delete(room.id);
6389
+ room.destroy();
6390
+ }
6391
+ function leaseRoom(info) {
6392
+ const leave = () => {
6393
+ const self = leave;
6394
+ if (!info.unsubs.delete(self)) {
6395
+ warn(
6396
+ "This leave function was already called. Calling it more than once has no effect."
6397
+ );
6398
+ } else {
6399
+ if (info.unsubs.size === 0) {
6400
+ teardownRoom(info.room);
6401
+ }
6402
+ }
6403
+ };
6404
+ info.unsubs.add(leave);
6405
+ return {
6406
+ room: info.room,
6407
+ leave
6408
+ };
6409
+ }
6410
+ function enterRoom(roomId, options2) {
6411
+ const existing = roomsById.get(roomId);
6412
+ if (existing !== void 0) {
6413
+ return leaseRoom(existing);
6414
+ }
6415
+ deprecateIf(
6416
+ options2.initialPresence === null || options2.initialPresence === void 0,
6417
+ "Please provide an initial presence value for the current user when entering the room."
6418
+ );
6419
+ const baseUrl = getBaseUrlFromClientOptions(clientOptions);
6420
+ const newRoom = createRoom(
6421
+ {
6422
+ initialPresence: options2.initialPresence ?? {},
6423
+ initialStorage: options2.initialStorage
6424
+ },
6425
+ {
6426
+ roomId,
6427
+ throttleDelay,
6428
+ lostConnectionTimeout,
6429
+ backgroundKeepAliveTimeout,
6430
+ polyfills: clientOptions.polyfills,
6431
+ delegates: clientOptions.mockedDelegates ?? {
6432
+ createSocket: makeCreateSocketDelegateForRoom(
6433
+ roomId,
6434
+ baseUrl,
6435
+ clientOptions.polyfills?.WebSocket
6436
+ ),
6437
+ authenticate: makeAuthDelegateForRoom(roomId, authManager)
6438
+ },
6439
+ enableDebugLogging: clientOptions.enableDebugLogging,
6440
+ unstable_batchedUpdates: options2?.unstable_batchedUpdates,
6441
+ baseUrl,
6442
+ unstable_fallbackToHTTP: !!clientOptions.unstable_fallbackToHTTP
6443
+ }
6444
+ );
6445
+ const newRoomInfo = {
6446
+ room: newRoom,
6447
+ unsubs: /* @__PURE__ */ new Set()
6448
+ };
6449
+ roomsById.set(roomId, newRoomInfo);
6450
+ setupDevTools(() => Array.from(roomsById.keys()));
6451
+ linkDevTools(roomId, newRoom);
6452
+ const shouldConnect = options2.autoConnect ?? options2.shouldInitiallyConnect ?? true;
6453
+ if (shouldConnect) {
6454
+ if (typeof atob === "undefined") {
6455
+ if (clientOptions.polyfills?.atob === void 0) {
6456
+ throw new Error(
6457
+ "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"
6458
+ );
6459
+ }
6460
+ global.atob = clientOptions.polyfills.atob;
6461
+ }
6462
+ newRoom.connect();
6560
6463
  }
6464
+ return leaseRoom(newRoomInfo);
6561
6465
  }
6562
- async function resolve() {
6563
- if (!context.promise) {
6564
- return;
6565
- }
6566
- try {
6567
- const data = await context.promise;
6568
- context.isInvalid = false;
6569
- state = {
6570
- isLoading: false,
6571
- data
6572
- };
6573
- } catch (error3) {
6574
- state = {
6575
- isLoading: false,
6576
- data: state.data,
6577
- error: error3
6578
- };
6579
- }
6580
- context.promise = void 0;
6581
- notify();
6466
+ function enter(roomId, options2) {
6467
+ const { room, leave: _ } = enterRoom(roomId, options2);
6468
+ return room;
6582
6469
  }
6583
- async function revalidate() {
6584
- context.isInvalid = true;
6585
- return get();
6470
+ function getRoom(roomId) {
6471
+ const room = roomsById.get(roomId)?.room;
6472
+ return room ? room : null;
6586
6473
  }
6587
- async function get() {
6588
- if (context.isInvalid) {
6589
- if (!context.promise) {
6590
- context.isInvalid = true;
6591
- context.promise = $asyncFunction();
6592
- state = { isLoading: true, data: state.data };
6593
- notify();
6594
- }
6595
- await resolve();
6474
+ function forceLeave(roomId) {
6475
+ const unsubs = roomsById.get(roomId)?.unsubs ?? /* @__PURE__ */ new Set();
6476
+ for (const unsub of unsubs) {
6477
+ unsub();
6596
6478
  }
6597
- return getState();
6598
6479
  }
6599
- function getState() {
6600
- return state;
6480
+ function logout() {
6481
+ authManager.reset();
6482
+ for (const { room } of roomsById.values()) {
6483
+ if (!isIdle(room.getStatus())) {
6484
+ room.reconnect();
6485
+ }
6486
+ }
6601
6487
  }
6602
6488
  return {
6603
- ...eventSource2.observable,
6604
- get,
6605
- getState,
6606
- revalidate
6489
+ logout,
6490
+ // Old, deprecated APIs
6491
+ enter,
6492
+ getRoom,
6493
+ leave: forceLeave,
6494
+ // New, preferred API
6495
+ enterRoom
6607
6496
  };
6608
6497
  }
6609
- function createAsyncCache(asyncFunction, options) {
6610
- const cache = /* @__PURE__ */ new Map();
6611
- function create(key) {
6612
- let cacheItem = cache.get(key);
6613
- if (cacheItem) {
6614
- return cacheItem;
6615
- }
6616
- cacheItem = createCacheItem(key, asyncFunction, options);
6617
- cache.set(key, cacheItem);
6618
- return cacheItem;
6619
- }
6620
- function get(key) {
6621
- return create(key).get();
6498
+ function checkBounds(option, value, min, max, recommendedMin) {
6499
+ if (typeof value !== "number" || value < min || max !== void 0 && value > max) {
6500
+ throw new Error(
6501
+ max !== void 0 ? `${option} should be between ${recommendedMin ?? min} and ${max}.` : `${option} should be at least ${recommendedMin ?? min}.`
6502
+ );
6622
6503
  }
6623
- function getState(key) {
6624
- return cache.get(key)?.getState();
6504
+ return value;
6505
+ }
6506
+ function getBackgroundKeepAliveTimeout(value) {
6507
+ if (value === void 0)
6508
+ return void 0;
6509
+ return checkBounds(
6510
+ "backgroundKeepAliveTimeout",
6511
+ value,
6512
+ MIN_BACKGROUND_KEEP_ALIVE_TIMEOUT
6513
+ );
6514
+ }
6515
+ function getThrottle(value) {
6516
+ return checkBounds("throttle", value, MIN_THROTTLE, MAX_THROTTLE);
6517
+ }
6518
+ function getLostConnectionTimeout(value) {
6519
+ return checkBounds(
6520
+ "lostConnectionTimeout",
6521
+ value,
6522
+ MIN_LOST_CONNECTION_TIMEOUT,
6523
+ MAX_LOST_CONNECTION_TIMEOUT,
6524
+ RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
6525
+ );
6526
+ }
6527
+
6528
+ // src/crdts/utils.ts
6529
+ function toPlainLson(lson) {
6530
+ if (lson instanceof LiveObject) {
6531
+ return {
6532
+ liveblocksType: "LiveObject",
6533
+ data: Object.fromEntries(
6534
+ Object.entries(lson.toObject()).flatMap(
6535
+ ([key, value]) => value !== void 0 ? [[key, toPlainLson(value)]] : []
6536
+ )
6537
+ )
6538
+ };
6539
+ } else if (lson instanceof LiveMap) {
6540
+ return {
6541
+ liveblocksType: "LiveMap",
6542
+ data: Object.fromEntries(
6543
+ [...lson].map(([key, value]) => [key, toPlainLson(value)])
6544
+ )
6545
+ };
6546
+ } else if (lson instanceof LiveList) {
6547
+ return {
6548
+ liveblocksType: "LiveList",
6549
+ data: [...lson].map((item) => toPlainLson(item))
6550
+ };
6551
+ } else {
6552
+ return lson;
6625
6553
  }
6626
- function revalidate(key) {
6627
- return create(key).revalidate();
6554
+ }
6555
+
6556
+ // src/immutable.ts
6557
+ function lsonObjectToJson(obj) {
6558
+ const result = {};
6559
+ for (const key in obj) {
6560
+ const val = obj[key];
6561
+ if (val !== void 0) {
6562
+ result[key] = lsonToJson(val);
6563
+ }
6628
6564
  }
6629
- function subscribe(key, callback) {
6630
- return create(key).subscribe(callback) ?? noop;
6565
+ return result;
6566
+ }
6567
+ function liveObjectToJson(liveObject) {
6568
+ return lsonObjectToJson(liveObject.toObject());
6569
+ }
6570
+ function liveMapToJson(map) {
6571
+ const result = {};
6572
+ for (const [key, value] of map.entries()) {
6573
+ result[key] = lsonToJson(value);
6631
6574
  }
6632
- function subscribeOnce(key, callback) {
6633
- return create(key).subscribeOnce(callback) ?? noop;
6575
+ return result;
6576
+ }
6577
+ function lsonListToJson(value) {
6578
+ return value.map(lsonToJson);
6579
+ }
6580
+ function liveListToJson(value) {
6581
+ return lsonListToJson(value.toArray());
6582
+ }
6583
+ function lsonToJson(value) {
6584
+ if (value instanceof LiveObject) {
6585
+ return liveObjectToJson(value);
6586
+ } else if (value instanceof LiveList) {
6587
+ return liveListToJson(value);
6588
+ } else if (value instanceof LiveMap) {
6589
+ return liveMapToJson(value);
6590
+ } else if (value instanceof LiveRegister) {
6591
+ return value.data;
6634
6592
  }
6635
- function has(key) {
6636
- return cache.has(key);
6593
+ if (Array.isArray(value)) {
6594
+ return lsonListToJson(value);
6595
+ } else if (isPlainObject(value)) {
6596
+ return lsonObjectToJson(value);
6637
6597
  }
6638
- function clear() {
6639
- cache.clear();
6598
+ return value;
6599
+ }
6600
+ function deepLiveify(value) {
6601
+ if (Array.isArray(value)) {
6602
+ return new LiveList(value.map(deepLiveify));
6603
+ } else if (isPlainObject(value)) {
6604
+ const init = {};
6605
+ for (const key in value) {
6606
+ const val = value[key];
6607
+ if (val === void 0) {
6608
+ continue;
6609
+ }
6610
+ init[key] = deepLiveify(val);
6611
+ }
6612
+ return new LiveObject(init);
6613
+ } else {
6614
+ return value;
6640
6615
  }
6641
- return {
6642
- create,
6643
- get,
6644
- getState,
6645
- revalidate,
6646
- subscribe,
6647
- subscribeOnce,
6648
- has,
6649
- clear
6650
- };
6651
6616
  }
6652
-
6653
- // src/lib/Poller.ts
6654
- function makePoller(callback) {
6655
- let context = {
6656
- state: "stopped",
6657
- timeoutHandle: null,
6658
- interval: null,
6659
- lastScheduledAt: null,
6660
- remainingInterval: null
6661
- };
6662
- function poll() {
6663
- if (context.state === "running") {
6664
- schedule(context.interval);
6617
+ function patchLiveList(liveList, prev, next) {
6618
+ let i = 0;
6619
+ let prevEnd = prev.length - 1;
6620
+ let nextEnd = next.length - 1;
6621
+ let prevNode = prev[0];
6622
+ let nextNode = next[0];
6623
+ outer: {
6624
+ while (prevNode === nextNode) {
6625
+ ++i;
6626
+ if (i > prevEnd || i > nextEnd) {
6627
+ break outer;
6628
+ }
6629
+ prevNode = prev[i];
6630
+ nextNode = next[i];
6631
+ }
6632
+ prevNode = prev[prevEnd];
6633
+ nextNode = next[nextEnd];
6634
+ while (prevNode === nextNode) {
6635
+ prevEnd--;
6636
+ nextEnd--;
6637
+ if (i > prevEnd || i > nextEnd) {
6638
+ break outer;
6639
+ }
6640
+ prevNode = prev[prevEnd];
6641
+ nextNode = next[nextEnd];
6665
6642
  }
6666
- void callback();
6667
- }
6668
- function schedule(interval) {
6669
- context = {
6670
- state: "running",
6671
- interval: context.state !== "stopped" ? context.interval : interval,
6672
- lastScheduledAt: performance.now(),
6673
- timeoutHandle: setTimeout(poll, interval),
6674
- remainingInterval: null
6675
- };
6676
6643
  }
6677
- function scheduleRemaining(remaining) {
6678
- if (context.state !== "paused") {
6679
- return;
6644
+ if (i > prevEnd) {
6645
+ if (i <= nextEnd) {
6646
+ while (i <= nextEnd) {
6647
+ liveList.insert(deepLiveify(next[i]), i);
6648
+ i++;
6649
+ }
6650
+ }
6651
+ } else if (i > nextEnd) {
6652
+ let localI = i;
6653
+ while (localI <= prevEnd) {
6654
+ liveList.delete(i);
6655
+ localI++;
6656
+ }
6657
+ } else {
6658
+ while (i <= prevEnd && i <= nextEnd) {
6659
+ prevNode = prev[i];
6660
+ nextNode = next[i];
6661
+ const liveListNode = liveList.get(i);
6662
+ if (isLiveObject(liveListNode) && isPlainObject(prevNode) && isPlainObject(nextNode)) {
6663
+ patchLiveObject(liveListNode, prevNode, nextNode);
6664
+ } else {
6665
+ liveList.set(i, deepLiveify(nextNode));
6666
+ }
6667
+ i++;
6668
+ }
6669
+ while (i <= nextEnd) {
6670
+ liveList.insert(deepLiveify(next[i]), i);
6671
+ i++;
6672
+ }
6673
+ let localI = i;
6674
+ while (localI <= prevEnd) {
6675
+ liveList.delete(i);
6676
+ localI++;
6680
6677
  }
6681
- context = {
6682
- state: "running",
6683
- interval: context.interval,
6684
- lastScheduledAt: context.lastScheduledAt,
6685
- timeoutHandle: setTimeout(poll, remaining),
6686
- remainingInterval: null
6687
- };
6688
6678
  }
6689
- function start(interval) {
6690
- if (context.state === "running") {
6679
+ }
6680
+ function patchLiveObjectKey(liveObject, key, prev, next) {
6681
+ if (process.env.NODE_ENV !== "production") {
6682
+ const nonSerializableValue = findNonSerializableValue(next);
6683
+ if (nonSerializableValue) {
6684
+ error2(
6685
+ `New state path: '${nonSerializableValue.path}' value: '${String(
6686
+ nonSerializableValue.value
6687
+ )}' is not serializable.
6688
+ Only serializable value can be synced with Liveblocks.`
6689
+ );
6691
6690
  return;
6692
6691
  }
6693
- schedule(interval);
6694
6692
  }
6695
- function restart(interval) {
6696
- stop();
6697
- start(interval);
6693
+ const value = liveObject.get(key);
6694
+ if (next === void 0) {
6695
+ liveObject.delete(key);
6696
+ } else if (value === void 0) {
6697
+ liveObject.set(key, deepLiveify(next));
6698
+ } else if (prev === next) {
6699
+ return;
6700
+ } else if (isLiveList(value) && Array.isArray(prev) && Array.isArray(next)) {
6701
+ patchLiveList(value, prev, next);
6702
+ } else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
6703
+ patchLiveObject(value, prev, next);
6704
+ } else {
6705
+ liveObject.set(key, deepLiveify(next));
6698
6706
  }
6699
- function pause() {
6700
- if (context.state !== "running") {
6701
- return;
6702
- }
6703
- clearTimeout(context.timeoutHandle);
6704
- context = {
6705
- state: "paused",
6706
- interval: context.interval,
6707
- lastScheduledAt: context.lastScheduledAt,
6708
- timeoutHandle: null,
6709
- remainingInterval: context.interval - (performance.now() - context.lastScheduledAt)
6710
- };
6707
+ }
6708
+ function patchLiveObject(root, prev, next) {
6709
+ const updates = {};
6710
+ for (const key in next) {
6711
+ patchLiveObjectKey(root, key, prev[key], next[key]);
6711
6712
  }
6712
- function resume() {
6713
- if (context.state !== "paused") {
6714
- return;
6713
+ for (const key in prev) {
6714
+ if (next[key] === void 0) {
6715
+ root.delete(key);
6715
6716
  }
6716
- scheduleRemaining(context.remainingInterval);
6717
6717
  }
6718
- function stop() {
6719
- if (context.state === "stopped") {
6720
- return;
6721
- }
6722
- if (context.timeoutHandle) {
6723
- clearTimeout(context.timeoutHandle);
6718
+ if (Object.keys(updates).length > 0) {
6719
+ root.update(updates);
6720
+ }
6721
+ }
6722
+ function getParentsPath(node) {
6723
+ const path = [];
6724
+ while (node.parent.type === "HasParent") {
6725
+ if (isLiveList(node.parent.node)) {
6726
+ path.push(node.parent.node._indexOfPosition(node.parent.key));
6727
+ } else {
6728
+ path.push(node.parent.key);
6724
6729
  }
6725
- context = {
6726
- state: "stopped",
6727
- interval: null,
6728
- lastScheduledAt: null,
6729
- timeoutHandle: null,
6730
- remainingInterval: null
6731
- };
6730
+ node = node.parent.node;
6732
6731
  }
6733
- return {
6734
- start,
6735
- restart,
6736
- pause,
6737
- resume,
6738
- stop
6739
- };
6732
+ return path;
6740
6733
  }
6741
-
6742
- // src/lib/stringify.ts
6743
- function stringify(object, ...args) {
6744
- if (typeof object !== "object" || object === null || Array.isArray(object)) {
6745
- return JSON.stringify(object, ...args);
6734
+ function legacy_patchImmutableObject(state, updates) {
6735
+ return updates.reduce(
6736
+ (state2, update) => legacy_patchImmutableObjectWithUpdate(state2, update),
6737
+ state
6738
+ );
6739
+ }
6740
+ function legacy_patchImmutableObjectWithUpdate(state, update) {
6741
+ const path = getParentsPath(update.node);
6742
+ return legacy_patchImmutableNode(state, path, update);
6743
+ }
6744
+ function legacy_patchImmutableNode(state, path, update) {
6745
+ const pathItem = path.pop();
6746
+ if (pathItem === void 0) {
6747
+ switch (update.type) {
6748
+ case "LiveObject": {
6749
+ if (!isJsonObject(state)) {
6750
+ throw new Error(
6751
+ "Internal: received update on LiveObject but state was not an object"
6752
+ );
6753
+ }
6754
+ const newState = Object.assign({}, state);
6755
+ for (const key in update.updates) {
6756
+ if (update.updates[key]?.type === "update") {
6757
+ const val = update.node.get(key);
6758
+ if (val !== void 0) {
6759
+ newState[key] = lsonToJson(val);
6760
+ }
6761
+ } else if (update.updates[key]?.type === "delete") {
6762
+ delete newState[key];
6763
+ }
6764
+ }
6765
+ return newState;
6766
+ }
6767
+ case "LiveList": {
6768
+ if (!Array.isArray(state)) {
6769
+ throw new Error(
6770
+ "Internal: received update on LiveList but state was not an array"
6771
+ );
6772
+ }
6773
+ let newState = state.map((x) => x);
6774
+ for (const listUpdate of update.updates) {
6775
+ if (listUpdate.type === "set") {
6776
+ newState = newState.map(
6777
+ (item, index) => index === listUpdate.index ? lsonToJson(listUpdate.item) : item
6778
+ );
6779
+ } else if (listUpdate.type === "insert") {
6780
+ if (listUpdate.index === newState.length) {
6781
+ newState.push(lsonToJson(listUpdate.item));
6782
+ } else {
6783
+ newState = [
6784
+ ...newState.slice(0, listUpdate.index),
6785
+ lsonToJson(listUpdate.item),
6786
+ ...newState.slice(listUpdate.index)
6787
+ ];
6788
+ }
6789
+ } else if (listUpdate.type === "delete") {
6790
+ newState.splice(listUpdate.index, 1);
6791
+ } else if (listUpdate.type === "move") {
6792
+ if (listUpdate.previousIndex > listUpdate.index) {
6793
+ newState = [
6794
+ ...newState.slice(0, listUpdate.index),
6795
+ lsonToJson(listUpdate.item),
6796
+ ...newState.slice(listUpdate.index, listUpdate.previousIndex),
6797
+ ...newState.slice(listUpdate.previousIndex + 1)
6798
+ ];
6799
+ } else {
6800
+ newState = [
6801
+ ...newState.slice(0, listUpdate.previousIndex),
6802
+ ...newState.slice(
6803
+ listUpdate.previousIndex + 1,
6804
+ listUpdate.index + 1
6805
+ ),
6806
+ lsonToJson(listUpdate.item),
6807
+ ...newState.slice(listUpdate.index + 1)
6808
+ ];
6809
+ }
6810
+ }
6811
+ }
6812
+ return newState;
6813
+ }
6814
+ case "LiveMap": {
6815
+ if (!isJsonObject(state)) {
6816
+ throw new Error(
6817
+ "Internal: received update on LiveMap but state was not an object"
6818
+ );
6819
+ }
6820
+ const newState = Object.assign({}, state);
6821
+ for (const key in update.updates) {
6822
+ if (update.updates[key]?.type === "update") {
6823
+ const value = update.node.get(key);
6824
+ if (value !== void 0) {
6825
+ newState[key] = lsonToJson(value);
6826
+ }
6827
+ } else if (update.updates[key]?.type === "delete") {
6828
+ delete newState[key];
6829
+ }
6830
+ }
6831
+ return newState;
6832
+ }
6833
+ }
6834
+ }
6835
+ if (Array.isArray(state)) {
6836
+ const newArray = [...state];
6837
+ newArray[pathItem] = legacy_patchImmutableNode(
6838
+ state[pathItem],
6839
+ path,
6840
+ update
6841
+ );
6842
+ return newArray;
6843
+ } else if (isJsonObject(state)) {
6844
+ const node = state[pathItem];
6845
+ if (node === void 0) {
6846
+ return state;
6847
+ } else {
6848
+ const stateAsObj = state;
6849
+ return {
6850
+ ...stateAsObj,
6851
+ [pathItem]: legacy_patchImmutableNode(node, path, update)
6852
+ };
6853
+ }
6854
+ } else {
6855
+ return state;
6746
6856
  }
6747
- const sortedObject = Object.keys(object).sort().reduce(
6748
- (sortedObject2, key) => {
6749
- sortedObject2[key] = object[key];
6750
- return sortedObject2;
6751
- },
6752
- {}
6753
- );
6754
- return JSON.stringify(sortedObject, ...args);
6755
6857
  }
6756
6858
 
6757
- // src/comments/comment-body.ts
6758
- function isCommentBodyParagraph(element) {
6759
- return "type" in element && element.type === "mention";
6760
- }
6761
- function isCommentBodyText(element) {
6762
- return "text" in element && typeof element.text === "string";
6763
- }
6764
- function isCommentBodyMention(element) {
6765
- return "type" in element && element.type === "mention";
6766
- }
6767
- function isCommentBodyLink(element) {
6768
- return "type" in element && element.type === "link";
6769
- }
6770
- var commentBodyElementsGuards = {
6771
- paragraph: isCommentBodyParagraph,
6772
- text: isCommentBodyText,
6773
- link: isCommentBodyLink,
6774
- mention: isCommentBodyMention
6775
- };
6776
- var commentBodyElementsTypes = {
6777
- paragraph: "block",
6778
- text: "inline",
6779
- link: "inline",
6780
- mention: "inline"
6781
- };
6782
- function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
6783
- if (!body || !body?.content) {
6784
- return;
6859
+ // src/lib/shallow.ts
6860
+ function shallowArray(xs, ys) {
6861
+ if (xs.length !== ys.length) {
6862
+ return false;
6785
6863
  }
6786
- const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
6787
- const type = element ? commentBodyElementsTypes[element] : "all";
6788
- const guard = element ? commentBodyElementsGuards[element] : () => true;
6789
- const visitor = typeof elementOrVisitor === "function" ? elementOrVisitor : possiblyVisitor;
6790
- for (const block of body.content) {
6791
- if (type === "all" || type === "block") {
6792
- if (guard(block)) {
6793
- visitor?.(block);
6794
- }
6795
- }
6796
- if (type === "all" || type === "inline") {
6797
- for (const inline of block.children) {
6798
- if (guard(inline)) {
6799
- visitor?.(inline);
6800
- }
6801
- }
6864
+ for (let i = 0; i < xs.length; i++) {
6865
+ if (!Object.is(xs[i], ys[i])) {
6866
+ return false;
6802
6867
  }
6803
6868
  }
6869
+ return true;
6804
6870
  }
6805
- function getMentionedIdsFromCommentBody(body) {
6806
- const mentionedIds = /* @__PURE__ */ new Set();
6807
- traverseCommentBody(
6808
- body,
6809
- "mention",
6810
- (mention) => mentionedIds.add(mention.id)
6871
+ function shallowObj(objA, objB) {
6872
+ if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null || Object.prototype.toString.call(objA) !== "[object Object]" || Object.prototype.toString.call(objB) !== "[object Object]") {
6873
+ return false;
6874
+ }
6875
+ const keysA = Object.keys(objA);
6876
+ if (keysA.length !== Object.keys(objB).length) {
6877
+ return false;
6878
+ }
6879
+ return keysA.every(
6880
+ (key) => Object.prototype.hasOwnProperty.call(objB, key) && Object.is(objA[key], objB[key])
6811
6881
  );
6812
- return Array.from(mentionedIds);
6813
6882
  }
6814
- async function resolveUsersInCommentBody(body, resolveUsers) {
6815
- const resolvedUsers = /* @__PURE__ */ new Map();
6816
- if (!resolveUsers) {
6817
- return resolvedUsers;
6883
+ function shallow(a, b) {
6884
+ if (Object.is(a, b)) {
6885
+ return true;
6818
6886
  }
6819
- const userIds = getMentionedIdsFromCommentBody(body);
6820
- const users = await resolveUsers({
6821
- userIds
6822
- });
6823
- for (const [index, userId] of userIds.entries()) {
6824
- const user = users?.[index];
6825
- if (user) {
6826
- resolvedUsers.set(userId, user);
6887
+ const isArrayA = Array.isArray(a);
6888
+ const isArrayB = Array.isArray(b);
6889
+ if (isArrayA || isArrayB) {
6890
+ if (!isArrayA || !isArrayB) {
6891
+ return false;
6827
6892
  }
6893
+ return shallowArray(a, b);
6828
6894
  }
6829
- return resolvedUsers;
6895
+ return shallowObj(a, b);
6830
6896
  }
6831
- var htmlEscapables = {
6832
- "&": "&amp;",
6833
- "<": "&lt;",
6834
- ">": "&gt;",
6835
- '"': "&quot;",
6836
- "'": "&#39;"
6897
+
6898
+ // src/lib/AsyncCache.ts
6899
+ var noop = () => {
6837
6900
  };
6838
- var htmlEscapablesRegex = new RegExp(
6839
- Object.keys(htmlEscapables).map((entity) => `\\${entity}`).join("|"),
6840
- "g"
6841
- );
6842
- function htmlSafe(value) {
6843
- return new HtmlSafeString([String(value)], []);
6844
- }
6845
- function joinHtml(strings) {
6846
- if (strings.length <= 0) {
6847
- return new HtmlSafeString([""], []);
6901
+ function isShallowEqual(a, b) {
6902
+ if (a.isLoading !== b.isLoading || a.data === void 0 !== (b.data === void 0) || a.error === void 0 !== (b.error === void 0)) {
6903
+ return false;
6904
+ } else {
6905
+ return shallow(a.data, b.data) && shallow(a.error, b.error);
6848
6906
  }
6849
- return new HtmlSafeString(
6850
- ["", ...Array(strings.length - 1).fill(""), ""],
6851
- strings
6852
- );
6853
6907
  }
6854
- function escapeHtml(value) {
6855
- if (value instanceof HtmlSafeString) {
6856
- return value.toString();
6908
+ function createCacheItem(key, asyncFunction, options) {
6909
+ const $asyncFunction = async () => asyncFunction(key);
6910
+ const context = {
6911
+ isInvalid: true
6912
+ };
6913
+ let state = { isLoading: false };
6914
+ let previousState = { isLoading: false };
6915
+ const eventSource2 = makeEventSource();
6916
+ function notify() {
6917
+ const isEqual = options?.isStateEqual ?? isShallowEqual;
6918
+ if (!isEqual(previousState, state)) {
6919
+ previousState = state;
6920
+ eventSource2.notify(state);
6921
+ }
6857
6922
  }
6858
- if (Array.isArray(value)) {
6859
- return joinHtml(value).toString();
6923
+ async function resolve() {
6924
+ if (!context.promise) {
6925
+ return;
6926
+ }
6927
+ try {
6928
+ const data = await context.promise;
6929
+ context.isInvalid = false;
6930
+ state = {
6931
+ isLoading: false,
6932
+ data
6933
+ };
6934
+ } catch (error3) {
6935
+ state = {
6936
+ isLoading: false,
6937
+ data: state.data,
6938
+ error: error3
6939
+ };
6940
+ }
6941
+ context.promise = void 0;
6942
+ notify();
6860
6943
  }
6861
- return String(value).replace(
6862
- htmlEscapablesRegex,
6863
- (character) => htmlEscapables[character]
6864
- );
6865
- }
6866
- var HtmlSafeString = class {
6867
- constructor(strings, values) {
6868
- this._strings = strings;
6869
- this._values = values;
6944
+ async function revalidate() {
6945
+ context.isInvalid = true;
6946
+ return get();
6870
6947
  }
6871
- toString() {
6872
- return this._strings.reduce((result, str, i) => {
6873
- return result + escapeHtml(nn(this._values[i - 1])) + str;
6874
- });
6948
+ async function get() {
6949
+ if (context.isInvalid) {
6950
+ if (!context.promise) {
6951
+ context.isInvalid = true;
6952
+ context.promise = $asyncFunction();
6953
+ state = { isLoading: true, data: state.data };
6954
+ notify();
6955
+ }
6956
+ await resolve();
6957
+ }
6958
+ return getState();
6875
6959
  }
6876
- };
6877
- function html(strings, ...values) {
6878
- return new HtmlSafeString(strings, values);
6960
+ function getState() {
6961
+ return state;
6962
+ }
6963
+ return {
6964
+ ...eventSource2.observable,
6965
+ get,
6966
+ getState,
6967
+ revalidate
6968
+ };
6879
6969
  }
6880
- var markdownEscapables = {
6881
- _: "\\_",
6882
- "*": "\\*",
6883
- "#": "\\#",
6884
- "`": "\\`",
6885
- "~": "\\~",
6886
- "!": "\\!",
6887
- "|": "\\|",
6888
- "(": "\\(",
6889
- ")": "\\)",
6890
- "{": "\\{",
6891
- "}": "\\}",
6892
- "[": "\\[",
6893
- "]": "\\]"
6894
- };
6895
- var markdownEscapablesRegex = new RegExp(
6896
- Object.keys(markdownEscapables).map((entity) => `\\${entity}`).join("|"),
6897
- "g"
6898
- );
6899
- function joinMarkdown(strings) {
6900
- if (strings.length <= 0) {
6901
- return new MarkdownSafeString([""], []);
6970
+ function createAsyncCache(asyncFunction, options) {
6971
+ const cache = /* @__PURE__ */ new Map();
6972
+ function create(key) {
6973
+ let cacheItem = cache.get(key);
6974
+ if (cacheItem) {
6975
+ return cacheItem;
6976
+ }
6977
+ cacheItem = createCacheItem(key, asyncFunction, options);
6978
+ cache.set(key, cacheItem);
6979
+ return cacheItem;
6980
+ }
6981
+ function get(key) {
6982
+ return create(key).get();
6983
+ }
6984
+ function getState(key) {
6985
+ return cache.get(key)?.getState();
6902
6986
  }
6903
- return new MarkdownSafeString(
6904
- ["", ...Array(strings.length - 1).fill(""), ""],
6905
- strings
6906
- );
6907
- }
6908
- function escapeMarkdown(value) {
6909
- if (value instanceof MarkdownSafeString) {
6910
- return value.toString();
6987
+ function revalidate(key) {
6988
+ return create(key).revalidate();
6911
6989
  }
6912
- if (Array.isArray(value)) {
6913
- return joinMarkdown(value).toString();
6990
+ function subscribe(key, callback) {
6991
+ return create(key).subscribe(callback) ?? noop;
6914
6992
  }
6915
- return String(value).replace(
6916
- markdownEscapablesRegex,
6917
- (character) => markdownEscapables[character]
6918
- );
6919
- }
6920
- var MarkdownSafeString = class {
6921
- constructor(strings, values) {
6922
- this._strings = strings;
6923
- this._values = values;
6993
+ function subscribeOnce(key, callback) {
6994
+ return create(key).subscribeOnce(callback) ?? noop;
6924
6995
  }
6925
- toString() {
6926
- return this._strings.reduce((result, str, i) => {
6927
- return result + escapeMarkdown(nn(this._values[i - 1])) + str;
6928
- });
6996
+ function has(key) {
6997
+ return cache.has(key);
6929
6998
  }
6930
- };
6931
- function markdown(strings, ...values) {
6932
- return new MarkdownSafeString(strings, values);
6933
- }
6934
- function toAbsoluteUrl(url) {
6935
- if (url.startsWith("http://") || url.startsWith("https://")) {
6936
- return url;
6937
- } else if (url.startsWith("www.")) {
6938
- return "https://" + url;
6999
+ function clear() {
7000
+ cache.clear();
6939
7001
  }
6940
- return;
7002
+ return {
7003
+ create,
7004
+ get,
7005
+ getState,
7006
+ revalidate,
7007
+ subscribe,
7008
+ subscribeOnce,
7009
+ has,
7010
+ clear
7011
+ };
6941
7012
  }
6942
- var stringifyCommentBodyPlainElements = {
6943
- paragraph: ({ children }) => children,
6944
- text: ({ element }) => element.text,
6945
- link: ({ element }) => element.url,
6946
- mention: ({ element, user }) => {
6947
- return `@${user?.name ?? element.id}`;
6948
- }
6949
- };
6950
- var stringifyCommentBodyHtmlElements = {
6951
- paragraph: ({ children }) => {
6952
- return children ? html`<p>${htmlSafe(children)}</p>` : children;
6953
- },
6954
- text: ({ element }) => {
6955
- let children = element.text;
6956
- if (!children) {
6957
- return children;
6958
- }
6959
- if (element.bold) {
6960
- children = html`<strong>${children}</strong>`;
6961
- }
6962
- if (element.italic) {
6963
- children = html`<em>${children}</em>`;
6964
- }
6965
- if (element.strikethrough) {
6966
- children = html`<s>${children}</s>`;
7013
+
7014
+ // src/lib/Poller.ts
7015
+ function makePoller(callback) {
7016
+ let context = {
7017
+ state: "stopped",
7018
+ timeoutHandle: null,
7019
+ interval: null,
7020
+ lastScheduledAt: null,
7021
+ remainingInterval: null
7022
+ };
7023
+ function poll() {
7024
+ if (context.state === "running") {
7025
+ schedule(context.interval);
6967
7026
  }
6968
- if (element.code) {
6969
- children = html`<code>${children}</code>`;
7027
+ void callback();
7028
+ }
7029
+ function schedule(interval) {
7030
+ context = {
7031
+ state: "running",
7032
+ interval: context.state !== "stopped" ? context.interval : interval,
7033
+ lastScheduledAt: performance.now(),
7034
+ timeoutHandle: setTimeout(poll, interval),
7035
+ remainingInterval: null
7036
+ };
7037
+ }
7038
+ function scheduleRemaining(remaining) {
7039
+ if (context.state !== "paused") {
7040
+ return;
6970
7041
  }
6971
- return children;
6972
- },
6973
- link: ({ element, href }) => {
6974
- return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.url}</a>`;
6975
- },
6976
- mention: ({ element, user }) => {
6977
- return html`<span data-mention>@${user?.name ?? element.id}</span>`;
7042
+ context = {
7043
+ state: "running",
7044
+ interval: context.interval,
7045
+ lastScheduledAt: context.lastScheduledAt,
7046
+ timeoutHandle: setTimeout(poll, remaining),
7047
+ remainingInterval: null
7048
+ };
6978
7049
  }
6979
- };
6980
- var stringifyCommentBodyMarkdownElements = {
6981
- paragraph: ({ children }) => {
6982
- return children;
6983
- },
6984
- text: ({ element }) => {
6985
- let children = element.text;
6986
- if (!children) {
6987
- return children;
7050
+ function start(interval) {
7051
+ if (context.state === "running") {
7052
+ return;
6988
7053
  }
6989
- if (element.bold) {
6990
- children = markdown`**${children}**`;
7054
+ schedule(interval);
7055
+ }
7056
+ function restart(interval) {
7057
+ stop();
7058
+ start(interval);
7059
+ }
7060
+ function pause() {
7061
+ if (context.state !== "running") {
7062
+ return;
6991
7063
  }
6992
- if (element.italic) {
6993
- children = markdown`_${children}_`;
7064
+ clearTimeout(context.timeoutHandle);
7065
+ context = {
7066
+ state: "paused",
7067
+ interval: context.interval,
7068
+ lastScheduledAt: context.lastScheduledAt,
7069
+ timeoutHandle: null,
7070
+ remainingInterval: context.interval - (performance.now() - context.lastScheduledAt)
7071
+ };
7072
+ }
7073
+ function resume() {
7074
+ if (context.state !== "paused") {
7075
+ return;
6994
7076
  }
6995
- if (element.strikethrough) {
6996
- children = markdown`~~${children}~~`;
7077
+ scheduleRemaining(context.remainingInterval);
7078
+ }
7079
+ function stop() {
7080
+ if (context.state === "stopped") {
7081
+ return;
6997
7082
  }
6998
- if (element.code) {
6999
- children = markdown`\`${children}\``;
7083
+ if (context.timeoutHandle) {
7084
+ clearTimeout(context.timeoutHandle);
7000
7085
  }
7001
- return children;
7002
- },
7003
- link: ({ element, href }) => {
7004
- return markdown`[${element.url}](${href})`;
7005
- },
7006
- mention: ({ element, user }) => {
7007
- return markdown`@${user?.name ?? element.id}`;
7086
+ context = {
7087
+ state: "stopped",
7088
+ interval: null,
7089
+ lastScheduledAt: null,
7090
+ timeoutHandle: null,
7091
+ remainingInterval: null
7092
+ };
7008
7093
  }
7009
- };
7010
- async function stringifyCommentBody(body, options) {
7011
- const format = options?.format ?? "plain";
7012
- const separator = options?.separator ?? (format === "markdown" ? "\n\n" : "\n");
7013
- const elements = {
7014
- ...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
7015
- ...options?.elements
7094
+ return {
7095
+ start,
7096
+ restart,
7097
+ pause,
7098
+ resume,
7099
+ stop
7016
7100
  };
7017
- const resolvedUsers = await resolveUsersInCommentBody(
7018
- body,
7019
- options?.resolveUsers
7101
+ }
7102
+
7103
+ // src/lib/stringify.ts
7104
+ function stringify(object, ...args) {
7105
+ if (typeof object !== "object" || object === null || Array.isArray(object)) {
7106
+ return JSON.stringify(object, ...args);
7107
+ }
7108
+ const sortedObject = Object.keys(object).sort().reduce(
7109
+ (sortedObject2, key) => {
7110
+ sortedObject2[key] = object[key];
7111
+ return sortedObject2;
7112
+ },
7113
+ {}
7020
7114
  );
7021
- const blocks = body.content.flatMap((block, blockIndex) => {
7022
- switch (block.type) {
7023
- case "paragraph": {
7024
- const inlines = block.children.flatMap((inline, inlineIndex) => {
7025
- if (isCommentBodyMention(inline)) {
7026
- return inline.id ? [
7027
- elements.mention(
7028
- {
7029
- element: inline,
7030
- user: resolvedUsers.get(inline.id)
7031
- },
7032
- inlineIndex
7033
- )
7034
- ] : [];
7035
- }
7036
- if (isCommentBodyLink(inline)) {
7037
- return [
7038
- elements.link(
7039
- {
7040
- element: inline,
7041
- href: toAbsoluteUrl(inline.url) ?? inline.url
7042
- },
7043
- inlineIndex
7044
- )
7045
- ];
7046
- }
7047
- if (isCommentBodyText(inline)) {
7048
- return [elements.text({ element: inline }, inlineIndex)];
7049
- }
7050
- return [];
7051
- });
7052
- return [
7053
- elements.paragraph(
7054
- { element: block, children: inlines.join("") },
7055
- blockIndex
7056
- )
7057
- ];
7058
- }
7059
- default:
7060
- return [];
7061
- }
7062
- });
7063
- return blocks.join(separator);
7115
+ return JSON.stringify(sortedObject, ...args);
7064
7116
  }
7065
7117
 
7066
7118
  // src/index.ts
@@ -7081,6 +7133,9 @@ export {
7081
7133
  b64decode,
7082
7134
  cloneLson,
7083
7135
  fancy_console_exports as console,
7136
+ convertToCommentData,
7137
+ convertToCommentUserReaction,
7138
+ convertToThreadData,
7084
7139
  createAsyncCache,
7085
7140
  createClient,
7086
7141
  createCommentsApi,