UBERF-8968: Get rid of prosemirror in transactor ()

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2025-01-21 23:49:45 +07:00 committed by GitHub
parent 0534c2b90e
commit 4ec92d94b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 891 additions and 338 deletions
common/config/rush
packages
plugins
contact-resources
contact
pods/server/src
rush.json
server-plugins
activity-resources
chunter-resources
controlled-documents-resources
gmail-resources
notification-resources
notification/src
telegram-resources
time-resources
tracker-resources
server
services/ses/pod-ses
workers/transactor

View File

@ -1069,6 +1069,9 @@ importers:
'@rush-temp/text':
specifier: file:./projects/text.tgz
version: file:projects/text.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.24.2)(prosemirror-inputrules@1.4.0)(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.34.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))(utf-8-validate@6.0.4)
'@rush-temp/text-core':
specifier: file:./projects/text-core.tgz
version: file:projects/text-core.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))(utf-8-validate@6.0.4)
'@rush-temp/text-editor':
specifier: file:./projects/text-editor.tgz
version: file:projects/text-editor.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
@ -1273,9 +1276,6 @@ importers:
'@types/cors':
specifier: ^2.8.12
version: 2.8.17
'@types/crypto-js':
specifier: ^4.1.1
version: 4.2.2
'@types/csvtojson':
specifier: ^2.0.0
version: 2.0.3
@ -1394,8 +1394,8 @@ importers:
specifier: ^8.3.1
version: 8.3.4
'@types/web-push':
specifier: ~3.6.3
version: 3.6.3
specifier: ^3.6.4
version: 3.6.4
'@types/ws':
specifier: ^8.5.11
version: 8.5.11
@ -1490,7 +1490,7 @@ importers:
specifier: ^3.1.5
version: 3.1.8(encoding@0.1.13)
crypto-js:
specifier: ^4.1.1
specifier: ^4.2.0
version: 4.2.0
css-loader:
specifier: ^5.2.1
@ -1667,8 +1667,8 @@ importers:
specifier: ^1.1.1
version: 1.1.1
intl-messageformat:
specifier: ^9.7.1
version: 9.13.0
specifier: ^10.7.14
version: 10.7.14
itty-router:
specifier: ^5.0.18
version: 5.0.18
@ -1729,6 +1729,9 @@ importers:
markdown-it:
specifier: ^14.0.0
version: 14.0.0
md5.js:
specifier: ^1.3.5
version: 1.3.5
mermaid:
specifier: ~11.4.1
version: 11.4.1
@ -1928,7 +1931,7 @@ importers:
specifier: ^8.3.2
version: 8.3.2
web-push:
specifier: ~3.6.7
specifier: ^3.6.7
version: 3.6.7
webpack:
specifier: ^5.97.1
@ -2928,20 +2931,20 @@ packages:
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
engines: {node: '>=14'}
'@formatjs/ecma402-abstract@1.11.4':
resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==}
'@formatjs/ecma402-abstract@2.3.2':
resolution: {integrity: sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg==}
'@formatjs/fast-memoize@1.2.1':
resolution: {integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==}
'@formatjs/fast-memoize@2.2.6':
resolution: {integrity: sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==}
'@formatjs/icu-messageformat-parser@2.1.0':
resolution: {integrity: sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==}
'@formatjs/icu-messageformat-parser@2.11.0':
resolution: {integrity: sha512-Hp81uTjjdTk3FLh/dggU5NK7EIsVWc5/ZDWrIldmf2rBuPejuZ13CZ/wpVE2SToyi4EiroPTQ1XJcJuZFIxTtw==}
'@formatjs/icu-skeleton-parser@1.3.6':
resolution: {integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==}
'@formatjs/icu-skeleton-parser@1.8.12':
resolution: {integrity: sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg==}
'@formatjs/intl-localematcher@0.2.25':
resolution: {integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==}
'@formatjs/intl-localematcher@0.5.10':
resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==}
'@gar/promisify@1.1.3':
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
@ -3808,7 +3811,7 @@ packages:
version: 0.0.0
'@rush-temp/account@file:projects/account.tgz':
resolution: {integrity: sha512-aFUxA1twR0m+7sDVFXyKoeVQtVLMrr973LpLK6LHrRa+PD010X6+AyoOkkyVJKFN0Q6zu2Oqz88PsEt50Z2Mjg==, tarball: file:projects/account.tgz}
resolution: {integrity: sha512-xWdmeIZiSKL5eyvVHNUEnn/GXGsaNkayjxoDW+ydFB1GwUWExvSTA7OOs9mfG+IwLNU08GNKNGBYGM3XKZ2PsA==, tarball: file:projects/account.tgz}
version: 0.0.0
'@rush-temp/activity-assets@file:projects/activity-assets.tgz':
@ -3936,7 +3939,7 @@ packages:
version: 0.0.0
'@rush-temp/cloud-transactor@file:projects/cloud-transactor.tgz':
resolution: {integrity: sha512-sz4uAEfFOZfE7TOae+sv+RWlxunC4WlsQ7GTOX+kE+KDTNJK/r3d7+yIR/O3iDd4nsbLO/Al/9Z+wMZDXsmJcw==, tarball: file:projects/cloud-transactor.tgz}
resolution: {integrity: sha512-RmuejZDmaNRcHOCurZBKIHOgqyfTwUNtJdhzJEANVw3XAbuapCjlaCwmtK5bad2D6S1+VrHrXoF4vw4FamxZkw==, tarball: file:projects/cloud-transactor.tgz}
version: 0.0.0
'@rush-temp/collaboration@file:projects/collaboration.tgz':
@ -3956,11 +3959,11 @@ packages:
version: 0.0.0
'@rush-temp/contact-resources@file:projects/contact-resources.tgz':
resolution: {integrity: sha512-NwGEZHpomoqLYe2i/Z3Wm9xLB1Cl7okFToLBn51JWhFebje+20w47PFde2tUX77f5bfYuLJ0ykZbKnfDbFz9TA==, tarball: file:projects/contact-resources.tgz}
resolution: {integrity: sha512-sOoou1UaCHSN/qYHhEiWyiPICxmqP2p+ACDAeVzhU8fuUa5tejlzuANOdiEYqz4c8+u/NhlCQHd7G7G5WjMPpg==, tarball: file:projects/contact-resources.tgz}
version: 0.0.0
'@rush-temp/contact@file:projects/contact.tgz':
resolution: {integrity: sha512-sjRtgKszoagGm79PFQKes/sfRMcddH1qEMG3/xKuRLKlVcVJJ56twJaCCUeXP+7MsXMH5eEcS3sNCIdtznuASg==, tarball: file:projects/contact.tgz}
resolution: {integrity: sha512-59CabCDtmhSMmziQMA8E7sB/xLjRibcfTjx/EuYh4trtpfOawQ6lVZSljJ3Fr3huHHg4A8A14L71MC4PgPSepQ==, tarball: file:projects/contact.tgz}
version: 0.0.0
'@rush-temp/controlled-documents-assets@file:projects/controlled-documents-assets.tgz':
@ -4536,7 +4539,7 @@ packages:
version: 0.0.0
'@rush-temp/platform@file:projects/platform.tgz':
resolution: {integrity: sha512-GyVd1NUl00O73Bwq0b/KFXhAfrko5BnqcN9ak+WWmDjHqi4CsWUfYm6ol5B7rbC38dqXXD4yBad/iEgywLliXA==, tarball: file:projects/platform.tgz}
resolution: {integrity: sha512-BlslXnvEEebr3t7GEdTHD5HcoOTdez1j6Fb8Q6KctCunFjTeRtN5vtsutv2ZR+ChIGgZ5seZ6DOUFNM1MJf4ew==, tarball: file:projects/platform.tgz}
version: 0.0.0
'@rush-temp/pod-account@file:projects/pod-account.tgz':
@ -4592,7 +4595,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-ses@file:projects/pod-ses.tgz':
resolution: {integrity: sha512-P+HVwQR4SO6F4EXl5ReGR6hbng8CKsl4IK6Ww6u7yhNh1x7YGKzmV7UMRjSUc7qsNu4CzOhF7AYatCB602ANjA==, tarball: file:projects/pod-ses.tgz}
resolution: {integrity: sha512-gVCpGeUc+IsGfdCD84Sp+PcY21yRBkUhwfMUUC8VXcjf5MiayypLcc8pIGD2ufGGRJRAI3+clxIfiVc29ofBxw==, tarball: file:projects/pod-ses.tgz}
version: 0.0.0
'@rush-temp/pod-sign@file:projects/pod-sign.tgz':
@ -4740,7 +4743,7 @@ packages:
version: 0.0.0
'@rush-temp/server-activity-resources@file:projects/server-activity-resources.tgz':
resolution: {integrity: sha512-+UU7f65uCYtxuk56pj1bJ2ZaE6YHxVubtTfbsrLa8ZAGUOvWjrxY37B8j8GfcgGioL6PESsB12uEZIvghnNNaQ==, tarball: file:projects/server-activity-resources.tgz}
resolution: {integrity: sha512-pzbXI3TbbMXYv/qVmx97uHJD8IOrI7glY8HsD1Np0PAGqzGpWBOm1j6oHKjO/WvfXPCvbmBRBvir22/+IdeXKA==, tarball: file:projects/server-activity-resources.tgz}
version: 0.0.0
'@rush-temp/server-activity@file:projects/server-activity.tgz':
@ -4784,7 +4787,7 @@ packages:
version: 0.0.0
'@rush-temp/server-chunter-resources@file:projects/server-chunter-resources.tgz':
resolution: {integrity: sha512-Kpp8RTEJ+z5d8Rq7HbOhui8ZyV8OvKEXVkAXT80Ju/qhVnZ5Mnw/jk33dgwur1NPVjCWOK2SPECHf+eqzwXpBg==, tarball: file:projects/server-chunter-resources.tgz}
resolution: {integrity: sha512-anzs0C7AXicSpf9F+jjqdokc3cZunw5O8gC0095fQmEoOW94OmvTyboGrx8iK3i+iYam4Z6Bkt60rXRo3JvXZQ==, tarball: file:projects/server-chunter-resources.tgz}
version: 0.0.0
'@rush-temp/server-chunter@file:projects/server-chunter.tgz':
@ -4812,7 +4815,7 @@ packages:
version: 0.0.0
'@rush-temp/server-controlled-documents-resources@file:projects/server-controlled-documents-resources.tgz':
resolution: {integrity: sha512-g27+rhbyGhRdYikNstWia5wPng1EUiDp4DoEu51KVFrE4MOeaEXvB85Zb/4Jxg1RL4H7P6o7VRABeVqv6fkqsg==, tarball: file:projects/server-controlled-documents-resources.tgz}
resolution: {integrity: sha512-mYK9xbNtII74MyRDJjutIBvUvdXfVeGwaixq9uRTVlqQsmb8oYvqVsTLLfmjL/e/eb1nmUPtnWh7sogkGYU/yA==, tarball: file:projects/server-controlled-documents-resources.tgz}
version: 0.0.0
'@rush-temp/server-controlled-documents@file:projects/server-controlled-documents.tgz':
@ -4860,7 +4863,7 @@ packages:
version: 0.0.0
'@rush-temp/server-gmail-resources@file:projects/server-gmail-resources.tgz':
resolution: {integrity: sha512-V9M3+rquV4MPbPtmwWPdekguzA5WKJRDvU7xtpYKQSJ7J7hkLPKNah4GtSjin0opg6lTgtoAJUQaYLKheeCCbA==, tarball: file:projects/server-gmail-resources.tgz}
resolution: {integrity: sha512-B+iAjGb/dbs1BhVc2MmYZxv2LrHQk/IH3UazglCC7v3Nu5x6ny4oMPCHXqv7W7MMVjIzqt60Wsw7spkvyS1CSA==, tarball: file:projects/server-gmail-resources.tgz}
version: 0.0.0
'@rush-temp/server-gmail@file:projects/server-gmail.tgz':
@ -4912,7 +4915,7 @@ packages:
version: 0.0.0
'@rush-temp/server-notification-resources@file:projects/server-notification-resources.tgz':
resolution: {integrity: sha512-DhxiRHwKgwqkX4WP2ZmqGL7fHs0Ev3rtxHT/uylk5SPCPbKmBe20xyEzheSahP0Vm6kIS9XerGQuxGrI31eVBw==, tarball: file:projects/server-notification-resources.tgz}
resolution: {integrity: sha512-sLf+6eAqeGkx+28mefySh7RB9mMnEh5Cuw/Q036PALbzj/1tihPIIxlst9kMZAYgGr7WRaZGDGK4Pq2yxfCrPQ==, tarball: file:projects/server-notification-resources.tgz}
version: 0.0.0
'@rush-temp/server-notification@file:projects/server-notification.tgz':
@ -4972,7 +4975,7 @@ packages:
version: 0.0.0
'@rush-temp/server-telegram-resources@file:projects/server-telegram-resources.tgz':
resolution: {integrity: sha512-VsEVxoPM/hyelQa9/rQFzo+vbS8ixxU4RvVWdM0ae9HJuaEyszX4y9GheG/SDMa0NzCtyOOLf9KqL3ohGpkWRg==, tarball: file:projects/server-telegram-resources.tgz}
resolution: {integrity: sha512-2zGK/gI0mG8ZvEUYDK0lt8m525blawYUmk64vMfYF0K7hkOmKaE+aSECh6UJozxuxURpyNNzi/9R0SNnMyfXKw==, tarball: file:projects/server-telegram-resources.tgz}
version: 0.0.0
'@rush-temp/server-telegram@file:projects/server-telegram.tgz':
@ -4984,7 +4987,7 @@ packages:
version: 0.0.0
'@rush-temp/server-time-resources@file:projects/server-time-resources.tgz':
resolution: {integrity: sha512-WePYLAz/QHfFL+jMa/ZE6JQIvNYK4tGBs4ur6UBG3inJigtt9p7AR4PO+nr7dtGZqG8RT0T0TrGREcdV7pX5Gw==, tarball: file:projects/server-time-resources.tgz}
resolution: {integrity: sha512-N9YLO1krTVoyAYV78E8sW2mDjSuJ/wHD9EHPAZO6AHxtoFmzlMPJaLprjGf7bWIemiGyAZKcAFhmpu0RSlaYGA==, tarball: file:projects/server-time-resources.tgz}
version: 0.0.0
'@rush-temp/server-time@file:projects/server-time.tgz':
@ -5000,7 +5003,7 @@ packages:
version: 0.0.0
'@rush-temp/server-tracker-resources@file:projects/server-tracker-resources.tgz':
resolution: {integrity: sha512-VmLhXIzNRo1VeABIkcyYrRK7P2+OtDpXpYCrutGgmUqT+ioPHxaefntsS+wzItpblY78u4DJYSIkfOhtQXCTtg==, tarball: file:projects/server-tracker-resources.tgz}
resolution: {integrity: sha512-Npz6QfpPLP18rNxIGzNvjKf4QEhT8ywVdlYNt7e4QGvA1zetFDava2Cg0QpN31LZue8pTDCC3T94kBQMUiUfqg==, tarball: file:projects/server-tracker-resources.tgz}
version: 0.0.0
'@rush-temp/server-tracker@file:projects/server-tracker.tgz':
@ -5139,6 +5142,10 @@ packages:
resolution: {integrity: sha512-noV3nlBP0OvM6i4C5YUevkhltHxG/2bct4pusP3O3AEQ7Za+A4qwzpsE08pHfA9f9pedEG677GE4EmG9x61rVQ==, tarball: file:projects/tests-sanity.tgz}
version: 0.0.0
'@rush-temp/text-core@file:projects/text-core.tgz':
resolution: {integrity: sha512-piWZZp7T/zx+IKTP7fQGdY2schRbTEF2k0YuwXHkipp5MCA3BQLfy9VaZxAo5D4jhg0xySc1grVUfAqoOJIv8g==, tarball: file:projects/text-core.tgz}
version: 0.0.0
'@rush-temp/text-editor-assets@file:projects/text-editor-assets.tgz':
resolution: {integrity: sha512-HrJXNpWkSNwguy4rSL6Cihc3/1eTmi0KQAmyLix1ci88uOK/9kKga7lMnKAsuobrB9DjbuLgPpcEMNpavMi13w==, tarball: file:projects/text-editor-assets.tgz}
version: 0.0.0
@ -5156,7 +5163,7 @@ packages:
version: 0.0.0
'@rush-temp/text@file:projects/text.tgz':
resolution: {integrity: sha512-p2TRL6FgtqVBooDf+E5A4HB+45VS03IvNxudhC9gCotewS726ZLOxl2XdfeuYB2rHYbzdmmiG2zb8GbPwqTkOw==, tarball: file:projects/text.tgz}
resolution: {integrity: sha512-3aISTkQPGhn/Af7odByrglZazlbus1nu5kHvq77BZ0LJZ2lVZDZ5eTf0/EH6IpvGkmSvkEmkVJUl/bG/5Z7urA==, tarball: file:projects/text.tgz}
version: 0.0.0
'@rush-temp/theme@file:projects/theme.tgz':
@ -6254,8 +6261,8 @@ packages:
'@types/verror@1.10.10':
resolution: {integrity: sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==}
'@types/web-push@3.6.3':
resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
'@types/web-push@3.6.4':
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
'@types/webidl-conversions@7.0.3':
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
@ -9137,6 +9144,10 @@ packages:
has-unicode@2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
hash-base@3.1.0:
resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
engines: {node: '>=4'}
hasown@2.0.1:
resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
engines: {node: '>= 0.4'}
@ -9283,10 +9294,6 @@ packages:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
https-proxy-agent@7.0.4:
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.5:
resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
engines: {node: '>= 14'}
@ -9398,8 +9405,8 @@ packages:
resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==}
engines: {node: '>=10.13.0'}
intl-messageformat@9.13.0:
resolution: {integrity: sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==}
intl-messageformat@10.7.14:
resolution: {integrity: sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==}
invert-kv@2.0.0:
resolution: {integrity: sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==}
@ -10351,6 +10358,9 @@ packages:
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
engines: {node: '>=10'}
md5.js@1.3.5:
resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
@ -13590,7 +13600,7 @@ snapshots:
'@smithy/util-middleware': 3.0.0
'@smithy/util-retry': 3.0.0
'@smithy/util-utf8': 3.0.0
tslib: 2.6.2
tslib: 2.7.0
transitivePeerDependencies:
- aws-crt
@ -13891,7 +13901,7 @@ snapshots:
'@smithy/property-provider': 3.0.0
'@smithy/shared-ini-file-loader': 3.0.0
'@smithy/types': 3.0.0
tslib: 2.6.2
tslib: 2.7.0
'@aws-sdk/types@3.577.0':
dependencies:
@ -14541,29 +14551,31 @@ snapshots:
'@fastify/busboy@2.1.1': {}
'@formatjs/ecma402-abstract@1.11.4':
'@formatjs/ecma402-abstract@2.3.2':
dependencies:
'@formatjs/intl-localematcher': 0.2.25
tslib: 2.6.2
'@formatjs/fast-memoize': 2.2.6
'@formatjs/intl-localematcher': 0.5.10
decimal.js: 10.4.3
tslib: 2.7.0
'@formatjs/fast-memoize@1.2.1':
'@formatjs/fast-memoize@2.2.6':
dependencies:
tslib: 2.6.2
tslib: 2.7.0
'@formatjs/icu-messageformat-parser@2.1.0':
'@formatjs/icu-messageformat-parser@2.11.0':
dependencies:
'@formatjs/ecma402-abstract': 1.11.4
'@formatjs/icu-skeleton-parser': 1.3.6
tslib: 2.6.2
'@formatjs/ecma402-abstract': 2.3.2
'@formatjs/icu-skeleton-parser': 1.8.12
tslib: 2.7.0
'@formatjs/icu-skeleton-parser@1.3.6':
'@formatjs/icu-skeleton-parser@1.8.12':
dependencies:
'@formatjs/ecma402-abstract': 1.11.4
tslib: 2.6.2
'@formatjs/ecma402-abstract': 2.3.2
tslib: 2.7.0
'@formatjs/intl-localematcher@0.2.25':
'@formatjs/intl-localematcher@0.5.10':
dependencies:
tslib: 2.6.2
tslib: 2.7.0
'@gar/promisify@1.1.3': {}
@ -15760,10 +15772,12 @@ snapshots:
'@rush-temp/account@file:projects/account.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@types/crypto-js': 4.2.2
'@types/jest': 29.5.12
'@types/otp-generator': 4.0.2
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
crypto-js: 4.2.0
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
@ -16925,9 +16939,11 @@ snapshots:
'@rush-temp/contact-resources@file:projects/contact-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(postcss-load-config@4.0.2(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@types/crypto-js': 4.2.2
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
crypto-js: 4.2.0
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
@ -16969,13 +16985,13 @@ snapshots:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
crypto-js: 4.2.0
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
md5.js: 1.3.5
prettier: 3.2.5
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.3.3)
typescript: 5.3.3
@ -20207,7 +20223,7 @@ snapshots:
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
intl-messageformat: 9.13.0
intl-messageformat: 10.7.14
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
prettier: 3.2.5
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.3.3)
@ -20816,6 +20832,7 @@ snapshots:
'@types/express': 4.17.21
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@types/web-push': 3.6.4
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
aws-sdk: 2.1664.0
@ -20835,6 +20852,7 @@ snapshots:
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.3.3)
ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3)
typescript: 5.3.3
web-push: 3.6.7
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
@ -23122,7 +23140,6 @@ snapshots:
'@rush-temp/server-notification-resources@file:projects/server-notification-resources.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@types/jest': 29.5.12
'@types/web-push': 3.6.3
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
@ -23134,7 +23151,6 @@ snapshots:
prettier: 3.2.5
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.3.3)
typescript: 5.3.3
web-push: 3.6.7
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
@ -24705,6 +24721,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@rush-temp/text-core@file:projects/text-core.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@20.11.19)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))(utf-8-validate@6.0.4)':
dependencies:
'@types/jest': 29.5.12
'@types/markdown-it': 13.0.8
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.6.2)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.6.2)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
fast-equals: 5.0.1
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
prettier: 3.2.5
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.6.2)
typescript: 5.6.2
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- bufferutil
- canvas
- esbuild
- node-notifier
- supports-color
- ts-node
- utf-8-validate
'@rush-temp/text-editor-assets@file:projects/text-editor-assets.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@types/jest': 29.5.12
@ -25866,7 +25913,7 @@ snapshots:
'@aws-crypto/crc32': 3.0.0
'@smithy/types': 3.0.0
'@smithy/util-hex-encoding': 3.0.0
tslib: 2.6.2
tslib: 2.7.0
'@smithy/eventstream-serde-browser@3.0.0':
dependencies:
@ -26952,7 +26999,7 @@ snapshots:
'@types/verror@1.10.10':
optional: true
'@types/web-push@3.6.3':
'@types/web-push@3.6.4':
dependencies:
'@types/node': 20.11.19
@ -29182,7 +29229,7 @@ snapshots:
dot-case@3.0.4:
dependencies:
no-case: 3.0.4
tslib: 2.6.2
tslib: 2.7.0
dot-prop@6.0.1:
dependencies:
@ -30656,6 +30703,12 @@ snapshots:
has-unicode@2.0.1: {}
hash-base@3.1.0:
dependencies:
inherits: 2.0.4
readable-stream: 3.6.2
safe-buffer: 5.2.1
hasown@2.0.1:
dependencies:
function-bind: 1.1.2
@ -30849,13 +30902,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.4:
dependencies:
agent-base: 7.1.1
debug: 4.3.5
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.1
@ -30956,12 +31002,12 @@ snapshots:
interpret@3.1.1: {}
intl-messageformat@9.13.0:
intl-messageformat@10.7.14:
dependencies:
'@formatjs/ecma402-abstract': 1.11.4
'@formatjs/fast-memoize': 1.2.1
'@formatjs/icu-messageformat-parser': 2.1.0
tslib: 2.6.2
'@formatjs/ecma402-abstract': 2.3.2
'@formatjs/fast-memoize': 2.2.6
'@formatjs/icu-messageformat-parser': 2.11.0
tslib: 2.7.0
invert-kv@2.0.0: {}
@ -32189,6 +32235,12 @@ snapshots:
escape-string-regexp: 4.0.0
optional: true
md5.js@1.3.5:
dependencies:
hash-base: 3.1.0
inherits: 2.0.4
safe-buffer: 5.2.1
md5@2.3.0:
dependencies:
charenc: 0.0.2
@ -32911,7 +32963,7 @@ snapshots:
pascal-case@3.1.2:
dependencies:
no-case: 3.0.4
tslib: 2.6.2
tslib: 2.7.0
passport-custom@1.1.1:
dependencies:
@ -35104,7 +35156,7 @@ snapshots:
dependencies:
asn1.js: 5.4.1
http_ece: 1.2.0
https-proxy-agent: 7.0.4
https-proxy-agent: 7.0.5
jws: 4.0.0
minimist: 1.2.8
transitivePeerDependencies:

