hocuspocus lol

This commit is contained in:
R3D347HR4Y 2026-06-09 14:29:58 +02:00
parent d3c930cac6
commit cf087e637e
3 changed files with 695 additions and 0 deletions

View File

@ -0,0 +1,16 @@
{
"name": "ulti-hocuspocus",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node --env-file=.env server.mjs",
"dev": "node --env-file=.env --watch server.mjs"
},
"dependencies": {
"@hocuspocus/server": "^4.1.0",
"@hocuspocus/transformer": "^4.1.0",
"@tiptap/starter-kit": "^3.23.2",
"yjs": "^13.6.27"
}
}

View File

@ -0,0 +1,548 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@hocuspocus/server':
specifier: ^4.1.0
version: 4.1.0(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31)
'@hocuspocus/transformer':
specifier: ^4.1.0
version: 4.1.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(y-prosemirror@1.3.7(prosemirror-model@1.25.7)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31))(yjs@13.6.31)
'@tiptap/starter-kit':
specifier: ^3.23.2
version: 3.26.0
yjs:
specifier: ^13.6.27
version: 13.6.31
packages:
'@hocuspocus/common@4.1.0':
resolution: {integrity: sha512-SOBbu0GcBMbLo7IYRDZC6gvEcoATbEFIC5KqzvLanC6dZZLkv91pYEBli+Exs/G71ELL3iUjSwnaf+gksxcjFA==}
'@hocuspocus/server@4.1.0':
resolution: {integrity: sha512-mp0Of76kRkK/u+9pKXPLNV4NI2Cyjql/jVnudVb0z6xAd/i7mBDTK88p1I1sNoX9yvCvhY7lQlrvVx6sMcl8Xw==}
engines: {node: '>=22'}
peerDependencies:
y-protocols: ^1.0.6
yjs: ^13.6.8
'@hocuspocus/transformer@4.1.0':
resolution: {integrity: sha512-zjobRWsSipRM0hQvAZ65zPR0jrDcXgVHSFyuePtaNIZTBBeUbn1y6cLyBVQTIazE96UsuNwsZsBsMyFM7WdMUQ==}
peerDependencies:
'@tiptap/core': ^3.0.1
'@tiptap/pm': ^3.0.1
y-prosemirror: ^1.2.1
yjs: ^13.6.8
'@tiptap/core@3.26.0':
resolution: {integrity: sha512-7jTed/RirIVsp+lLdLvGzGqF3EBGpnGHGYKOwz6t28V2BIJLAFdUhfEVdWie7xPxQNWK0TP+fPlsqZS0vxfHBg==}
peerDependencies:
'@tiptap/pm': 3.26.0
'@tiptap/extension-blockquote@3.26.0':
resolution: {integrity: sha512-57accpka9affjiJRjP2LMNCDJDTMjTvO23RJCxtP43sp9cTIZ7YZnyDfRxCINTRBNK0X4o4w2+emOLyRwsk3CA==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-bold@3.26.0':
resolution: {integrity: sha512-j6CzTMofcGJ5iMoUgDRQpM0FkG00jBID3aKqs+UBbgtzLgtG/CI/91tMFv0XPC30LeFA895qYgvGZtHdejZhiQ==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-bullet-list@3.26.0':
resolution: {integrity: sha512-Jv7BX+kBB2wUIvO/NhuUjv+T3kAed2Tjr664fgQ2zKT6X69jKIkYuCCedrIHuOyaOQ+SBDuH9h51wYv/E97QgQ==}
peerDependencies:
'@tiptap/extension-list': 3.26.0
'@tiptap/extension-code-block@3.26.0':
resolution: {integrity: sha512-WPN9iZ3UjeDD2ckDzSs9tleibXv0cLj7j575NxuvjhwZTehYGNeYDSUTi+6DQUG6bKbhGg9Wcei5H0131vvJHg==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/pm': 3.26.0
'@tiptap/extension-code@3.26.0':
resolution: {integrity: sha512-VJYcV6rvjnENRTroOi9tDcHWW6G0pmCoRETwatlbgfDzuCmkTOwVwQjeJCXOVMMLNPzNiXZzibsRCUt+Azq/jw==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-document@3.26.0':
resolution: {integrity: sha512-Xhd6DCjaxCN4otQNvV6qra+XuoIjk6Vyjm87E5xn5Y/BMw7UGAG7LTkk3C2IEvxKrVZwJjalfxEqdHOgXQzVfw==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-dropcursor@3.26.0':
resolution: {integrity: sha512-rhAtp5J/YVDUCUIc5T7b0XY9dLeuI72JgOr53w0QQc0VA0uwbfTn7sx0LI9PDCE9uwmDH8H3snVRZRnAvlM8oA==}
peerDependencies:
'@tiptap/extensions': 3.26.0
'@tiptap/extension-gapcursor@3.26.0':
resolution: {integrity: sha512-SIe68SDwx2fozt/XKG0FhCwzz/yRN6Bvo4D5TqvfDg6NK3PQb1DS4GN9PilmJqbY+kXryuiWEEJOWi7HpO8SuQ==}
peerDependencies:
'@tiptap/extensions': 3.26.0
'@tiptap/extension-hard-break@3.26.0':
resolution: {integrity: sha512-baXvv/rtOTVd2Axjb7Zbb41Y9Qmy3U2fP7EHqLuhViqGxVX8LwQtP0PHUXEZkPokbBpRez10+dmOlvvsYFKAZQ==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-heading@3.26.0':
resolution: {integrity: sha512-qenEQEgzE5FjQay/H6iKOnwIt6DPO27cS+v0mGhXmrL1MjrNER4X0ZkATJbVd0WA6ffsAGaP44NKYDworGeidw==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-horizontal-rule@3.26.0':
resolution: {integrity: sha512-a+N/C4wkQV+/8x4ShdoiC2JdTW3Tw84C5cAloYLFMeaWmRa2me9ACSI+zo0SO9bbH9RJwsoRp7eaxBbk27eF1Q==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/pm': 3.26.0
'@tiptap/extension-italic@3.26.0':
resolution: {integrity: sha512-s8oFpH+0xmhvY19f452/2dExO3p1tjxh761g6cg4irwEUNUEAJKF2VLcjiaeOhNJ+pmnQYxb+VSkwkXvO+7vHQ==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-link@3.26.0':
resolution: {integrity: sha512-FA/d157aBxyvZFvsdc5eSu46tmHWXebAsqOQSvivOMyw+deBb00VlMsf+iD2J8+sekjbMYwx/hvbsu+xUoX43Q==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/pm': 3.26.0
'@tiptap/extension-list-item@3.26.0':
resolution: {integrity: sha512-MccGyj9HY4fkl04eIiFoTCkr8067Jku/VVdJNtRWW104Spx43C/7V2zpbxPvpcDhq3dW384fDxYXfpnb186xLg==}
peerDependencies:
'@tiptap/extension-list': 3.26.0
'@tiptap/extension-list-keymap@3.26.0':
resolution: {integrity: sha512-oBcj6qaNrRHQ+N0+pDuOVAQa4Nx9r8Cm5ANvyM2lTpoy60sOLOizuVvcvw1andVxbSrsZ1N/Sk+RZWyv1uoWyQ==}
peerDependencies:
'@tiptap/extension-list': 3.26.0
'@tiptap/extension-list@3.26.0':
resolution: {integrity: sha512-EM8woyHDNKLEQ+lWUEoDtA4KrwP6fei/mYX1NxseMzKHHo7LFecx7wk6sovAXZrUvdML/yFBihgiMiO5VIsfkg==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/pm': 3.26.0
'@tiptap/extension-ordered-list@3.26.0':
resolution: {integrity: sha512-ItLdFlcMsJz2vhbs1PcUfcN7nzVqGBOwPeCrrWxjrgscp+K3JoOGD+HhVVpBACOMwivUrlh8Ry5Ohvues2nOeA==}
peerDependencies:
'@tiptap/extension-list': 3.26.0
'@tiptap/extension-paragraph@3.26.0':
resolution: {integrity: sha512-h8fYLikg4qN39IghQ1y9g+zzUsgxBpDi5YS3IZbWoxWYYx1YqLL8nAvOiPr7Us14aQ0TjA2/xY7zqmyf29rX1A==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-strike@3.26.0':
resolution: {integrity: sha512-jUll3Pqhq7u1JKvO0B6USW/bmVmUsO6sRcxo/d5tXqLhS0tWAobOGoGU2IgwXnQDSjf+vF73RYD5tRGDLkRC9Q==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-text@3.26.0':
resolution: {integrity: sha512-yZXdevp3/8omGbb40Z52VfvID+tsRNhPQ1GNUToD56XSr2BjdJyAzAb9rWGgDKgVMUPLgJ26yT0O278RFqOKhA==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extension-underline@3.26.0':
resolution: {integrity: sha512-LlVkivH5cBwov/EMD8BL7ZRcU6YcadiSVIffLW1hyalw9YfhaFzoLxjtWhL7jiU/n2Kg+9dXSZxmV2hTeTwyrQ==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/extensions@3.26.0':
resolution: {integrity: sha512-4wajuqnO2X0+LVvsBjW/xk3/tmdb16bNL939QhicAay4YYqXITeV2v3XJsryzmG4L5GkK1yLxvRGk4aLoxWrnA==}
peerDependencies:
'@tiptap/core': 3.26.0
'@tiptap/pm': 3.26.0
'@tiptap/pm@3.26.0':
resolution: {integrity: sha512-q4RDeWwVrhOL0jJCGRgGxLSdjOYwzQ4h2InURZVhC66433ipcHd6f3bqSOhcXZ4r0sFmMNsuF7aZmUntjWLc7w==}
'@tiptap/starter-kit@3.26.0':
resolution: {integrity: sha512-o34EtMfqtBaljdmeElZsRG/067oGx9Zcq+j2GWo71KlZe22ga/ALexeTf1c+ETsjCxSTKR6eyQ4RZvz/2JpYfg==}
async-mutex@0.5.0:
resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
crossws@0.4.5:
resolution: {integrity: sha512-wUR89x/Rw7/8t+vn0CmGDYM9TD6VtARGb0LD5jq2wjtMy1vCP4M+sm6N6TigWeTYvnA8MoW29NqqXD0ep0rfBA==}
peerDependencies:
srvx: '>=0.11.5'
peerDependenciesMeta:
srvx:
optional: true
isomorphic.js@0.2.5:
resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==}
kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
lib0@0.2.117:
resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==}
engines: {node: '>=16'}
hasBin: true
linkifyjs@4.3.3:
resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==}
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
prosemirror-changeset@2.4.1:
resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==}
prosemirror-commands@1.7.1:
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
prosemirror-dropcursor@1.8.2:
resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
prosemirror-gapcursor@1.4.1:
resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
prosemirror-history@1.5.0:
resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
prosemirror-inputrules@1.5.1:
resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
prosemirror-keymap@1.2.3:
resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
prosemirror-model@1.25.7:
resolution: {integrity: sha512-A79aN8QEFUwI6cax8Yq4Rpcx1TJZ3Kagn+ii7qLo4/V8H3mMiHrhFyhTyHHvpSnOgMPpWiDGSwM3etwrxE50ug==}
prosemirror-schema-list@1.5.1:
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
prosemirror-state@1.4.4:
resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
prosemirror-tables@1.8.5:
resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
prosemirror-transform@1.12.0:
resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
prosemirror-view@1.41.8:
resolution: {integrity: sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==}
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
y-prosemirror@1.3.7:
resolution: {integrity: sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
peerDependencies:
prosemirror-model: ^1.7.1
prosemirror-state: ^1.2.3
prosemirror-view: ^1.9.10
y-protocols: ^1.0.1
yjs: ^13.5.38
y-protocols@1.0.7:
resolution: {integrity: sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
peerDependencies:
yjs: ^13.0.0
yjs@13.6.31:
resolution: {integrity: sha512-Eq+5BRfbeGyqGVrTJL3bEcr8gKkxPuyuoHmAwpk52fDb8kOVMrfVSTRPd6yiGgX5Fskb96qCRjzjbRjrL4YEnw==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
snapshots:
'@hocuspocus/common@4.1.0':
dependencies:
lib0: 0.2.117
'@hocuspocus/server@4.1.0(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31)':
dependencies:
'@hocuspocus/common': 4.1.0
async-mutex: 0.5.0
crossws: 0.4.5
kleur: 4.1.5
lib0: 0.2.117
y-protocols: 1.0.7(yjs@13.6.31)
yjs: 13.6.31
transitivePeerDependencies:
- srvx
'@hocuspocus/transformer@4.1.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(y-prosemirror@1.3.7(prosemirror-model@1.25.7)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31))(yjs@13.6.31)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
'@tiptap/starter-kit': 3.26.0
y-prosemirror: 1.3.7(prosemirror-model@1.25.7)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31)
yjs: 13.6.31
'@tiptap/core@3.26.0(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/pm': 3.26.0
'@tiptap/extension-blockquote@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-bold@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-bullet-list@3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-code-block@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
'@tiptap/extension-code@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-document@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-dropcursor@3.26.0(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-gapcursor@3.26.0(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-hard-break@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-heading@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-horizontal-rule@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
'@tiptap/extension-italic@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-link@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
linkifyjs: 4.3.3
'@tiptap/extension-list-item@3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-list-keymap@3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
'@tiptap/extension-ordered-list@3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-paragraph@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-strike@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-text@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-underline@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
'@tiptap/pm@3.26.0':
dependencies:
prosemirror-changeset: 2.4.1
prosemirror-commands: 1.7.1
prosemirror-dropcursor: 1.8.2
prosemirror-gapcursor: 1.4.1
prosemirror-history: 1.5.0
prosemirror-inputrules: 1.5.1
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.7
prosemirror-schema-list: 1.5.1
prosemirror-state: 1.4.4
prosemirror-tables: 1.8.5
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
'@tiptap/starter-kit@3.26.0':
dependencies:
'@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
'@tiptap/extension-blockquote': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-bold': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-bullet-list': 3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-code': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-code-block': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-document': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-dropcursor': 3.26.0(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-gapcursor': 3.26.0(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-hard-break': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-heading': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-horizontal-rule': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-italic': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-link': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/extension-list-item': 3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-list-keymap': 3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-ordered-list': 3.26.0(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
'@tiptap/extension-paragraph': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-strike': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-text': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extension-underline': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
'@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
'@tiptap/pm': 3.26.0
async-mutex@0.5.0:
dependencies:
tslib: 2.8.1
crossws@0.4.5: {}
isomorphic.js@0.2.5: {}
kleur@4.1.5: {}
lib0@0.2.117:
dependencies:
isomorphic.js: 0.2.5
linkifyjs@4.3.3: {}
orderedmap@2.1.1: {}
prosemirror-changeset@2.4.1:
dependencies:
prosemirror-transform: 1.12.0
prosemirror-commands@1.7.1:
dependencies:
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-dropcursor@1.8.2:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
prosemirror-gapcursor@1.4.1:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-view: 1.41.8
prosemirror-history@1.5.0:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
rope-sequence: 1.3.4
prosemirror-inputrules@1.5.1:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-keymap@1.2.3:
dependencies:
prosemirror-state: 1.4.4
w3c-keyname: 2.2.8
prosemirror-model@1.25.7:
dependencies:
orderedmap: 2.1.1
prosemirror-schema-list@1.5.1:
dependencies:
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-state@1.4.4:
dependencies:
prosemirror-model: 1.25.7
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
prosemirror-tables@1.8.5:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.8
prosemirror-transform@1.12.0:
dependencies:
prosemirror-model: 1.25.7
prosemirror-view@1.41.8:
dependencies:
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
rope-sequence@1.3.4: {}
tslib@2.8.1: {}
w3c-keyname@2.2.8: {}
y-prosemirror@1.3.7(prosemirror-model@1.25.7)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.31))(yjs@13.6.31):
dependencies:
lib0: 0.2.117
prosemirror-model: 1.25.7
prosemirror-state: 1.4.4
prosemirror-view: 1.41.8
y-protocols: 1.0.7(yjs@13.6.31)
yjs: 13.6.31
y-protocols@1.0.7(yjs@13.6.31):
dependencies:
lib0: 0.2.117
yjs: 13.6.31
yjs@13.6.31:
dependencies:
lib0: 0.2.117

View File

@ -0,0 +1,131 @@
import { Server } from "@hocuspocus/server"
import { TiptapTransformer } from "@hocuspocus/transformer"
import * as Y from "yjs"
import { createRequire } from "node:module"
const require = createRequire(import.meta.url)
const StarterKit = require("@tiptap/starter-kit").default
const PORT = Number(process.env.HOCUSPOCUS_PORT || 1234)
const SECRET = process.env.HOCUSPOCUS_SECRET || ""
const ULTID_URL = (process.env.ULTID_INTERNAL_URL || "http://127.0.0.1").replace(/\/$/, "")
function decodeJwtPayload(token) {
const parts = token.split(".")
if (parts.length < 2) return null
try {
const json = Buffer.from(parts[1], "base64url").toString("utf8")
return JSON.parse(json)
} catch {
return null
}
}
function hookContext(payload) {
return payload?.lastContext ?? payload?.context ?? {}
}
async function loadFromUltid(context) {
if (!context?.path || !context?.user) return null
const params = new URLSearchParams({ user: context.user, path: context.path })
const res = await fetch(`${ULTID_URL}/api/v1/richtext/internal/document?${params}`, {
headers: SECRET ? { "X-Hocuspocus-Secret": SECRET } : {},
})
if (res.status === 404) return null
if (!res.ok) throw new Error(`load failed: ${res.status}`)
const raw = await res.text()
if (!raw.trim()) return null
try {
const doc = JSON.parse(raw)
if (doc.yjsState) {
return Buffer.from(doc.yjsState, "base64")
}
if (doc.content) {
const ydoc = TiptapTransformer.toYdoc(doc.content, "default", [StarterKit])
return Buffer.from(Y.encodeStateAsUpdate(ydoc))
}
} catch (err) {
console.error("[onLoadDocument] parse", err)
}
return null
}
async function storeToUltid(context, document) {
if (!context?.path || !context?.user) {
throw new Error("store missing path or user in context")
}
const state = Buffer.from(Y.encodeStateAsUpdate(document)).toString("base64")
let tiptapJson = null
try {
tiptapJson = TiptapTransformer.fromYdoc(document, "default")
} catch {
/* optional */
}
const body = {
room: context.room ?? context.path,
path: context.path,
user: context.user,
sub: context.sub ?? "",
yjsState: state,
document: tiptapJson,
}
const res = await fetch(`${ULTID_URL}/api/v1/richtext/hooks/store`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(SECRET ? { "X-Hocuspocus-Secret": SECRET } : {}),
},
body: JSON.stringify(body),
})
if (!res.ok) {
const detail = await res.text().catch(() => "")
throw new Error(`store failed: ${res.status}${detail ? ` ${detail}` : ""}`)
}
}
const server = new Server({
port: PORT,
debounce: 3000,
maxDebounce: 10000,
async onAuthenticate(data) {
const { token, documentName } = data
if (!SECRET) return {}
const payload = decodeJwtPayload(token)
if (!payload?.room || payload.room !== documentName) {
console.error("[onAuthenticate] forbidden", { documentName, room: payload?.room })
throw new Error("forbidden")
}
if (payload.exp && payload.exp * 1000 < Date.now()) {
console.error("[onAuthenticate] token expired", { documentName })
throw new Error("token expired")
}
return {
room: payload.room,
path: payload.path,
user: payload.user,
sub: payload.sub,
name: payload.name,
mode: payload.mode,
}
},
async onLoadDocument(data) {
const ctx = hookContext(data)
return await loadFromUltid(ctx)
},
async onStoreDocument(data) {
const ctx = hookContext(data)
if (ctx.mode === "view") return
try {
await storeToUltid(ctx, data.document)
} catch (err) {
console.error("[onStoreDocument]", err)
}
},
})
server.listen()
console.log(`Hocuspocus listening on :${PORT}`)