diff --git a/.gitignore b/.gitignore index 53ce572..af3b71f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ node_modules/ dist/ build/ -*.env +*.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 26cf791..a565940 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,56 +8,469 @@ "name": "xi.hocus", "version": "0.1.0", "dependencies": { - "@hocuspocus/extension-database": "^2.13.5", - "@hocuspocus/extension-logger": "^2.13.6", - "@hocuspocus/server": "^2.13.5" + "@hocuspocus/extension-database": "^3.4.4", + "@hocuspocus/extension-logger": "^3.4.4", + "@hocuspocus/server": "^3.4.4" }, "devDependencies": { "@types/node": "^22.5.5", + "tsx": "^4.19.2", "typescript": "^5.5.4" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@hocuspocus/common": { - "version": "2.13.6", - "resolved": "https://registry.npmjs.org/@hocuspocus/common/-/common-2.13.6.tgz", - "integrity": "sha512-jeEawHoaBckfbjMRPqjANjtQkeHgCBsk3XN7CEs/1jvUm60d86FT4y5fp3MWTEE18qkZacbZldn32za+72RBdg==", - "license": "MIT", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/common/-/common-3.4.4.tgz", + "integrity": "sha512-RykIJ0tsHHMP4Xk+4UCbc7SO5LgGxGUSTdbh6anJEsaALAyqinf1Nn5HYuMjLPolAmsar1v++m9zufR09NLpXA==", "dependencies": { "lib0": "^0.2.87" } }, "node_modules/@hocuspocus/extension-database": { - "version": "2.13.5", - "resolved": "https://registry.npmjs.org/@hocuspocus/extension-database/-/extension-database-2.13.5.tgz", - "integrity": "sha512-+sS26ZKOM50cbTtV+mti8Z1AEEiv+rZuvMC77ol3SWGlcNGsBx45XklKof4ifQ8oCIMgMC0jzP7+avQ9OtOC0w==", - "license": "MIT", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/extension-database/-/extension-database-3.4.4.tgz", + "integrity": "sha512-z7iq2Dw+GOp4aQq7ys3PD0BA++7tQdXBsSHZ+8mkAbxfTDzjzQ576TphxPiXXC1WQ7yjeFXq03xp/KLIhg3Pyg==", "dependencies": { - "@hocuspocus/server": "^2.13.5" + "@hocuspocus/server": "^3.4.4" }, "peerDependencies": { "yjs": "^13.6.8" } }, "node_modules/@hocuspocus/extension-logger": { - "version": "2.13.6", - "resolved": "https://registry.npmjs.org/@hocuspocus/extension-logger/-/extension-logger-2.13.6.tgz", - "integrity": "sha512-DQ1zBGFdRBCEmGfo3bcR2W3P/6SQmMmSYq4iqKB98U54901fKJz7cI+vrp6Xfr447Z1j2sfdlN9YTPaVMjv4rQ==", - "license": "MIT", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/extension-logger/-/extension-logger-3.4.4.tgz", + "integrity": "sha512-GEnjmvQlrDlr5hoTQ8NHStZkzpL42wkwZ5XOBMkEX/TgqcnEtKCK1SOK0xj+px9tEHaflDLtCq6f5oy4gOCkPQ==", "dependencies": { - "@hocuspocus/server": "^2.13.6" + "@hocuspocus/server": "^3.4.4" } }, "node_modules/@hocuspocus/server": { - "version": "2.13.6", - "resolved": "https://registry.npmjs.org/@hocuspocus/server/-/server-2.13.6.tgz", - "integrity": "sha512-VqiHkkQlfa2A5mFTe+rdePLt3kHJrk9ZzqDqWJfqQDlYHdyAXpW53cddsXYYDWQ/tZjYj3zQgZAT+78AHrnb/A==", - "license": "MIT", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/server/-/server-3.4.4.tgz", + "integrity": "sha512-UV+oaONAejOzeYgUygNcgsc8RdZvSokVvAxluZJIisLACpRO/VsseQ5lWKDRwLd7Fn6+rHWDH3hGuQ1fdX1Ycg==", "dependencies": { - "@hocuspocus/common": "^2.13.6", + "@hocuspocus/common": "^3.4.4", "async-lock": "^1.3.1", + "async-mutex": "^0.5.0", "kleur": "^4.1.4", "lib0": "^0.2.47", - "uuid": "^10.0.0", "ws": "^8.5.0" }, "peerDependencies": { @@ -78,8 +491,82 @@ "node_modules/async-lock": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "license": "MIT" + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } }, "node_modules/isomorphic.js": { "version": "0.2.5", @@ -95,7 +582,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", "engines": { "node": ">=6" } @@ -121,6 +607,39 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/typescript": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", @@ -142,24 +661,10 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "engines": { "node": ">=10.0.0" }, @@ -177,10 +682,9 @@ } }, "node_modules/y-protocols": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", - "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", - "license": "MIT", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.7.tgz", + "integrity": "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==", "peer": true, "dependencies": { "lib0": "^0.2.85" diff --git a/package.json b/package.json index 71be720..8e4419d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Hocuspocus server connected to the real backend via a webhook", "scripts": { "build": "tsc", - "start": "node dist/index.js" + "start": "node dist/index.js", + "dev": "tsx src/index.ts" }, "repository": { "type": "git", @@ -12,11 +13,12 @@ }, "devDependencies": { "@types/node": "^22.5.5", + "tsx": "^4.19.2", "typescript": "^5.5.4" }, "dependencies": { - "@hocuspocus/extension-database": "^2.13.5", - "@hocuspocus/extension-logger": "^2.13.6", - "@hocuspocus/server": "^2.13.5" + "@hocuspocus/extension-database": "^3.4.4", + "@hocuspocus/extension-logger": "^3.4.4", + "@hocuspocus/server": "^3.4.4" } } diff --git a/src/hooks/access.ts b/src/hooks/access.ts index ca0734d..4cf8a80 100644 --- a/src/hooks/access.ts +++ b/src/hooks/access.ts @@ -1,22 +1,21 @@ -import { onAuthenticatePayload } from "@hocuspocus/server" -import { IncomingHttpHeaders } from "http" +import type { onAuthenticatePayload } from "@hocuspocus/server" +import type { IncomingHttpHeaders } from "http" import { HocusPocusError, logServerError } from "../common/errors" import { fetchStorage } from "../common/fetcher" function getSimpleHeaderValue(headers: IncomingHttpHeaders, name: string): string { const header = headers[name.toLowerCase()] - if (typeof header === "string") { - return header - } - logServerError(`Invalid auth header in ${name}: ${header}\n${headers}`) + if (typeof header === "string") return header + + logServerError(`Invalid auth header in ${name}: ${String(header)}\n${JSON.stringify(headers)}`) throw new HocusPocusError("Proxy Error") } type ProxyAuthHeadersT = { - "X-Session-ID": string, - "X-User-ID": string, - "X-Username": string, + "X-Session-ID": string + "X-User-ID": string + "X-Username": string } function parseProxyHeaders(requestHeaders: IncomingHttpHeaders): ProxyAuthHeadersT { @@ -27,42 +26,41 @@ function parseProxyHeaders(requestHeaders: IncomingHttpHeaders): ProxyAuthHeader } } -export async function verifyYDocAccess( - { documentName, requestHeaders, connection, token }: onAuthenticatePayload -): Promise<{} | ProxyAuthHeadersT> { +export async function verifyYDocAccess({ + documentName, + requestHeaders, + connectionConfig, + token, +}: onAuthenticatePayload): Promise<{} | ProxyAuthHeadersT> { if (documentName.startsWith("test/")) return {} const proxyAuthHeaders = parseProxyHeaders(requestHeaders) - if (documentName.includes("/")) { - throw new HocusPocusError("Invalid document name") - } - - const response = await fetchStorage( - `/ydocs/${documentName}/access-level/`, - { - headers: { - "X-Storage-Token": token, - ...proxyAuthHeaders, - } - } - ) + const response = await fetchStorage(`/ydocs/${documentName}/access-level/`, { + headers: { + "X-Storage-Token": token, + ...proxyAuthHeaders, + }, + }) if (response?.ok) { - const data = await response.json() + const data: unknown = await response.json() + if (data === "read-write") { return proxyAuthHeaders - } else if (data === "read-only") { - connection.readOnly = true + } + + if (data === "read-only") { + // ✅ правильно для onAuthenticatePayload в 3.4.x + connectionConfig.readOnly = true return proxyAuthHeaders - } else { - throw new HocusPocusError("Access Denied") } - } else if (response?.status === 403) { - throw new HocusPocusError("Invalid storage token") - } else if (response?.status === 404) { - throw new HocusPocusError("YDoc not found") - } else { - throw new HocusPocusError() + + throw new HocusPocusError("Access Denied") } -} + + if (response?.status === 403) throw new HocusPocusError("Invalid storage token") + if (response?.status === 404) throw new HocusPocusError("YDoc not found") + + throw new HocusPocusError() +} \ No newline at end of file diff --git a/src/hooks/download.ts b/src/hooks/download.ts index 3232909..934c50d 100644 --- a/src/hooks/download.ts +++ b/src/hooks/download.ts @@ -1,9 +1,11 @@ -import { fetchPayload } from "@hocuspocus/server" +import type { fetchPayload } from "@hocuspocus/server" -import { HocusPocusError } from "../common/errors" +import { HocusPocusError, logServerError } from "../common/errors" import { fetchStorageSafely } from "../common/fetcher" -export async function downloadYDocContent({ documentName }: fetchPayload): Promise { +export async function downloadYDocContent({ + documentName, +}: fetchPayload): Promise { if (documentName.startsWith("test/")) return null const response = await fetchStorageSafely(`/ydocs/${documentName}/content/`) @@ -11,11 +13,17 @@ export async function downloadYDocContent({ documentName }: fetchPayload): Promi throw new HocusPocusError() } - const arrayBuffer = await response.arrayBuffer() - - if (!arrayBuffer || arrayBuffer.byteLength === 0) { + const contentLength = response.headers.get("content-length") + if (contentLength === "0") { return null } - return new Uint8Array(arrayBuffer) -} + // ВАЖНО: читаем весь ответ целиком (не одним chunk) + const buf = await response.arrayBuffer().catch(() => null) + if (!buf) { + logServerError("Download: failed to read arrayBuffer") + throw new HocusPocusError() + } + + return new Uint8Array(buf) +} \ No newline at end of file diff --git a/src/hooks/store.ts b/src/hooks/store.ts index 97dd26a..4b329ac 100644 --- a/src/hooks/store.ts +++ b/src/hooks/store.ts @@ -1,18 +1,15 @@ -import { storePayload } from "@hocuspocus/server" +import type { storePayload } from "@hocuspocus/server" import { fetchStorageSafely } from "../common/fetcher" export async function storeYDocContent({ documentName, state }: storePayload): Promise { if (documentName.startsWith("test/")) return - await fetchStorageSafely( - `/ydocs/${documentName}/content/`, - { - method: "PUT", - headers: { - "Content-Type": "application/octet-stream" - }, - body: state, - } - ) -} + await fetchStorageSafely(`/ydocs/${documentName}/content/`, { + method: "PUT", + headers: { + "Content-Type": "application/octet-stream", + }, + body: new Uint8Array(state), + }) +} \ No newline at end of file diff --git a/src/hooks/tokenSync.ts b/src/hooks/tokenSync.ts new file mode 100644 index 0000000..76cd934 --- /dev/null +++ b/src/hooks/tokenSync.ts @@ -0,0 +1,84 @@ +import type { onTokenSyncPayload } from "@hocuspocus/server" +import type { IncomingHttpHeaders } from "http" + +import { HocusPocusError, logServerError } from "../common/errors" +import { fetchStorage } from "../common/fetcher" + +type ProxyAuthHeadersT = { + "X-Session-ID": string + "X-User-ID": string + "X-Username": string +} + +function getSimpleHeaderValue(headers: IncomingHttpHeaders, name: string): string { + const header = headers[name.toLowerCase()] + if (typeof header === "string") return header + + logServerError(`Invalid auth header in ${name}: ${String(header)}\n${JSON.stringify(headers)}`) + throw new HocusPocusError("Proxy Error") +} + +function parseProxyHeaders(requestHeaders: IncomingHttpHeaders): ProxyAuthHeadersT { + return { + "X-Session-ID": getSimpleHeaderValue(requestHeaders, "X-Session-ID"), + "X-User-ID": getSimpleHeaderValue(requestHeaders, "X-User-ID"), + "X-Username": getSimpleHeaderValue(requestHeaders, "X-Username"), + } +} + +function isProxyAuthHeaders(v: unknown): v is ProxyAuthHeadersT { + if (!v || typeof v !== "object") return false + const o = v as Record + return ( + typeof o["X-Session-ID"] === "string" && + typeof o["X-User-ID"] === "string" && + typeof o["X-Username"] === "string" + ) +} + +export async function syncTokenAccess({ + documentName, + requestHeaders, + token, + context, + connectionConfig, +}: onTokenSyncPayload): Promise<{ accessLevel: "read-write" | "read-only" }> { + if (documentName.startsWith("test/")) { + connectionConfig.readOnly = false + return { accessLevel: "read-write" } + } + + const proxyAuthHeaders: ProxyAuthHeadersT = isProxyAuthHeaders(context) + ? context + : parseProxyHeaders(requestHeaders) + + const response = await fetchStorage(`/ydocs/${documentName}/access-level/`, { + headers: { + "X-Storage-Token": token, + ...proxyAuthHeaders, + }, + }) + + if (!response) throw new HocusPocusError() + + if (response.ok) { + const level: unknown = await response.json() + + if (level === "read-write") { + connectionConfig.readOnly = false + return { accessLevel: "read-write" } + } + + if (level === "read-only") { + connectionConfig.readOnly = true + return { accessLevel: "read-only" } + } + + throw new HocusPocusError("Access Denied") + } + + if (response.status === 403) throw new HocusPocusError("Invalid storage token") + if (response.status === 404) throw new HocusPocusError("YDoc not found") + + throw new HocusPocusError() +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 230ac61..59d96f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,30 +1,31 @@ -import { Database } from "@hocuspocus/extension-database" -import { Logger } from "@hocuspocus/extension-logger" -import { Server } from "@hocuspocus/server" +import { Database } from "@hocuspocus/extension-database"; +import { Logger } from "@hocuspocus/extension-logger"; +import { Server } from "@hocuspocus/server"; -import { verifyYDocAccess } from "./hooks/access" -import { downloadYDocContent } from "./hooks/download" -import { storeYDocContent } from "./hooks/store" +import { verifyYDocAccess } from "./hooks/access"; +import { downloadYDocContent } from "./hooks/download"; +import { storeYDocContent } from "./hooks/store"; +import { syncTokenAccess } from "./hooks/tokenSync"; -const server = Server.configure({ +const server = new Server({ name: "xi.hocus", port: 1234, timeout: 30000, debounce: 5000, maxDebounce: 30000, quiet: true, + onAuthenticate: verifyYDocAccess, + onTokenSync: syncTokenAccess, extensions: [ - new Logger( - { - onChange: process.env.enable_change_logs === "true", - } - ), + new Logger({ + onChange: process.env.enable_change_logs === "true", + }), new Database({ fetch: downloadYDocContent, store: storeYDocContent, }), ], -}) +}); -server.listen() +server.listen();