View File

@ -37,7 +37,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"intl-messageformat": "^9.7.1"
"intl-messageformat": "^10.7.14"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {

View File

@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}

View File

@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}

View File

@ -0,0 +1,49 @@
{
"name": "@hcengineering/text-core",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"files": [
"lib/**/*",
"types/**/*",
"tsconfig.json"
],
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"test": "jest --passWithNoTests --silent",
"build:watch": "compile",
"format": "format src",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"@types/markdown-it": "~13.0.0",
"jest-environment-jsdom": "^29.7.0"
},
"dependencies": {
"@hcengineering/core": "^0.6.32",
"fast-equals": "^5.0.1"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}

View File

@ -0,0 +1,20 @@
//
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
export * from './markup/dsl'
export * from './markup/model'
export * from './markup/reference'
export * from './markup/traverse'
export * from './markup/utils'

View File

@ -0,0 +1,402 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { Markup } from '@hcengineering/core'
import { deepEqual } from 'fast-equals'
import { nodeDoc, nodeParagraph, nodeText } from './dsl'
import { MarkupMark, MarkupMarkType, MarkupNode, MarkupNodeType, emptyMarkupNode, type AttrValue } from './model'
import { traverseNode } from './traverse'
/** @public */
export const EmptyMarkup: Markup = jsonToMarkup(emptyMarkupNode())
/** @public */
export function isEmptyMarkup (markup: Markup | undefined): boolean {
if (markup === undefined || markup === null || markup === '') {
return true
}
return isEmptyNode(markupToJSON(markup))
}
/** @public */
export function areEqualMarkups (markup1: Markup, markup2: Markup): boolean {
if (markup1 === markup2) {
return true
}
const node1 = markupToJSON(markup1)
const node2 = markupToJSON(markup2)
if (isEmptyNode(node1) && isEmptyNode(node2)) {
return true
}
return equalNodes(node1, node2)
}
/** @public */
export function areEqualJson (json1: MarkupNode, json2: MarkupNode): boolean {
return equalNodes(json1, json2)
}
function equalNodes (node1: MarkupNode, node2: MarkupNode): boolean {
if (node1.type !== node2.type) return false
const text1 = node1.text ?? ''
const text2 = node2.text ?? ''
if (text1 !== text2) return false
if (!equalArrays(node1.content, node2.content, equalNodes)) return false
if (!equalArrays(node1.marks, node2.marks, equalMarks)) return false
if (!equalRecords(node1.attrs, node2.attrs)) return false
return true
}
function equalArrays<T> (a: T[] | undefined, b: T[] | undefined, equal: (a: T, b: T) => boolean): boolean {
if (a === b) return true
const arr1 = a ?? []
const arr2 = b ?? []
if (arr1.length !== arr2.length) return false
return arr1.every((item1, i) => equal(item1, arr2[i]))
}
function equalRecords (a: Record<string, any> | undefined, b: Record<string, any> | undefined): boolean {
if (a === b) return true
a = Object.fromEntries(Object.entries(a ?? {}).filter(([_, v]) => v != null))
b = Object.fromEntries(Object.entries(b ?? {}).filter(([_, v]) => v != null))
return deepEqual(a, b)
}
export function equalMarks (a: MarkupMark, b: MarkupMark): boolean {
return a.type === b.type && equalRecords(a.attrs, b.attrs)
}
const emptyNodes = [MarkupNodeType.hard_break]
const nonEmptyNodes = [
MarkupNodeType.horizontal_rule,
MarkupNodeType.image,
MarkupNodeType.reference,
MarkupNodeType.subLink,
MarkupNodeType.table
]
/** @public */
export function isEmptyNode (node: MarkupNode): boolean {
if (emptyNodes.includes(node.type)) return true
if (nonEmptyNodes.includes(node.type)) return false
if (node.text !== undefined && node.text?.trim().length > 0) return false
const content = node.content ?? []
return content.every(isEmptyNode)
}
// Markup
/** @public */
export function jsonToMarkup (json: MarkupNode): Markup {
return JSON.stringify(json)
}
/** @public */
export function markupToJSON (markup: Markup): MarkupNode {
if (markup == null || markup === '') {
return emptyMarkupNode()
}
try {
// Ideally Markup should contain only serialized JSON
// But there seem to be some cases when it contains HTML or plain text
// So we need to handle those cases and produce valid MarkupNode
if (markup.startsWith('{')) {
return JSON.parse(markup) as MarkupNode
} else {
return nodeDoc(nodeParagraph(nodeText(markup)))
}
} catch (error) {
return emptyMarkupNode()
}
}
// UTILS
const ELLIPSIS_CHAR = '…'
const WHITESPACE = ' '
/** @public */
export function stripTags (markup: Markup, textLimit = 0): string {
const parsed = markupToJSON(markup)
const textParts: string[] = []
let charCount = 0
let isHardStop = false
const pushText = (text: string): void => {
if (textLimit > 0 && charCount + text.length > textLimit) {
const toAddCount = textLimit - charCount
const textPart = text.substring(0, toAddCount)
textParts.push(textPart)
textParts.push(ELLIPSIS_CHAR)
isHardStop = true
} else {
textParts.push(text)
charCount += text.length
}
}
traverseNode(parsed, (node, parent): boolean => {
if (isHardStop) {
return false
}
if (node.type === MarkupNodeType.text) {
const text = node.text ?? ''
pushText(text)
return false
} else if (
node.type === MarkupNodeType.paragraph ||
node.type === MarkupNodeType.table ||
node.type === MarkupNodeType.doc ||
node.type === MarkupNodeType.blockquote
) {
if (textParts.length > 0 && textParts[textParts.length - 1] !== WHITESPACE) {
textParts.push(WHITESPACE)
charCount++
}
} else if (node.type === MarkupNodeType.reference) {
const label = `${node.attrs?.label ?? ''}`
pushText(label.length > 0 ? `@${label}` : '')
}
return true
})
const result = textParts.join('')
return result
}
class NodeBuilder {
textParts: string[] = []
constructor (private readonly addTags: boolean) {}
addText (text: string): void {
this.textParts.push(text)
}
addTag (text: string, newLine: boolean = false): void {
if (this.addTags) {
this.textParts.push(text)
}
if (!this.addTags && newLine) {
this.textParts.push('\n')
}
}
toText (): string {
return this.textParts.join('')
}
}
function addMark (builder: NodeBuilder, mark?: MarkupMark, next?: () => void): void {
if (mark != null) {
const attrs = mark.attrs ?? {}
if (mark.type === MarkupMarkType.bold) {
builder.addTag('<strong>')
next?.()
builder.addTag('</strong>')
} else if (mark.type === MarkupMarkType.code) {
builder.addTag('<code class="proseCode">')
next?.()
builder.addTag('</code>')
} else if (mark.type === MarkupMarkType.em) {
builder.addTag('<em>')
next?.()
builder.addTag('</em>')
} else if (mark.type === MarkupMarkType.link) {
builder.addTag(`<a href=${attrs.href} target=${attrs.target}>`)
next?.()
builder.addTag('</a>')
} else if (mark.type === MarkupMarkType.strike) {
builder.addTag('<s>')
next?.()
builder.addTag('</s>')
} else if (mark.type === MarkupMarkType.underline) {
builder.addTag('<u>')
next?.()
builder.addTag('</u>')
} else {
builder.addTag(`unknown mark: "${mark.type as string}"`, false)
next?.()
}
}
}
function addMarks (builder: NodeBuilder, marks: MarkupMark[], next?: () => void): void {
if (marks.length > 0) {
const mark = marks[0]
const others = marks.slice(1)
if (others.length > 0) {
addMark(builder, mark, () => {
addMarks(builder, others, next)
})
}
}
}
function addNodeContent (builder: NodeBuilder, node?: MarkupNode): void {
if (node == null) return
const attrs = node.attrs ?? {}
const nodes = node.content ?? []
if (node.type === MarkupNodeType.doc) {
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
} else if (node.type === MarkupNodeType.text) {
builder.addText(node.text ?? '')
} else if (node.type === MarkupNodeType.paragraph) {
builder.addTag('<p>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</p>')
} else if (node.type === MarkupNodeType.blockquote) {
builder.addTag('<blockquote>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</blockquote>')
} else if (node.type === MarkupNodeType.horizontal_rule) {
builder.addTag('<hr/>')
} else if (node.type === MarkupNodeType.heading) {
const level = toNumber(node.attrs?.level) ?? 1
builder.addTag(`<h${level}>`)
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag(`</h${level}>`)
} else if (node.type === MarkupNodeType.code_block) {
builder.addTag('<pre><code>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</code></pre>')
} else if (node.type === MarkupNodeType.image) {
const src = toString(attrs.src)
const alt = toString(attrs.alt)
builder.addText(`<img src="${src}" alt="${alt}"/>`)
} else if (node.type === MarkupNodeType.reference) {
const label = toString(attrs.label)
builder.addText(label !== undefined ? `@${label}` : '')
} else if (node.type === MarkupNodeType.hard_break) {
builder.addTag('<br/>')
} else if (node.type === MarkupNodeType.ordered_list) {
builder.addTag('<ol>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</ol>')
} else if (node.type === MarkupNodeType.bullet_list) {
builder.addTag('<ul>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</ul>')
} else if (node.type === MarkupNodeType.list_item) {
builder.addTag('<li>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</li>')
} else if (node.type === MarkupNodeType.subLink) {
builder.addTag('<sub>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</sub>')
} else if (node.type === MarkupNodeType.table) {
builder.addTag('<table><tbody>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</tbody></table>')
} else if (node.type === MarkupNodeType.table_row) {
builder.addTag('<tr>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</tr>')
} else if (node.type === MarkupNodeType.table_cell) {
builder.addTag('<td>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</td>')
} else if (node.type === MarkupNodeType.table_header) {
builder.addTag('<th>')
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
builder.addTag('</th>')
} else {
builder.addText(`unknown node: "${node.type}"`)
nodes.forEach((childNode) => {
addNode(builder, childNode)
})
}
}
function addNode (builder: NodeBuilder, node: MarkupNode): void {
const marks = node.marks ?? []
if (marks.length > 0) {
addMarks(builder, marks, () => {
addNodeContent(builder, node)
})
} else {
addNodeContent(builder, node)
}
}
function toString (value: AttrValue | undefined): string | undefined {
return value !== undefined ? `${value}` : undefined
}
function toNumber (value: AttrValue | undefined): number | undefined {
if (typeof value === 'boolean') {
return value ? 1 : 0
}
return value !== undefined ? (typeof value === 'string' ? parseInt(value) : value) : undefined
}
export function markupToHTML (markup: Markup): string {
const jsonModel = markupToJSON(markup)
const builder = new NodeBuilder(true)
addNode(builder, jsonModel)
return builder.toText()
}
export function markupToText (markup: Markup): string {
const jsonModel = markupToJSON(markup)
const builder = new NodeBuilder(false)
addNode(builder, jsonModel)
return builder.toText()
}

View File

@ -0,0 +1,10 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}

View File

@ -40,6 +40,7 @@
},
"dependencies": {
"@hcengineering/core": "^0.6.32",
"@hcengineering/text-core": "^0.6.0",
"@tiptap/core": "^2.6.6",
"@tiptap/html": "^2.6.6",
"@tiptap/pm": "^2.6.6",

View File

@ -14,13 +14,9 @@
//
export * from './extensions'
export * from './markup/dsl'
export * from './markup/model'
export * from './markup/reference'
export * from './markup/traverse'
export * from './markup/utils'
export * from '@hcengineering/text-core'
export * from './nodes'
// export * from './ydoc'
export * from './markup/utils'
export * from './marks/code'
export * from './marks/colors'
export * from './marks/noteBase'

View File

@ -1,9 +1,8 @@
import { MarkupNode, markupToJSON } from '@hcengineering/text-core'
import { Extensions } from '@tiptap/core'
import { defaultExtensions } from '../extensions'
import { MarkdownParser } from './parser'
import { MarkdownState, storeMarks, storeNodes } from './serializer'
import { MarkupNode } from '../markup/model'
import { markupToJSON } from '../markup/utils'
/**
* @public

View File

@ -1,5 +1,5 @@
import { deepEqual } from 'fast-equals'
import { MarkupMark, MarkupMarkType, MarkupNode } from '../markup/model'
import { MarkupMark, MarkupMarkType, MarkupNode } from '@hcengineering/text-core'
export function traverseMarks (node: MarkupNode, f: (el: MarkupMark) => void): void {
node.marks?.forEach(f)

View File

@ -1,4 +1,4 @@
import { Attrs, MarkupNode } from '../markup/model'
import { Attrs, MarkupNode } from '@hcengineering/text-core'
export function traverseMarkupNode (node: MarkupNode, f: (el: MarkupNode) => void): void {
f(node)

View File

@ -3,10 +3,10 @@ import MarkdownIt, { type Token } from 'markdown-it'
import type { RuleCore } from 'markdown-it/lib/parser_core'
import type StateCore from 'markdown-it/lib/rules_core/state_core'
import { Attrs, AttrValue, MarkupMark, MarkupMarkType, MarkupNode, MarkupNodeType } from '@hcengineering/text-core'
import { htmlToJSON } from '../markup/utils'
import { addToSet, removeFromSet, sameSet } from './marks'
import { messageContent } from './node'
import { Attrs, AttrValue, MarkupMark, MarkupMarkType, MarkupNode, MarkupNodeType } from '../markup/model'
import { htmlToJSON } from '../markup/utils'
interface ParsingBlockRule {
block: MarkupNodeType

View File

@ -1,8 +1,8 @@
import { MarkupMark, MarkupNode, MarkupNodeType } from '@hcengineering/text-core'
import { generateHTML } from '@tiptap/html'
import { defaultExtensions } from '../extensions'
import { isInSet, markEq } from './marks'
import { messageContent, nodeAttrs } from './node'
import { MarkupMark, MarkupNode, MarkupNodeType } from '../markup/model'
import { defaultExtensions } from '../extensions'
type FirstDelim = (i: number, attrs?: Record<string, any>, parentAttrs?: Record<string, any>) => string
interface IState {

View File

@ -1,5 +1,13 @@
import { nodeDoc, nodeImage, nodeParagraph, nodeReference, nodeText, markLink, markUnderline } from '../dsl'
import { MarkupNodeType } from '../model'
import {
markLink,
markUnderline,
MarkupNodeType,
nodeDoc,
nodeImage,
nodeParagraph,
nodeReference,
nodeText
} from '@hcengineering/text-core'
import { jsonToHTML } from '../utils'
describe('dsl', () => {

View File

@ -17,28 +17,34 @@
// limitations under the License.
//
import { Editor, getSchema } from '@tiptap/core'
import { MarkupMarkType, MarkupNode, MarkupNodeType } from '../model'
import {
areEqualMarkups,
isEmptyMarkup,
isEmptyNode,
jsonToMarkup,
MarkupMarkType,
MarkupNode,
MarkupNodeType,
markupToJSON,
nodeDoc,
nodeParagraph,
nodeText,
markupToHTML
} from '@hcengineering/text-core'
import { Editor, getSchema } from '@tiptap/core'
import { ServerKit } from '../../kits/server-kit'
import {
getMarkup,
htmlToJSON,
htmlToMarkup,
htmlToPmNode,
isEmptyMarkup,
isEmptyNode,
jsonToHTML,
jsonToMarkup,
jsonToText,
markupToHTML,
markupToJSON,
markupToPmNode,
pmNodeToHTML,
pmNodeToJSON,
pmNodeToMarkup
} from '../utils'
import { ServerKit } from '../../kits/server-kit'
import { nodeDoc, nodeParagraph, nodeText } from '../dsl'
// mock tiptap functions
jest.mock('@tiptap/html', () => ({

View File

@ -18,13 +18,18 @@ import { Editor, Extensions, getSchema } from '@tiptap/core'
import { generateHTML, generateJSON } from '@tiptap/html'
import { Node as ProseMirrorNode, Schema } from '@tiptap/pm/model'
import { deepEqual } from 'fast-equals'
import {
MarkupNode,
emptyMarkupNode,
jsonToMarkup,
markupToJSON,
nodeDoc,
nodeParagraph,
nodeText
} from '@hcengineering/text-core'
import { defaultExtensions } from '../extensions'
import { nodeDoc, nodeParagraph, nodeText } from './dsl'
import { MarkupMark, MarkupNode, MarkupNodeType, emptyMarkupNode } from './model'
/** @public */
export const EmptyMarkup: Markup = jsonToMarkup(emptyMarkupNode())
const defaultSchema = getSchema(defaultExtensions)
/** @public */
@ -32,88 +37,6 @@ export function getMarkup (editor?: Editor): Markup {
return jsonToMarkup(editor?.getJSON() as MarkupNode)
}
/** @public */
export function isEmptyMarkup (markup: Markup | undefined): boolean {
if (markup === undefined || markup === null || markup === '') {
return true
}
return isEmptyNode(markupToJSON(markup))
}
/** @public */
export function areEqualMarkups (markup1: Markup, markup2: Markup): boolean {
if (markup1 === markup2) {
return true
}
const node1 = markupToJSON(markup1)
const node2 = markupToJSON(markup2)
if (isEmptyNode(node1) && isEmptyNode(node2)) {
return true
}
return equalNodes(node1, node2)
}
/** @public */
export function areEqualJson (json1: MarkupNode, json2: MarkupNode): boolean {
return equalNodes(json1, json2)
}
function equalNodes (node1: MarkupNode, node2: MarkupNode): boolean {
if (node1.type !== node2.type) return false
const text1 = node1.text ?? ''
const text2 = node2.text ?? ''
if (text1 !== text2) return false
if (!equalArrays(node1.content, node2.content, equalNodes)) return false
if (!equalArrays(node1.marks, node2.marks, equalMarks)) return false
if (!equalRecords(node1.attrs, node2.attrs)) return false
return true
}
function equalArrays<T> (a: T[] | undefined, b: T[] | undefined, equal: (a: T, b: T) => boolean): boolean {
if (a === b) return true
const arr1 = a ?? []
const arr2 = b ?? []
if (arr1.length !== arr2.length) return false
return arr1.every((item1, i) => equal(item1, arr2[i]))
}
function equalRecords (a: Record<string, any> | undefined, b: Record<string, any> | undefined): boolean {
if (a === b) return true
a = Object.fromEntries(Object.entries(a ?? {}).filter(([_, v]) => v != null))
b = Object.fromEntries(Object.entries(b ?? {}).filter(([_, v]) => v != null))
return deepEqual(a, b)
}
function equalMarks (a: MarkupMark, b: MarkupMark): boolean {
return a.type === b.type && equalRecords(a.attrs, b.attrs)
}
const emptyNodes = [MarkupNodeType.hard_break]
const nonEmptyNodes = [
MarkupNodeType.horizontal_rule,
MarkupNodeType.image,
MarkupNodeType.reference,
MarkupNodeType.subLink,
MarkupNodeType.table
]
/** @public */
export function isEmptyNode (node: MarkupNode): boolean {
if (emptyNodes.includes(node.type)) return true
if (nonEmptyNodes.includes(node.type)) return false
if (node.text !== undefined && node.text?.trim().length > 0) return false
const content = node.content ?? []
return content.every(isEmptyNode)
}
// Markup
/** @public */
@ -127,15 +50,8 @@ export function markupToPmNode (markup: Markup, schema?: Schema, extensions?: Ex
return jsonToPmNode(json, schema, extensions)
}
// JSON
/** @public */
export function jsonToMarkup (json: MarkupNode): Markup {
return JSON.stringify(json)
}
/** @public */
export function markupToJSON (markup: Markup): MarkupNode {
export function markupHtmlToJSON (markup: Markup): MarkupNode {
if (markup == null || markup === '') {
return emptyMarkupNode()
}
@ -178,10 +94,10 @@ export function pmNodeToText (node: ProseMirrorNode): string {
return jsonToText(node.toJSON())
}
export function markupToText (markup: Markup, schema?: Schema, extensions?: Extensions): string {
const pmNode = markupToPmNode(markup, schema, extensions)
return pmNode.textBetween(0, pmNode.content.size, '\n', '')
}
// export function markupToText (markup: Markup, schema?: Schema, extensions?: Extensions): string {
// const pmNode = markupToPmNode(markup, schema, extensions)
// return pmNode.textBetween(0, pmNode.content.size, '\n', '')
// }
// HTML
@ -191,11 +107,11 @@ export function htmlToMarkup (html: string, extensions?: Extensions): Markup {
return jsonToMarkup(json)
}
/** @public */
export function markupToHTML (markup: Markup, extensions?: Extensions): string {
const json = markupToJSON(markup)
return jsonToHTML(json, extensions)
}
// /** @public */
// export function markupToHTML (markup: Markup, extensions?: Extensions): string {
// const json = markupToJSON(markup)
// return jsonToHTML(json, extensions)
// }
/** @public */
export function htmlToJSON (html: string, extensions?: Extensions): MarkupNode {
@ -221,55 +137,3 @@ export function pmNodeToHTML (node: ProseMirrorNode, extensions?: Extensions): s
extensions ??= defaultExtensions
return generateHTML(node.toJSON(), extensions)
}
// UTILS
const ELLIPSIS_CHAR = '…'
const WHITESPACE = ' '
/** @public */
export function stripTags (markup: Markup, textLimit = 0, extensions: Extensions | undefined = undefined): string {
const schema = extensions === undefined ? defaultSchema : getSchema(extensions)
const parsed = markupToPmNode(markup, schema)
const textParts: string[] = []
let charCount = 0
let isHardStop = false
const pushText = (text: string): void => {
if (textLimit > 0 && charCount + text.length > textLimit) {
const toAddCount = textLimit - charCount
const textPart = text.substring(0, toAddCount)
textParts.push(textPart)
textParts.push(ELLIPSIS_CHAR)
isHardStop = true
} else {
textParts.push(text)
charCount += text.length
}
}
parsed.descendants((node, _pos, parent): boolean => {
if (isHardStop) {
return false
}
if (node.type.isText) {
const text = node.text ?? ''
pushText(text)
return false
} else if (node.type.isBlock) {
if (textParts.length > 0 && textParts[textParts.length - 1] !== WHITESPACE) {
textParts.push(WHITESPACE)
charCount++
}
} else if (node.type.name === 'reference') {
const label = node.attrs.label ?? ''
pushText(label.length > 0 ? `@${label}` : '')
}
return true
})
const result = textParts.join('')
return result
}

View File

@ -35,7 +35,8 @@
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"svelte-eslint-parser": "^0.33.1"
"svelte-eslint-parser": "^0.33.1",
"@types/crypto-js": "^4.2.2"
},
"dependencies": {
"@hcengineering/activity": "^0.6.0",
@ -60,6 +61,7 @@
"@hcengineering/view": "^0.6.13",
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/workbench": "^0.6.16",
"svelte": "^4.2.19"
"svelte": "^4.2.19",
"crypto-js": "^4.2.0"
}
}

View File

@ -15,7 +15,9 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { AvatarType, buildGravatarId, checkHasGravatar, type AvatarInfo } from '@hcengineering/contact'
import MD5 from 'crypto-js/md5'
import { AvatarType, checkHasGravatar, type AvatarInfo } from '@hcengineering/contact'
import type { Ref } from '@hcengineering/core'
import { Blob as PlatformBlob } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
@ -38,6 +40,10 @@
import AvatarComponent from './Avatar.svelte'
import EditAvatarPopup from './EditAvatarPopup.svelte'
function buildGravatarId (email: string): string {
return MD5(email.trim().toLowerCase()).toString()
}
export let selectedAvatarType: AvatarType
export let selectedAvatar: AvatarInfo['avatar']
export let selectedAvatarProps: AvatarInfo['avatarProps']

View File

@ -23,7 +23,6 @@
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@types/crypto-js": "^4.1.1",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
@ -42,8 +41,7 @@
"@hcengineering/ui": "^0.6.15",
"@hcengineering/core": "^0.6.32",
"@hcengineering/templates": "^0.6.11",
"@hcengineering/view": "^0.6.13",
"crypto-js": "^4.1.1"
"@hcengineering/view": "^0.6.13"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {

View File

@ -16,7 +16,6 @@
import { AttachedData, Class, Client, Doc, FindResult, Hierarchy, Ref } from '@hcengineering/core'
import { getMetadata } from '@hcengineering/platform'
import { ColorDefinition } from '@hcengineering/ui'
import MD5 from 'crypto-js/md5'
import { AvatarProvider, AvatarType, Channel, Contact, Person, contactPlugin } from '.'
import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
@ -48,13 +47,6 @@ export function getAvatarColorName (color: string): string {
return AVATAR_COLORS.find((col) => col.color === color)?.name ?? AVATAR_COLORS[0].name
}
/**
* @public
*/
export function buildGravatarId (email: string): string {
return MD5(email.trim().toLowerCase()).toString()
}
/**
* @public
*/

View File

@ -7,7 +7,6 @@ import { Analytics } from '@hcengineering/analytics'
import { SplitLogger, configureAnalytics } from '@hcengineering/analytics-service'
import contactPlugin from '@hcengineering/contact'
import { MeasureMetricsContext, newMetrics, setOperationLogProfiling } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { setMetadata } from '@hcengineering/platform'
import { serverConfigFromEnv } from '@hcengineering/server'
import serverAiBot from '@hcengineering/server-ai-bot'
@ -65,10 +64,7 @@ setMetadata(serverCore.metadata.FrontUrl, config.frontUrl)
setMetadata(serverCore.metadata.FilesUrl, config.filesUrl)
setMetadata(serverToken.metadata.Secret, config.serverSecret)
setMetadata(serverNotification.metadata.SesUrl, config.sesUrl ?? '')
setMetadata(notification.metadata.PushPublicKey, config.pushPublicKey)
setMetadata(serverNotification.metadata.PushPrivateKey, config.pushPrivateKey)
setMetadata(serverNotification.metadata.PushSubject, config.pushSubject)
setMetadata(serverCore.metadata.ElasticIndexVersion, 'v1')
setMetadata(serverNotification.metadata.SesAuthToken, config.sesAuthToken)
setMetadata(serverTelegram.metadata.BotUrl, process.env.TELEGRAM_BOT_URL)
setMetadata(serverAiBot.metadata.SupportWorkspaceId, process.env.SUPPORT_WORKSPACE)
setMetadata(serverAiBot.metadata.EndpointURL, process.env.AI_BOT_URL)

View File

@ -475,6 +475,11 @@
"projectFolder": "packages/theme",
"shouldPublish": true
},
{
"packageName": "@hcengineering/text-core",
"projectFolder": "packages/text-core",
"shouldPublish": true
},
{
"packageName": "@hcengineering/text",
"projectFolder": "packages/text",

View File

@ -44,7 +44,7 @@
"@hcengineering/server-activity": "^0.6.0",
"@hcengineering/server-core": "^0.6.1",
"@hcengineering/server-notification-resources": "^0.6.0",
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-core": "^0.6.0",
"@hcengineering/contact": "^0.6.24"
}
}

View File

@ -14,7 +14,7 @@
//
import { Class, Doc, Ref } from '@hcengineering/core'
import { EmptyMarkup, MarkupNodeType, jsonToMarkup } from '@hcengineering/text'
import { EmptyMarkup, MarkupNodeType, jsonToMarkup } from '@hcengineering/text-core'
import { getReferencesData } from '../references'

View File

@ -51,7 +51,7 @@ import {
toReceiverInfo,
type NotificationProviderControl
} from '@hcengineering/server-notification-resources'
import { areEqualJson, extractReferences, jsonToMarkup, markupToJSON } from '@hcengineering/text'
import { areEqualJson, extractReferences, jsonToMarkup, markupToJSON } from '@hcengineering/text-core'
export function isDocMentioned (doc: Ref<Doc>, content: string): boolean {
const references = []

View File

@ -50,6 +50,6 @@
"@hcengineering/server-notification": "^0.6.1",
"@hcengineering/server-notification-resources": "^0.6.0",
"@hcengineering/server": "^0.6.4",
"@hcengineering/text": "^0.6.5"
"@hcengineering/text-core": "^0.6.0"
}
}

View File

@ -45,7 +45,7 @@ import {
getDocCollaborators,
getMixinTx
} from '@hcengineering/server-notification-resources'
import { markupToHTML, markupToText, stripTags } from '@hcengineering/text'
import { markupToHTML, markupToText, stripTags } from '@hcengineering/text-core'
import { workbenchId } from '@hcengineering/workbench'
import { getPersonAccountById, NOTIFICATION_BODY_SIZE } from '@hcengineering/server-notification'

View File

@ -45,7 +45,6 @@
"@hcengineering/server-token": "^0.6.11",
"@hcengineering/chunter": "^0.6.20",
"@hcengineering/server-chunter-resources": "^0.6.0",
"@hcengineering/text": "^0.6.5",
"@hcengineering/request": "^0.6.14",
"@hcengineering/controlled-documents": "^0.1.0",
"@hcengineering/training": "^0.1.0"

View File

@ -45,7 +45,6 @@
"@hcengineering/platform": "^0.6.11",
"@hcengineering/server-core": "^0.6.1",
"@hcengineering/server-notification": "^0.6.1",
"@hcengineering/server-notification-resources": "^0.6.0",
"@hcengineering/text": "^0.6.5"
"@hcengineering/server-notification-resources": "^0.6.0"
}
}

View File

@ -115,10 +115,12 @@ export async function sendEmailNotification (
ctx.error('Please provide email service url to enable email notifications.')
return
}
const sesAuth: string | undefined = getMetadata(serverNotification.metadata.SesAuthToken)
await fetch(concatLink(sesURL, '/send'), {
method: 'post',
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {})
},
body: JSON.stringify({
text,

View File

@ -34,8 +34,7 @@
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"@types/web-push": "~3.6.3"
"@types/jest": "^29.5.5"
},
"dependencies": {
"@hcengineering/activity": "^0.6.0",
@ -48,9 +47,8 @@
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/chunter": "^0.6.20",
"@hcengineering/view": "^0.6.13",
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-core": "^0.6.0",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/server-view": "^0.6.0",
"web-push": "~3.6.7"
"@hcengineering/server-view": "^0.6.0"
}
}

View File

@ -79,10 +79,9 @@ import serverNotification, {
SenderInfo
} from '@hcengineering/server-notification'
import serverView from '@hcengineering/server-view'
import { markupToText, stripTags } from '@hcengineering/text'
import { markupToText, stripTags } from '@hcengineering/text-core'
import { encodeObjectURI } from '@hcengineering/view'
import { workbenchId } from '@hcengineering/workbench'
import webpush, { WebPushError } from 'web-push'
import { Analytics } from '@hcengineering/analytics'
import { Content, ContextsCache, ContextsCacheKey, NotifyParams, NotifyResult } from './types'
@ -574,10 +573,9 @@ export async function createPushNotification (
senderAvatar?: Data<AvatarInfo>,
path?: string[]
): Promise<void> {
const publicKey = getMetadata(notification.metadata.PushPublicKey)
const privateKey = getMetadata(serverNotification.metadata.PushPrivateKey)
const subject = getMetadata(serverNotification.metadata.PushSubject) ?? 'mailto:hey@huly.io'
if (privateKey === undefined || publicKey === undefined) return
const sesURL: string | undefined = getMetadata(serverNotification.metadata.SesUrl)
const sesAuth: string | undefined = getMetadata(serverNotification.metadata.SesAuthToken)
if (sesURL === undefined || sesURL === '') return
const userSubscriptions = subscriptions.filter((it) => it.user === target)
const data: PushData = {
title,
@ -604,34 +602,45 @@ export async function createPushNotification (
}
}
webpush.setVapidDetails(subject, publicKey, privateKey)
const limiter = new RateLimiter(5)
for (const subscription of userSubscriptions) {
await limiter.add(async () => {
await sendPushToSubscription(control, target, subscription, data)
await sendPushToSubscription(sesURL, sesAuth, control, target, subscription, data)
})
}
await limiter.waitProcessing()
}
const errorMessages = ['expired', 'Unregistered', 'No such subscription']
async function sendPushToSubscription (
sesURL: string,
sesAuth: string | undefined,
control: TriggerControl,
targetUser: Ref<Account>,
subscription: PushSubscription,
data: PushData
): Promise<void> {
try {
await webpush.sendNotification(subscription, JSON.stringify(data))
const result: 'ok' | 'clear-push' = (
await (
await fetch(concatLink(sesURL, '/web-push'), {
method: 'post',
headers: {
'Content-Type': 'application/json',
...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {})
},
body: JSON.stringify({
subscription,
data
})
})
).json()
).result
if (result === 'clear-push') {
const tx = control.txFactory.createTxRemoveDoc(subscription._class, subscription.space, subscription._id)
await control.apply(control.ctx, [tx])
}
} catch (err) {
control.ctx.info('Cannot send push notification to', { user: targetUser, err })
if (err instanceof WebPushError) {
if (errorMessages.some((p) => JSON.stringify((err as WebPushError).body).includes(p))) {
const tx = control.txFactory.createTxRemoveDoc(subscription._class, subscription.space, subscription._id)
await control.apply(control.ctx, [tx])
}
}
}
}

View File

@ -151,8 +151,7 @@ export const PUSH_NOTIFICATION_TITLE_SIZE = 80
export default plugin(serverNotificationId, {
metadata: {
SesUrl: '' as Metadata<string>,
PushPrivateKey: '' as Metadata<string>,
PushSubject: '' as Metadata<string>,
SesAuthToken: '' as Metadata<string>,
InboxOnlyNotifications: '' as Metadata<boolean>
},
class: {

View File

@ -50,6 +50,6 @@
"@hcengineering/server-token": "^0.6.11",
"@hcengineering/setting": "^0.6.17",
"@hcengineering/telegram": "^0.6.21",
"@hcengineering/text": "^0.6.5"
"@hcengineering/text-core": "^0.6.0"
}
}

View File

@ -49,7 +49,7 @@ import serverTelegram from '@hcengineering/server-telegram'
import { generateToken } from '@hcengineering/server-token'
import setting, { Integration } from '@hcengineering/setting'
import telegram, { TelegramMessage, TelegramNotificationRequest } from '@hcengineering/telegram'
import { markupToHTML } from '@hcengineering/text'
import { markupToHTML } from '@hcengineering/text-core'
/**
* @public

View File

@ -47,7 +47,7 @@
"@hcengineering/task": "^0.6.20",
"@hcengineering/tracker": "^0.6.24",
"@hcengineering/server-time": "^0.6.0",
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-core": "^0.6.0",
"@hcengineering/time": "^0.6.0"
}
}

View File

@ -43,7 +43,7 @@ import {
} from '@hcengineering/server-notification-resources'
import serverTime, { OnToDo, ToDoFactory } from '@hcengineering/server-time'
import task, { makeRank } from '@hcengineering/task'
import { jsonToMarkup, nodeDoc, nodeParagraph, nodeText } from '@hcengineering/text'
import { jsonToMarkup, nodeDoc, nodeParagraph, nodeText } from '@hcengineering/text-core'
import time, { ProjectToDo, ToDo, ToDoPriority, TodoAutomationHelper, WorkSlot } from '@hcengineering/time'
import tracker, { Issue, IssueStatus, Project, TimeSpendReport } from '@hcengineering/tracker'

View File

@ -47,7 +47,7 @@
"@hcengineering/server-notification": "^0.6.1",
"@hcengineering/server-task-resources": "^0.6.0",
"@hcengineering/task": "^0.6.20",
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-core": "^0.6.0",
"@hcengineering/tracker": "^0.6.24",
"@hcengineering/view": "^0.6.13",
"@hcengineering/workbench": "^0.6.16"

View File

@ -35,7 +35,7 @@ import { NotificationContent } from '@hcengineering/notification'
import { getMetadata, IntlString } from '@hcengineering/platform'
import serverCore, { TriggerControl } from '@hcengineering/server-core'
import { NOTIFICATION_BODY_SIZE } from '@hcengineering/server-notification'
import { stripTags } from '@hcengineering/text'
import { stripTags } from '@hcengineering/text-core'
import tracker, { Component, Issue, IssueParentInfo, TimeSpendReport, trackerId } from '@hcengineering/tracker'
import { workbenchId } from '@hcengineering/workbench'

View File

@ -35,7 +35,8 @@
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"@types/otp-generator": "^4.0.2"
"@types/otp-generator": "^4.0.2",
"@types/crypto-js": "^4.2.2"
},
"dependencies": {
"@hcengineering/mongo": "^0.6.1",
@ -55,6 +56,7 @@
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/server-storage": "^0.6.0",
"@hcengineering/server-core": "^0.6.1",
"@hcengineering/server-pipeline": "^0.6.0"
"@hcengineering/server-pipeline": "^0.6.0",
"crypto-js": "^4.2.0"
}
}

View File

@ -16,7 +16,6 @@
import { Analytics } from '@hcengineering/analytics'
import contact, {
AvatarType,
buildGravatarId,
checkHasGravatar,
combineName,
Employee,
@ -95,6 +94,10 @@ import {
} from './utils'
import { getWorkspaceDestroyAdapter } from '@hcengineering/server-pipeline'
import MD5 from 'crypto-js/md5'
function buildGravatarId (email: string): string {
return MD5(email.trim().toLowerCase()).toString()
}
/**
* @public
*/

View File

@ -6,12 +6,10 @@ export interface ServerEnv {
frontUrl: string
filesUrl: string | undefined
sesUrl: string | undefined
sesAuthToken: string | undefined
accountsUrl: string
serverPort: number
enableCompression: boolean
pushPublicKey: string | undefined
pushPrivateKey: string | undefined
pushSubject: string | undefined
brandingPath: string | undefined
}
@ -47,6 +45,7 @@ export function serverConfigFromEnv (): ServerEnv {
const filesUrl = process.env.FILES_URL
const sesUrl = process.env.SES_URL
const sesAuthToken = process.env.SES_AUTH_TOKEN
const accountsUrl = process.env.ACCOUNTS_URL
if (accountsUrl === undefined) {
@ -54,9 +53,6 @@ export function serverConfigFromEnv (): ServerEnv {
process.exit(1)
}
const pushPublicKey = process.env.PUSH_PUBLIC_KEY
const pushPrivateKey = process.env.PUSH_PRIVATE_KEY
const pushSubject = process.env.PUSH_SUBJECT
const brandingPath = process.env.BRANDING_PATH
return {
@ -67,12 +63,10 @@ export function serverConfigFromEnv (): ServerEnv {
frontUrl,
filesUrl,
sesUrl,
sesAuthToken,
accountsUrl,
serverPort,
enableCompression,
pushPublicKey,
pushPrivateKey,
pushSubject,
brandingPath
}
}

View File

@ -50,7 +50,8 @@
"@tsconfig/node16": "^1.0.4",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"eslint-plugin-node": "^11.1.0"
"eslint-plugin-node": "^11.1.0",
"@types/web-push": "^3.6.4"
},
"dependencies": {
"@hcengineering/client": "^0.6.18",
@ -62,6 +63,7 @@
"aws-sdk": "^2.1423.0",
"cors": "^2.8.5",
"dotenv": "~16.0.0",
"express": "^4.21.2"
"express": "^4.21.2",
"web-push": "^3.6.7"
}
}

View File

@ -19,14 +19,22 @@ interface Config {
AccessKey: string
SecretKey: string
Region: string
AuthToken?: string
PushSubject?: string
PushPublicKey?: string
PushPrivateKey?: string
}
const envMap: { [key in keyof Config]: string } = {
const envMap: { [key in keyof Required<Config>]: string } = {
Port: 'PORT',
Source: 'SOURCE',
AccessKey: 'ACCESS_KEY',
SecretKey: 'SECRET_KEY',
Region: 'REGION'
Region: 'REGION',
AuthToken: 'AUTH_TOKEN',
PushPublicKey: 'PUSH_PUBLIC_KEY',
PushPrivateKey: 'PUSH_PRIVATE_KEY',
PushSubject: 'PUSH_SUBJECT'
}
const parseNumber = (str: string | undefined): number | undefined => (str !== undefined ? Number(str) : undefined)
@ -37,7 +45,11 @@ const config: Config = (() => {
Source: process.env[envMap.Source],
AccessKey: process.env[envMap.AccessKey],
SecretKey: process.env[envMap.SecretKey],
Region: process.env[envMap.Region] ?? 'us-east-1'
Region: process.env[envMap.Region] ?? 'us-east-1',
AuthToken: process.env[envMap.AuthToken],
PushPublicKey: process.env[envMap.PushPublicKey],
PushPrivateKey: process.env[envMap.PushPrivateKey],
PushSubject: process.env[envMap.PushSubject]
}
const required: Array<keyof Config> = ['Port', 'Source', 'AccessKey', 'SecretKey', 'Region']

View File

@ -13,20 +13,57 @@
// limitations under the License.
//
import { PushSubscription, type PushData } from '@hcengineering/notification'
import type { Request, Response } from 'express'
import webpush, { WebPushError } from 'web-push'
import config from './config'
import { createServer, listen } from './server'
import { SES } from './ses'
import { Endpoint } from './types'
const errorMessages = ['expired', 'Unregistered', 'No such subscription']
async function sendPushToSubscription (subscription: PushSubscription, data: PushData): Promise<'ok' | 'clear-push'> {
try {
await webpush.sendNotification(subscription, JSON.stringify(data))
} catch (err: any) {
if (err instanceof WebPushError) {
if (errorMessages.some((p) => JSON.stringify((err as WebPushError).body).includes(p))) {
return 'clear-push'
}
}
}
return 'ok'
}
export const main = async (): Promise<void> => {
const ses = new SES()
console.log('SES service has been started')
if (config.PushPublicKey !== undefined && config.PushPrivateKey !== undefined) {
webpush.setVapidDetails(config.PushSubject ?? 'mailto:hey@huly.io', config.PushPublicKey, config.PushPublicKey)
}
const checkAuth = (req: Request<any>, res: Response<any>): boolean => {
if (config.AuthToken !== undefined) {
// We need to verify authorization
const authorization = req.headers.authorization ?? ''
const token = authorization.replace('Bearer ', '')
if (token !== config.AuthToken) {
res.status(401).send({ err: 'Invalid auth token' })
return false
}
}
return true
}
const endpoints: Endpoint[] = [
{
endpoint: '/send',
type: 'post',
handler: async (req, res) => {
if (!checkAuth(req, res)) {
return
}
const text = req.body?.text
if (text === undefined) {
res.status(400).send({ err: "'text' is missing" })
@ -54,6 +91,28 @@ export const main = async (): Promise<void> => {
res.send()
}
},
{
endpoint: '/web-push',
type: 'post',
handler: async (req, res) => {
if (!checkAuth(req, res)) {
return
}
const data: PushData | undefined = req.body?.data
if (data === undefined) {
res.status(400).send({ err: "'data' is missing" })
return
}
const subscription: PushSubscription | undefined = req.body?.subscription
if (subscription === undefined) {
res.status(400).send({ err: "'subscription' is missing" })
return
}
const result = await sendPushToSubscription(subscription, data)
res.json({ result })
}
}
]

View File

@ -17,5 +17,7 @@ done <<< "$files"
# Sort the array by size (numerically, in descending order) and print
printf '%s\n' "${file_info[@]}" | sort -t: -k1,1nr | while IFS=: read -r size path; do
echo "Size: $(($size/1024)) KB - $path"
if [ $(($size/1024)) -ne 0 ]; then
echo "Size: $(($size/1024)) KB - $path"
fi
done

View File

@ -54,7 +54,12 @@
"@hcengineering/server-client": "^0.6.0",
"@hcengineering/server-pipeline": "^0.6.0",
"@hcengineering/server-token": "^0.6.11",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/storage": "^0.6.0",
"@hcengineering/server-notification": "^0.6.1",
"@hcengineering/notification": "^0.6.23",
"@hcengineering/server-ai-bot": "^0.6.0",
"@hcengineering/server-telegram": "^0.6.0",
"itty-router": "^5.0.18",
"snappyjs": "^0.7.0"
}

View File

@ -16,7 +16,7 @@ import { setMetadata } from '@hcengineering/platform'
import { RPCHandler } from '@hcengineering/rpc'
import { ClientSession, createSessionManager, doSessionOp, type WebsocketData } from '@hcengineering/server'
import serverClient from '@hcengineering/server-client'
import {
import serverCore, {
createDummyStorageAdapter,
initStatisticsContext,
loadBrandingMap,
@ -52,6 +52,12 @@ import {
} from '@hcengineering/server-pipeline'
import { CloudFlareLogger } from './logger'
import model from './model.json'
// import { configureAnalytics } from '@hcengineering/analytics-service'
// import { Analytics } from '@hcengineering/analytics'
import serverAiBot from '@hcengineering/server-ai-bot'
import serverNotification from '@hcengineering/server-notification'
import serverTelegram from '@hcengineering/server-telegram'
import contactPlugin from '@hcengineering/contact'
export const PREFERRED_SAVE_SIZE = 500
export const PREFERRED_SAVE_INTERVAL = 30 * 1000
@ -82,6 +88,20 @@ export class Transactor extends DurableObject<Env> {
setExtraOptions({
useCF: true
})
// configureAnalytics(env.SENTRY_DSN, {})
// Analytics.setTag('application', 'transactor')
const lastNameFirst = process.env.LAST_NAME_FIRST === 'true'
setMetadata(contactPlugin.metadata.LastNameFirst, lastNameFirst)
setMetadata(serverCore.metadata.FrontUrl, env.FRONT_URL)
setMetadata(serverCore.metadata.FilesUrl, env.FILES_URL)
setMetadata(serverNotification.metadata.SesUrl, env.SES_URL ?? '')
setMetadata(serverNotification.metadata.SesAuthToken, env.SES_AUTH_TOKEN)
setMetadata(serverTelegram.metadata.BotUrl, process.env.TELEGRAM_BOT_URL)
setMetadata(serverAiBot.metadata.SupportWorkspaceId, process.env.SUPPORT_WORKSPACE)
setMetadata(serverAiBot.metadata.EndpointURL, process.env.AI_BOT_URL)
registerTxAdapterFactory('postgresql', createPostgresTxAdapter, true)
registerAdapterFactory('postgresql', createPostgresAdapter, true)
registerDestroyFactory('postgresql', createPostgreeDestroyAdapter, true)

View File

@ -18,4 +18,16 @@ interface Env {
FULLTEXT_URL: string | undefined
DB_MODE: 'hyperdrive' | 'direct' | undefined
FRONT_URL: string
FILES_URL?: string
SES_URL?: string
SES_AUTH_TOKEN?: string
SUPPORT_WORKSPACE?: string
TELEGRAM_BOT_URL: string
AI_BOT_URL?: string
LAST_NAME_FIRST?: string
}

View File

@ -25,6 +25,18 @@ mode = "smart"
# ACCOUNTS_URL = "http://127.0.0.1:3000"
# SERVER_SECRET = "secret"
DB_MODE='hyperdrive'
# FRONT_URL
ENABLE_COMPRESSION=true
# ACCOUNTS_URL
# FULLTEXT_URL
# STATS_URL
# PUSH_PUBLIC_KEY
# PUSH_PRIVATE_KEY
# SENTRY_DSN
# SUPPORT_WORKSPACE
# TELEGRAM_BOT_URL
# AI_BOT_URL
# LAST_NAME_FIRST
# Bind the Workers AI model catalog. Run machine learning models, powered by serverless GPUs, on Cloudflares global network
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#workers-ai