@tequila99/githuman 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +75 -0
  2. package/dist/cli/config.js +13 -1
  3. package/dist/cli/server-runtime.js +3 -0
  4. package/dist/server/app.js +37 -4
  5. package/dist/server/config/agents.js +2 -1
  6. package/dist/server/config/openapi.js +4 -0
  7. package/dist/server/db/migrations.js +61 -0
  8. package/dist/server/errors/note-assets.js +3 -0
  9. package/dist/server/errors/notes.js +3 -0
  10. package/dist/server/hooks/notes/same-origin.js +37 -0
  11. package/dist/server/hooks/terminal/local-origin.js +2 -1
  12. package/dist/server/repositories/notes-catalog.repo.js +129 -0
  13. package/dist/server/repositories/notes.repo.js +444 -0
  14. package/dist/server/routes/agent.js +1 -0
  15. package/dist/server/routes/notes.js +213 -0
  16. package/dist/server/routes/terminal.js +2 -1
  17. package/dist/server/services/agent/context-builder.js +13 -0
  18. package/dist/server/services/git.service.js +15 -11
  19. package/dist/server/services/notes-external-assets.service.js +177 -0
  20. package/dist/server/services/notes-identity.service.js +811 -0
  21. package/dist/server/services/notes-operations.service.js +296 -0
  22. package/dist/server/services/notes-watcher.service.js +44 -0
  23. package/dist/server/services/notes-writer.service.js +116 -0
  24. package/dist/server/services/notes.service.js +150 -0
  25. package/dist/server/services/terminal/backend.js +6 -3
  26. package/dist/server/services/terminal/channel.js +6 -3
  27. package/dist/server/services/terminal/registry.js +10 -5
  28. package/dist/server/services/terminal/session.js +10 -5
  29. package/dist/server/services/terminal/tokens.js +4 -2
  30. package/dist/server/utils/hash.js +4 -0
  31. package/dist/server/utils/note-asset-format.js +19 -0
  32. package/dist/server/utils/remote-asset-request.js +193 -0
  33. package/dist/server/utils/request-abort.js +20 -0
  34. package/dist/server/utils/system-error.js +14 -0
  35. package/dist/shared/agents/schemas.js +14 -0
  36. package/dist/shared/events/types.js +1 -0
  37. package/dist/shared/notes/asset-formats.js +57 -0
  38. package/dist/shared/notes/constants.js +7 -0
  39. package/dist/shared/notes/document.js +118 -0
  40. package/dist/shared/notes/external-assets.js +153 -0
  41. package/dist/shared/notes/identity.js +24 -0
  42. package/dist/shared/notes/links.js +44 -0
  43. package/dist/shared/notes/note-path.js +17 -0
  44. package/dist/shared/notes/reference-path.js +55 -0
  45. package/dist/shared/notes/references.js +190 -0
  46. package/dist/shared/notes/schemas.js +222 -0
  47. package/dist/shared/notes/types.js +1 -0
  48. package/dist/shared/utils/heading-slug.js +7 -0
  49. package/dist/shared/utils/is-uuid.js +5 -0
  50. package/dist/shared/utils/safe-decode.js +9 -0
  51. package/dist/shared/utils/web-url.js +15 -0
  52. package/dist/web/assets/AppPage-DdmqiV2R.js +1 -0
  53. package/dist/web/assets/ClosePopup-lpH_1Ju2.js +1 -0
  54. package/dist/web/assets/CreateButton-Dnpwobd9.js +93 -0
  55. package/dist/web/assets/CreateReviewFabButton-KvKPEJzI.js +1 -0
  56. package/dist/web/assets/{ErrorNotFound-CYOZg8yN.js → ErrorNotFound-Cq0wNQMu.js} +1 -1
  57. package/dist/web/assets/IndexPage-DE-IkAhK.js +1 -0
  58. package/dist/web/assets/IndexPage-F8MMAAkn.css +1 -0
  59. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  60. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  61. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  62. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  63. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  64. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  65. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  66. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  67. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  68. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  69. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  70. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  71. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  72. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  73. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  74. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  75. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  76. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  77. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  78. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  79. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  80. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  81. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  82. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  83. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  84. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  85. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  86. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  87. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  88. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  89. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  90. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  91. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  92. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  93. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  94. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  95. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  96. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  97. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  98. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  99. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  100. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  101. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  102. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  103. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  104. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  105. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  106. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  107. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  108. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  109. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  110. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  111. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  112. package/dist/web/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
  113. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  114. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  115. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  116. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  117. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  118. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  119. package/dist/web/assets/MainLayout-DM30ynlR.css +1 -0
  120. package/dist/web/assets/MainLayout-wWA8ckBg.js +6 -0
  121. package/dist/web/assets/NoteVisualEditor-Bj13_oEl.js +1054 -0
  122. package/dist/web/assets/NoteVisualEditor-IH3s3d3W.css +1 -0
  123. package/dist/web/assets/NotesPage-FidBg83M.js +4 -0
  124. package/dist/web/assets/NotesPage-KaCEnfdz.css +1 -0
  125. package/dist/web/assets/PanelResize-C51WwwLD.css +1 -0
  126. package/dist/web/assets/PanelResize-DY0Nzmql.js +1 -0
  127. package/dist/web/assets/{PdfPreview-CIiKnzQN.js → PdfPreview-C7HomxEg.js} +1 -1
  128. package/dist/web/assets/QBtn-DoggJe1Q.js +1 -0
  129. package/dist/web/assets/QFab-C-YZWgmW.js +1 -0
  130. package/dist/web/assets/QForm-DpEhYVCa.js +4 -0
  131. package/dist/web/assets/QInput-DJgTh2Qy.js +1 -0
  132. package/dist/web/assets/QResizeObserver-DeRFR_WF.js +1 -0
  133. package/dist/web/assets/QScrollArea-BSSZoaV5.js +1 -0
  134. package/dist/web/assets/QSelect-2iOBkp1K.js +1 -0
  135. package/dist/web/assets/QSpace-B5NneHAd.js +1 -0
  136. package/dist/web/assets/QVirtualScroll-CZJMBpmR.js +1 -0
  137. package/dist/web/assets/ReviewDetailPage-BI1iAhSf.js +1 -0
  138. package/dist/web/assets/ReviewsPage-RyST0FlH.js +1 -0
  139. package/dist/web/assets/{StatusSwitcher-DTD5inU1.css → StatusSwitcher-BCv_9MtI.css} +1 -1
  140. package/dist/web/assets/StatusSwitcher-DAeNzNP7.js +1 -0
  141. package/dist/web/assets/{TerminalView-Ch-ndYr6.js → TerminalView-BAzJgUTq.js} +2 -2
  142. package/dist/web/assets/{TerminalView-BurzSD2L.css → TerminalView-C1jFlfLH.css} +1 -1
  143. package/dist/web/assets/_baseMerge-BzZFQ6mI.js +1 -0
  144. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cab71FlB.js → abnfDiagram-O67JEVCF-BNZQ7duT.js} +1 -1
  145. package/dist/web/assets/agent-store-DP6Vrw6L.js +113 -0
  146. package/dist/web/assets/apl-gfMMiTNf.js +1 -0
  147. package/dist/web/assets/app-info-store-u6ZVFjv5.js +1 -0
  148. package/dist/web/assets/architecture-7GRP2DOG-BSdnjp9Z.js +1 -0
  149. package/dist/web/assets/{architectureDiagram-NJMV4G6O-Dl1WKOEi.js → architectureDiagram-NJMV4G6O-Bkj0kfFn.js} +1 -1
  150. package/dist/web/assets/asciiarmor-C_eisQCp.js +1 -0
  151. package/dist/web/assets/asn1-DCl_8MCW.js +1 -0
  152. package/dist/web/assets/asterisk-BEn0Cwpx.js +1 -0
  153. package/dist/web/assets/{blockDiagram-BEXU5L5S-CZ3Lxybj.js → blockDiagram-BEXU5L5S-CXO3zN2c.js} +1 -1
  154. package/dist/web/assets/brainfuck-D5EjA2JK.js +1 -0
  155. package/dist/web/assets/{c4Diagram-YGBWAQC7-ULi06PEd.js → c4Diagram-YGBWAQC7-BHfMeZNf.js} +1 -1
  156. package/dist/web/assets/ccount-CcG1m-Yh.js +1 -0
  157. package/dist/web/assets/channel-DmoOM6AB.js +1 -0
  158. package/dist/web/assets/{chunk-3FUC2YCW-B1f60wx3.js → chunk-3FUC2YCW-z1WbH7Gi.js} +1 -1
  159. package/dist/web/assets/{chunk-5DYCD2WN-f6Pcl3dF.js → chunk-5DYCD2WN-K9HPch97.js} +1 -1
  160. package/dist/web/assets/{chunk-742MDFTN-szX1QirW.js → chunk-742MDFTN-Bhjp9fC-.js} +1 -1
  161. package/dist/web/assets/{chunk-7INBJB4K-CP_ynmAQ.js → chunk-7INBJB4K-Do0EmU14.js} +1 -1
  162. package/dist/web/assets/{chunk-7M6MHVWA-DPL74uZE.js → chunk-7M6MHVWA-veeEjsOh.js} +5 -5
  163. package/dist/web/assets/{chunk-7PRAP22T-DH9TawZY.js → chunk-7PRAP22T-CPqB5tCK.js} +1 -1
  164. package/dist/web/assets/{chunk-GTNCS2PH-CKAuGvnf.js → chunk-GTNCS2PH-Civ1V52P.js} +1 -1
  165. package/dist/web/assets/{chunk-GWA4HPMP-DTbHCHSN.js → chunk-GWA4HPMP-C7LzAlyZ.js} +1 -1
  166. package/dist/web/assets/{chunk-MBY4JIJT-ClgvJMtC.js → chunk-MBY4JIJT-DfmJy0o_.js} +1 -1
  167. package/dist/web/assets/{chunk-NETBCI7D-Dq5Z7z8S.js → chunk-NETBCI7D-BZIKmMvL.js} +1 -1
  168. package/dist/web/assets/{chunk-O7XYJQB3-C0By5mcm.js → chunk-O7XYJQB3-DcHeu6b7.js} +1 -1
  169. package/dist/web/assets/{chunk-UA2S7LBM-BtquYpxe.js → chunk-UA2S7LBM-0SR2og6x.js} +1 -1
  170. package/dist/web/assets/{chunk-WEXAMYUT-BlZL42XJ.js → chunk-WEXAMYUT-CJMSoM-Z.js} +1 -1
  171. package/dist/web/assets/{chunk-Z7XXMR3K-BbtQbAvv.js → chunk-Z7XXMR3K-q6fce66x.js} +1 -1
  172. package/dist/web/assets/{chunk-ZIGJFQKS-tC_YdRrm.js → chunk-ZIGJFQKS-DsPzen4Y.js} +1 -1
  173. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-CvlA_A4N.js → classDiagram-v2-NBCMYWYE-Cpm_bvzb.js} +4 -4
  174. package/dist/web/assets/clike-D_6L3tnO.js +1 -0
  175. package/dist/web/assets/clojure-BflJGmDX.js +1 -0
  176. package/dist/web/assets/cmake-84nbDH0I.js +1 -0
  177. package/dist/web/assets/cobol-DHLQUbN7.js +1 -0
  178. package/dist/web/assets/coffeescript-De_zI4J0.js +1 -0
  179. package/dist/web/assets/commonlisp-DgAKBe0r.js +1 -0
  180. package/dist/web/assets/core-Bumj8M77.js +12 -0
  181. package/dist/web/assets/crystal-Bsdphhtm.js +1 -0
  182. package/dist/web/assets/css-CnaYoQib.js +1 -0
  183. package/dist/web/assets/{cynefin-OW5HDTMX-DFbhKkIi.js → cynefin-OW5HDTMX-ZTr5XZMW.js} +1 -1
  184. package/dist/web/assets/{cynefinDiagram-VND7K2PF-DHGaLNQN.js → cynefinDiagram-VND7K2PF-ByUmUx58.js} +1 -1
  185. package/dist/web/assets/cypher-p9eYesGn.js +1 -0
  186. package/dist/web/assets/d-DWkQyNnU.js +1 -0
  187. package/dist/web/assets/dagre-6A5THRUB-mQOy0E3r.js +4 -0
  188. package/dist/web/assets/{diagram-22UHCM2B-Djjo5ftL.js → diagram-22UHCM2B-BW0LPtyk.js} +8 -8
  189. package/dist/web/assets/{diagram-3UASUU5V-BcP7pJru.js → diagram-3UASUU5V-jo148ec5.js} +1 -1
  190. package/dist/web/assets/{diagram-ATOU4E4O-o_vsc8em.js → diagram-ATOU4E4O-CJt4F0i4.js} +1 -1
  191. package/dist/web/assets/{diagram-CDSNMT55-eyN9Aru8.js → diagram-CDSNMT55-DEShJv56.js} +1 -1
  192. package/dist/web/assets/{diagram-MLGK6HIB-Dfjipnvb.js → diagram-MLGK6HIB-DOFOADfs.js} +1 -1
  193. package/dist/web/assets/{diagram-MPIPVDR6-C2chocRe.js → diagram-MPIPVDR6-ux537BMl.js} +1 -1
  194. package/dist/web/assets/diff-ChtP43wD.js +1 -0
  195. package/dist/web/assets/dist-3B0OaUZ-.js +1 -0
  196. package/dist/web/assets/dist-7xWvOKEv.js +1 -0
  197. package/dist/web/assets/dist-BThMRW5U.js +23 -0
  198. package/dist/web/assets/dist-BWa3ecCV.js +2 -0
  199. package/dist/web/assets/dist-BY24_62d.js +1 -0
  200. package/dist/web/assets/dist-BbdrgL5V.js +11 -0
  201. package/dist/web/assets/dist-Bo7anpPj.js +9 -0
  202. package/dist/web/assets/dist-Bu9yRqTU.js +1 -0
  203. package/dist/web/assets/dist-Bvnostf5.js +1 -0
  204. package/dist/web/assets/dist-C3sBgvRy.js +1 -0
  205. package/dist/web/assets/dist-CK1va5C_.js +1 -0
  206. package/dist/web/assets/dist-CLv-grWO.js +1 -0
  207. package/dist/web/assets/dist-CqGqStPp.js +1 -0
  208. package/dist/web/assets/dist-D1EUrS6y.js +6 -0
  209. package/dist/web/assets/dist-D2Ce45aH.js +13 -0
  210. package/dist/web/assets/dist-D7Llw4iH.js +1 -0
  211. package/dist/web/assets/dist-DL0dBIkt.js +1 -0
  212. package/dist/web/assets/dist-DUt0feNB.js +1 -0
  213. package/dist/web/assets/dist-DZQ2IsS9.js +1 -0
  214. package/dist/web/assets/dist-DpKJcdvz.js +1 -0
  215. package/dist/web/assets/dist-DuZv0LJj.js +1 -0
  216. package/dist/web/assets/dist-LFPPEX39.js +1 -0
  217. package/dist/web/assets/dist-lC0EKTFH.js +1 -0
  218. package/dist/web/assets/dockerfile-DJkHQgkl.js +1 -0
  219. package/dist/web/assets/dtd-DTF72YNO.js +1 -0
  220. package/dist/web/assets/dylan-BCDoL_-p.js +1 -0
  221. package/dist/web/assets/ebnf-D4c0_ac3.js +1 -0
  222. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CE58dwC-.js → ebnfDiagram-ZINNZB2B-DEybuZle.js} +1 -1
  223. package/dist/web/assets/ecl-BVIeWmwY.js +1 -0
  224. package/dist/web/assets/eiffel-BEjRio4Q.js +1 -0
  225. package/dist/web/assets/{elk-276RUBZZ-KyTn9aJi.js → elk-276RUBZZ-CZXZRpGb.js} +1 -1
  226. package/dist/web/assets/elm-Cshzl8qu.js +1 -0
  227. package/dist/web/assets/{erDiagram-OPXOYQCR-D9aOi7ZD.js → erDiagram-OPXOYQCR-BgRqGAt5.js} +1 -1
  228. package/dist/web/assets/erlang-BV0c4Hdn.js +1 -0
  229. package/dist/web/assets/eventmodeling-NTZA5JFV-ZuRq6Sae.js +1 -0
  230. package/dist/web/assets/factor-LKH5gN8L.js +1 -0
  231. package/dist/web/assets/fcl-ClOhbFR7.js +1 -0
  232. package/dist/web/assets/flowDiagram-KWPJA3E3-DRyHAdLb.js +1 -0
  233. package/dist/web/assets/forth-D6RzAl64.js +1 -0
  234. package/dist/web/assets/fortran-Bt6PBEDR.js +1 -0
  235. package/dist/web/assets/{ganttDiagram-FUAMR5RP-DRAI6xWM.js → ganttDiagram-FUAMR5RP-CBJwX1gk.js} +1 -1
  236. package/dist/web/assets/gas-BHEdbvp9.js +1 -0
  237. package/dist/web/assets/gherkin-oBAE_ms0.js +1 -0
  238. package/dist/web/assets/{gitGraph-4MIJSDKK-D1lAB6tr.js → gitGraph-4MIJSDKK-Dw-XA3G1.js} +1 -1
  239. package/dist/web/assets/{gitGraphDiagram-X574FWY7-77GVnwya.js → gitGraphDiagram-X574FWY7-BpGccoWp.js} +1 -1
  240. package/dist/web/assets/graphlib-C64HifUL.js +1 -0
  241. package/dist/web/assets/groovy-CUpZ7dEl.js +1 -0
  242. package/dist/web/assets/haskell-BRsoo5mP.js +1 -0
  243. package/dist/web/assets/haxe-DjVOVvNP.js +1 -0
  244. package/dist/web/assets/http-CLVgA2GD.js +1 -0
  245. package/dist/web/assets/i18n-sfJvmpVb.js +1 -0
  246. package/dist/web/assets/idl-BUZw3wgd.js +1 -0
  247. package/dist/web/assets/index-B2CzafCr.js +2 -0
  248. package/dist/web/assets/{info-A6RAGUB7-Di1qRpwO.js → info-A6RAGUB7-Dj5RfJsg.js} +1 -1
  249. package/dist/web/assets/{infoDiagram-VRGFBTTK-gceHneJ0.js → infoDiagram-VRGFBTTK-DyVSH991.js} +1 -1
  250. package/dist/web/assets/{init-app-IHgDyoRH.js → init-app-DSS44aRI.js} +1 -1
  251. package/dist/web/assets/{is-DssfAlMe.js → is-CE4jkrGQ.js} +1 -1
  252. package/dist/web/assets/isArrayLikeObject-BbaDyxId.js +1 -0
  253. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-B9QDQoSG.js → ishikawaDiagram-OU5B5YK6-Cu0n2oqT.js} +1 -1
  254. package/dist/web/assets/javascript-Bu8mWwtz.js +1 -0
  255. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-CVTMW0pg.js → journeyDiagram-ZHPQQLJL-Bz1Ot6Zg.js} +1 -1
  256. package/dist/web/assets/julia-CQfgDRfP.js +1 -0
  257. package/dist/web/assets/{kanban-definition-PNTS6WVX-TNoWYrUr.js → kanban-definition-PNTS6WVX-C42vuY7H.js} +1 -1
  258. package/dist/web/assets/{line-B_1CHIjZ.js → line-C5WKurbS.js} +1 -1
  259. package/dist/web/assets/livescript-BXxG0Yva.js +1 -0
  260. package/dist/web/assets/lua-DPCEkfD_.js +1 -0
  261. package/dist/web/assets/markdown-DBTEhbhQ.js +15 -0
  262. package/dist/web/assets/mathematica-u8YMmbU0.js +1 -0
  263. package/dist/web/assets/mbox-BEMVcqjs.js +1 -0
  264. package/dist/web/assets/{mermaid-parser.core-LAeoGxkL.js → mermaid-parser.core-B7l8qh8B.js} +3 -3
  265. package/dist/web/assets/{mermaid.core-DMppSzfM.js → mermaid.core-Drs2mjGg.js} +7 -7
  266. package/dist/web/assets/{mindmap-definition-NLK3R4M7-DJ-6nTWg.js → mindmap-definition-NLK3R4M7-BV1FNwCn.js} +1 -1
  267. package/dist/web/assets/mirc-C9zpzAZU.js +1 -0
  268. package/dist/web/assets/mllike-B45xgp2S.js +1 -0
  269. package/dist/web/assets/modelica-CqHI_q-D.js +1 -0
  270. package/dist/web/assets/mscgen-qgSLujhx.js +1 -0
  271. package/dist/web/assets/mumps-BSzj6Xyz.js +1 -0
  272. package/dist/web/assets/nginx-8H7HVH4w.js +1 -0
  273. package/dist/web/assets/nsis-viPvXFiq.js +1 -0
  274. package/dist/web/assets/ntriples-B9h1VB_g.js +1 -0
  275. package/dist/web/assets/octave-D2Q8cUp1.js +1 -0
  276. package/dist/web/assets/oz-BwTct_5T.js +1 -0
  277. package/dist/web/assets/{packet-AYTQ26CC-B4tvN4nf.js → packet-AYTQ26CC-BPmUY1Ig.js} +1 -1
  278. package/dist/web/assets/pascal-Dp_KdFgX.js +1 -0
  279. package/dist/web/assets/{pegDiagram-GJSIUBJH-BwQ6jaVw.js → pegDiagram-GJSIUBJH-D6NPxPdr.js} +1 -1
  280. package/dist/web/assets/perl-BkSkiI9m.js +1 -0
  281. package/dist/web/assets/{pie-WAS4IAKB-ZwlyCxls.js → pie-WAS4IAKB-CTp2jNAc.js} +1 -1
  282. package/dist/web/assets/{pieDiagram-5QR66LMP-BDYLBnB1.js → pieDiagram-5QR66LMP-BVkABrg7.js} +1 -1
  283. package/dist/web/assets/pig-AF4Hwhju.js +1 -0
  284. package/dist/web/assets/pinia-BVWaJ8NX.js +1 -0
  285. package/dist/web/assets/powershell-BcIto6Sf.js +1 -0
  286. package/dist/web/assets/properties-C357ku5U.js +1 -0
  287. package/dist/web/assets/protobuf-D1ij_kNL.js +1 -0
  288. package/dist/web/assets/pug-3K-JMW4g.js +1 -0
  289. package/dist/web/assets/puppet-BZyAS7I4.js +1 -0
  290. package/dist/web/assets/purify.es--06brOip.js +3 -0
  291. package/dist/web/assets/python-B_1Jd8HM.js +1 -0
  292. package/dist/web/assets/q-B9NhS7L_.js +1 -0
  293. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CkmLCxOG.js → quadrantDiagram-O4NWA36T-LZCjVd08.js} +1 -1
  294. package/dist/web/assets/r-CxDmU1I4.js +1 -0
  295. package/dist/web/assets/{radar-RG4KPBEZ-Ca7H5QOY.js → radar-RG4KPBEZ-CCiUrmsg.js} +1 -1
  296. package/dist/web/assets/{railroad-74A4TZTK-CX3Z2QYJ.js → railroad-74A4TZTK-CZn8VJbK.js} +1 -1
  297. package/dist/web/assets/railroad-abnf-HS5TGJTU-B-9tG9pW.js +1 -0
  298. package/dist/web/assets/railroad-ebnf-LZEXJU2U-D7lUyGOq.js +1 -0
  299. package/dist/web/assets/railroad-peg-WCYAUIDC-ja94t32K.js +1 -0
  300. package/dist/web/assets/{railroadDiagram-XR7U4H2S-D8ii6sM4.js → railroadDiagram-XR7U4H2S-nopy-mdw.js} +1 -1
  301. package/dist/web/assets/{requirementDiagram-PLB6GJNP-iHY-tE7c.js → requirementDiagram-PLB6GJNP-BOeMzf-x.js} +1 -1
  302. package/dist/web/assets/rpm-gpDK5sbt.js +1 -0
  303. package/dist/web/assets/ruby-ChfNwlCY.js +1 -0
  304. package/dist/web/assets/{safe-storage-BCP757jk.js → safe-storage-aRoyeFxv.js} +1 -1
  305. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CBprSUZk.js → sankeyDiagram-IPEJSGJF-Bbk0AQUe.js} +1 -1
  306. package/dist/web/assets/sas-C2OyD2Y6.js +1 -0
  307. package/dist/web/assets/scheme-D9qYN0V5.js +1 -0
  308. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-BkphpOQ_.js → sequenceDiagram-PO4LG4MO-ZpubjKFc.js} +1 -1
  309. package/dist/web/assets/shell-DwuoZtxw.js +1 -0
  310. package/dist/web/assets/sieve-tLWDfEkZ.js +1 -0
  311. package/dist/web/assets/simple-mode-DXiLM1-G.js +1 -0
  312. package/dist/web/assets/smalltalk-BLp5-jwC.js +1 -0
  313. package/dist/web/assets/solr-B78wSQJQ.js +1 -0
  314. package/dist/web/assets/sparql-Dz-mlCAC.js +1 -0
  315. package/dist/web/assets/spreadsheet-CTIncYxj.js +1 -0
  316. package/dist/web/assets/sql-DuQMFDvy.js +1 -0
  317. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-HN1RwAPZ.js → stateDiagram-v2-GCMORJYK-DNjDh9yZ.js} +2 -2
  318. package/dist/web/assets/stex-DNzd62Q_.js +1 -0
  319. package/dist/web/assets/stylus-BOVgKnng.js +1 -0
  320. package/dist/web/assets/swift-DKB6_1j6.js +1 -0
  321. package/dist/web/assets/{swimlanes-2SLR337P-Cc8jyETz.js → swimlanes-2SLR337P-C-_UzZF_.js} +1 -1
  322. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CVSZNvS2.js +8 -0
  323. package/dist/web/assets/tcl-BGfruO1u.js +1 -0
  324. package/dist/web/assets/textile-C4jVoHG1.js +1 -0
  325. package/dist/web/assets/theme-CeQhGDjh.js +1 -0
  326. package/dist/web/assets/tiddlywiki-VlUiK86J.js +1 -0
  327. package/dist/web/assets/tiki-AK6FwT8q.js +1 -0
  328. package/dist/web/assets/{timeline-definition-EJHVYXUP-Bas0Ksw6.js → timeline-definition-EJHVYXUP-BiaJyHdJ.js} +1 -1
  329. package/dist/web/assets/toml-BPTmHmyx.js +1 -0
  330. package/dist/web/assets/{treeView-Q6P3EWNA-Ck-xt3KO.js → treeView-Q6P3EWNA-CENjlerX.js} +1 -1
  331. package/dist/web/assets/{treemap-WGGIJYW6-k9Ay9d-c.js → treemap-WGGIJYW6-CBpdf3L2.js} +1 -1
  332. package/dist/web/assets/troff-C2dAgh7P.js +1 -0
  333. package/dist/web/assets/ttcn-PPSTk1ow.js +1 -0
  334. package/dist/web/assets/ttcn-cfg-2b4Gb5sE.js +1 -0
  335. package/dist/web/assets/turtle-Cx3rYX2g.js +1 -0
  336. package/dist/web/assets/use-add-agent-context-SApxn9fW.js +1 -0
  337. package/dist/web/assets/{use-app-theme-BfRqhHi_.js → use-app-theme-DP8Ymwwb.js} +1 -1
  338. package/dist/web/assets/use-dark-BHObfh0O.js +1 -0
  339. package/dist/web/assets/use-panel-width-CTXD_csP.js +1 -0
  340. package/dist/web/assets/use-router-link-BGc0noIO.js +1 -0
  341. package/dist/web/assets/use-server-events-C13Sw-rU.js +1 -0
  342. package/dist/web/assets/use-virtual-scroll-BSXYZjZ5.js +1 -0
  343. package/dist/web/assets/{usecaseDiagram-POWQR4AR-BlzYgvXB.js → usecaseDiagram-POWQR4AR-CcJ83_Ve.js} +1 -1
  344. package/dist/web/assets/vb-BthdM_4N.js +1 -0
  345. package/dist/web/assets/vbscript-D9f6rSsU.js +1 -0
  346. package/dist/web/assets/velocity-CqftRsjg.js +1 -0
  347. package/dist/web/assets/{vennDiagram-UO4OBE2U-gjloIQdf.js → vennDiagram-UO4OBE2U-DBjzpbFr.js} +1 -1
  348. package/dist/web/assets/verilog-CjI9Y6RI.js +1 -0
  349. package/dist/web/assets/vhdl-kS471soi.js +1 -0
  350. package/dist/web/assets/{wardley-WFR3VGLG-BnsYd7B2.js → wardley-WFR3VGLG-DmlbUWUM.js} +1 -1
  351. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-ktflnFLZ.js → wardleyDiagram-VNRHLVJA-CWSZCqf4.js} +1 -1
  352. package/dist/web/assets/webidl-D5jiJvOU.js +1 -0
  353. package/dist/web/assets/window-store-DNnKUhxs.js +1 -0
  354. package/dist/web/assets/xquery-BKJx4mf9.js +1 -0
  355. package/dist/web/assets/{xychartDiagram-PMCCYNJV-Ch5MYgz0.js → xychartDiagram-PMCCYNJV-CMnNSaZv.js} +1 -1
  356. package/dist/web/assets/yacas-CXXgtw0p.js +1 -0
  357. package/dist/web/assets/z80-Co3PbHdE.js +1 -0
  358. package/dist/web/index.html +6 -3
  359. package/package.json +10 -2
  360. package/dist/web/assets/AppPage-BGcEFW_o.js +0 -93
  361. package/dist/web/assets/CreateReviewFabButton-CdUXVriU.js +0 -1
  362. package/dist/web/assets/IndexPage-CYFIGWy9.css +0 -1
  363. package/dist/web/assets/IndexPage-OqgW0yH8.js +0 -1
  364. package/dist/web/assets/MainLayout-Bi0JE3ER.js +0 -11
  365. package/dist/web/assets/MainLayout-DPkSlqMr.css +0 -1
  366. package/dist/web/assets/QBtn-CzrOO1zA.js +0 -1
  367. package/dist/web/assets/QScrollArea-C4BKutrt.js +0 -1
  368. package/dist/web/assets/QSelect-BXUaq4ky.js +0 -1
  369. package/dist/web/assets/QSpace-BMpRu3a-.js +0 -13
  370. package/dist/web/assets/ReviewDetailPage-DlR7Vxkw.js +0 -1
  371. package/dist/web/assets/ReviewsPage-DM8XtlUK.js +0 -1
  372. package/dist/web/assets/StatusSwitcher-Blg5JmAa.js +0 -1
  373. package/dist/web/assets/agent-store-CDuiBhST.js +0 -1
  374. package/dist/web/assets/app-info-store-BmD_P8q2.js +0 -1
  375. package/dist/web/assets/architecture-7GRP2DOG-DMgDJZyQ.js +0 -1
  376. package/dist/web/assets/channel-BnUyOMnk.js +0 -1
  377. package/dist/web/assets/core-C37KF-j0.js +0 -12
  378. package/dist/web/assets/dagre-6A5THRUB-Bk3Po5o5.js +0 -4
  379. package/dist/web/assets/eventmodeling-NTZA5JFV-7YdTAmVS.js +0 -1
  380. package/dist/web/assets/flowDiagram-KWPJA3E3-eOIiY2oF.js +0 -1
  381. package/dist/web/assets/graphlib-DS17s2tU.js +0 -1
  382. package/dist/web/assets/i18n-C5Xnmkwe.js +0 -1
  383. package/dist/web/assets/index-BqedItFI.js +0 -2
  384. package/dist/web/assets/pinia-BKoxNmoW.js +0 -1
  385. package/dist/web/assets/railroad-abnf-HS5TGJTU-DZSn8MK2.js +0 -1
  386. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BVTJtDBq.js +0 -1
  387. package/dist/web/assets/railroad-peg-WCYAUIDC-DMzsNGzj.js +0 -1
  388. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-BxXXHQ70.js +0 -8
  389. package/dist/web/assets/theme-CPoPdFci.js +0 -1
  390. package/dist/web/assets/use-panel-width-C9dWftsJ.js +0 -1
  391. package/dist/web/assets/use-server-events-znzHKZZx.js +0 -1
  392. package/dist/web/assets/use-virtual-scroll-CIwaJCUj.js +0 -1
@@ -0,0 +1,296 @@
1
+ import { noteChildPath, noteParentPath } from "../../shared/notes/note-path.js";
2
+ import { randomUUID } from 'node:crypto';
3
+ import { lstat } from 'node:fs/promises';
4
+ import { noteReferences, referenceTarget, rewriteReferences, sameReference } from "../../shared/notes/references.js";
5
+ import { NotFoundError } from "../errors/http.js";
6
+ import { NoteConflictError, NoteExistsError } from "../errors/notes.js";
7
+ import { errorMessage } from "../../shared/utils/error-message.js";
8
+ export class NotesOperationsService {
9
+ files;
10
+ catalog;
11
+ writer;
12
+ record;
13
+ changed;
14
+ constructor(options) {
15
+ this.files = options.files;
16
+ this.catalog = options.catalog;
17
+ this.writer = options.writer;
18
+ this.record = options.record;
19
+ this.changed = options.changed;
20
+ }
21
+ issue(id, message, noteId, _candidate, _kind) {
22
+ this.catalog.issue({
23
+ id,
24
+ message,
25
+ noteId,
26
+ candidateId: null,
27
+ kind: 'rewrite'
28
+ });
29
+ }
30
+ async prepare(moves, source, target, folder, moved, preview, restore) {
31
+ const operation = {
32
+ id: randomUUID(),
33
+ moves: moves.map(move => ({
34
+ ...move,
35
+ version: this.record(move.id).version
36
+ })),
37
+ source,
38
+ target,
39
+ folder,
40
+ moved,
41
+ committed: false,
42
+ writes: []
43
+ };
44
+ if (restore)
45
+ operation.restore = restore;
46
+ const records = (preview ?? this.catalog.all()).filter(note => note.presence === 'present' && !note.redirectId && !note.deletedAt);
47
+ const oldPath = (note) => moves.find(move => move.id === note.id)?.from ?? note.path;
48
+ const newPath = (note) => moves.find(move => move.id === note.id)?.to ?? note.path;
49
+ const oldPaths = records.map(oldPath);
50
+ for (const record of records) {
51
+ const disk = await this.files.read(moved ? record.path : oldPath(record));
52
+ if (!moved && disk.version !== record.version) {
53
+ throw new NoteConflictError();
54
+ }
55
+ const refs = noteReferences(disk.text);
56
+ const links = this.catalog.links(record.id);
57
+ const changes = [];
58
+ for (const ref of refs) {
59
+ const matches = links.filter(link => sameReference(link.reference, ref));
60
+ if (matches.some(link => link.ambiguous))
61
+ continue;
62
+ const known = new Set(matches.map(link => link.targetId)).size === 1
63
+ ? matches[0]
64
+ : undefined;
65
+ const resolved = referenceTarget(ref, oldPath(record), oldPaths);
66
+ const targetRecord = known?.targetId
67
+ ? this.catalog.get(known.targetId)
68
+ : records.find(note => oldPath(note) === resolved?.path);
69
+ let destination = targetRecord ? newPath(targetRecord) : resolved?.path;
70
+ if (destination &&
71
+ !targetRecord &&
72
+ source &&
73
+ (destination === source ||
74
+ (folder && destination.startsWith(source + '/')))) {
75
+ destination = target + destination.slice(source.length);
76
+ }
77
+ if (!destination)
78
+ continue;
79
+ if (newPath(record) !== oldPath(record) ||
80
+ (targetRecord && newPath(targetRecord) !== oldPath(targetRecord)) ||
81
+ destination !== resolved?.path) {
82
+ changes.push({
83
+ reference: ref,
84
+ from: newPath(record),
85
+ target: destination,
86
+ anchor: known?.anchor ?? resolved?.anchor ?? ''
87
+ });
88
+ }
89
+ }
90
+ const after = rewriteReferences(disk.text, changes);
91
+ if (after !== disk.text) {
92
+ operation.writes.push({
93
+ id: record.id,
94
+ path: newPath(record),
95
+ before: disk.text,
96
+ after,
97
+ done: false
98
+ });
99
+ }
100
+ }
101
+ if (source) {
102
+ const stat = await lstat(await this.files.path(moved ? target : source, false));
103
+ operation.sourceFingerprint = stat.dev + ':' + stat.ino;
104
+ }
105
+ this.catalog.operation(operation);
106
+ return operation;
107
+ }
108
+ async execute(operation) {
109
+ await this.writer.assertOwner();
110
+ if (!operation.moved) {
111
+ const caseOnly = process.platform === 'win32' &&
112
+ operation.source !== operation.target &&
113
+ operation.source.toLowerCase() === operation.target.toLowerCase();
114
+ if (caseOnly && !operation.temporary) {
115
+ operation.temporary = noteChildPath(noteParentPath(operation.source), '__ght-case-' + operation.id + (operation.folder ? '' : '.md'));
116
+ this.catalog.operation(operation);
117
+ }
118
+ let fromPath = operation.source;
119
+ if (operation.temporary) {
120
+ if (await this.files.exists(operation.temporary)) {
121
+ fromPath = operation.temporary;
122
+ }
123
+ else if (!(await this.files.exactEntry(operation.target))) {
124
+ const stat = await lstat(await this.files.path(operation.source, false));
125
+ if (operation.sourceFingerprint &&
126
+ operation.sourceFingerprint !== stat.dev + ':' + stat.ino) {
127
+ throw new NoteConflictError();
128
+ }
129
+ for (const move of operation.moves) {
130
+ const disk = await this.files.read(move.from);
131
+ if (move.version && disk.version !== move.version) {
132
+ throw new NoteConflictError();
133
+ }
134
+ }
135
+ await this.files.moveEntry(operation.source, operation.temporary, operation.folder);
136
+ fromPath = operation.temporary;
137
+ }
138
+ }
139
+ const from = await this.files.exists(fromPath);
140
+ const to = await this.files.exists(operation.target);
141
+ if (from && to && !caseOnly)
142
+ throw new NoteExistsError();
143
+ const physical = from && !(caseOnly && (await this.files.exactEntry(operation.target)))
144
+ ? fromPath
145
+ : operation.target;
146
+ if (!(await this.files.exists(physical))) {
147
+ throw new NotFoundError('Both operation paths are missing. Recovery needs manual attention.');
148
+ }
149
+ if (operation.sourceFingerprint) {
150
+ const stat = await lstat(await this.files.path(physical, false));
151
+ if (operation.sourceFingerprint !== stat.dev + ':' + stat.ino) {
152
+ throw new NoteConflictError();
153
+ }
154
+ }
155
+ for (const move of operation.moves) {
156
+ const actual = physical === operation.target
157
+ ? move.to
158
+ : fromPath + move.from.slice(operation.source.length);
159
+ const disk = await this.files.read(actual);
160
+ if (move.version && disk.version !== move.version) {
161
+ throw new NoteConflictError();
162
+ }
163
+ }
164
+ if (physical !== operation.target) {
165
+ await this.files.moveEntry(fromPath, operation.target, operation.folder);
166
+ }
167
+ operation.moved = true;
168
+ this.catalog.operation(operation);
169
+ }
170
+ if (!operation.committed) {
171
+ if (operation.restore) {
172
+ const candidate = this.catalog.get(operation.restore.candidateId);
173
+ if (!candidate ||
174
+ candidate.presence !== 'present' ||
175
+ (await this.files.read(candidate.path)).version !==
176
+ operation.restore.version) {
177
+ throw new NoteConflictError();
178
+ }
179
+ }
180
+ this.catalog.transaction(() => {
181
+ if (operation.restore) {
182
+ const candidate = this.record(operation.restore.candidateId);
183
+ this.catalog.put({
184
+ ...candidate,
185
+ presence: 'missing',
186
+ redirectId: operation.restore.previousId
187
+ });
188
+ }
189
+ for (const move of operation.moves) {
190
+ const record = this.record(move.id);
191
+ this.catalog.put({ ...record, presence: 'missing' });
192
+ }
193
+ for (const move of operation.moves) {
194
+ const record = this.record(move.id);
195
+ this.catalog.put({ ...record, path: move.to, presence: 'present' });
196
+ this.catalog.history(record.id, move.from, move.to, operation.id);
197
+ }
198
+ if (operation.restore) {
199
+ this.catalog.setLinks(operation.restore.previousId, operation.restore.links);
200
+ }
201
+ operation.committed = true;
202
+ this.catalog.operation(operation);
203
+ });
204
+ }
205
+ let complete = true;
206
+ for (const write of operation.writes) {
207
+ if (write.done)
208
+ continue;
209
+ try {
210
+ await this.writer.assertOwner();
211
+ const disk = await this.files.read(write.path);
212
+ if (disk.text === write.after) {
213
+ write.done = true;
214
+ }
215
+ else if (disk.text === write.before) {
216
+ await this.files.save(write.path, write.after, disk.version);
217
+ write.done = true;
218
+ }
219
+ else {
220
+ throw new NoteConflictError();
221
+ }
222
+ this.catalog.dismiss('rewrite:' + operation.id + ':' + write.id);
223
+ this.catalog.operation(operation);
224
+ const fresh = await this.files.read(write.path);
225
+ this.changed(write.id, fresh);
226
+ }
227
+ catch (error) {
228
+ complete = false;
229
+ this.issue('rewrite:' + operation.id + ':' + write.id, 'Relative links need repair: ' + errorMessage(error), write.id, null, 'rewrite');
230
+ }
231
+ }
232
+ if (complete) {
233
+ this.catalog.dismiss('operation:' + operation.id);
234
+ this.catalog.finishOperation(operation.id);
235
+ }
236
+ return complete;
237
+ }
238
+ async recover() {
239
+ for (const operation of this.catalog.operations()) {
240
+ try {
241
+ await this.execute(operation);
242
+ }
243
+ catch (error) {
244
+ this.issue('operation:' + operation.id, 'File operation needs attention: ' + errorMessage(error), operation.moves[0]?.id ?? null, null, 'rewrite');
245
+ }
246
+ }
247
+ }
248
+ async retry() {
249
+ await this.writer.acquire();
250
+ const paths = this.catalog
251
+ .all()
252
+ .filter(note => !note.redirectId)
253
+ .map(note => note.path);
254
+ for (const operation of this.catalog.operations()) {
255
+ if (!operation.moved || !operation.committed)
256
+ continue;
257
+ for (const write of operation.writes.filter(item => !item.done)) {
258
+ const disk = await this.files.read(write.path);
259
+ if (disk.text === write.before || disk.text === write.after)
260
+ continue;
261
+ const before = noteReferences(write.before);
262
+ const after = noteReferences(write.after);
263
+ const current = noteReferences(disk.text);
264
+ const changes = [];
265
+ let safe = before.length === after.length;
266
+ for (let i = 0; i < before.length && safe; i++) {
267
+ const old = before[i];
268
+ const next = after[i];
269
+ if (old.href === next.href)
270
+ continue;
271
+ const matches = current.filter(ref => sameReference(ref, old));
272
+ const corrected = current.filter(ref => sameReference(ref, next));
273
+ if (matches.length === 0 && corrected.length === 1)
274
+ continue;
275
+ const target = referenceTarget(next, write.path, paths);
276
+ if (matches.length !== 1 || !target) {
277
+ safe = false;
278
+ break;
279
+ }
280
+ changes.push({
281
+ reference: matches[0],
282
+ from: write.path,
283
+ target: target.path,
284
+ anchor: target.anchor
285
+ });
286
+ }
287
+ if (!safe)
288
+ continue;
289
+ write.before = disk.text;
290
+ write.after = rewriteReferences(disk.text, changes);
291
+ this.catalog.operation(operation);
292
+ }
293
+ }
294
+ await this.recover();
295
+ }
296
+ }
@@ -0,0 +1,44 @@
1
+ import { setInterval, clearInterval } from 'node:timers';
2
+ import { errorMessage } from "../../shared/utils/error-message.js";
3
+ // File metadata detects changes without reading every note body on each poll.
4
+ const POLL_INTERVAL_MS = 2000;
5
+ export function watchNotes(service, onChange) {
6
+ let previous = '';
7
+ // Last snapshot error. Publish an event only when this state changes.
8
+ let previousError = '';
9
+ let pending = false;
10
+ let closed = false;
11
+ async function check() {
12
+ if (pending || closed)
13
+ return;
14
+ pending = true;
15
+ try {
16
+ const next = await service.snapshot();
17
+ if (!closed && (previousError || (previous && previous !== next))) {
18
+ onChange();
19
+ }
20
+ previous = next;
21
+ previousError = '';
22
+ }
23
+ catch (error) {
24
+ const message = errorMessage(error);
25
+ if (!closed && message !== previousError)
26
+ onChange();
27
+ previousError = message;
28
+ }
29
+ finally {
30
+ pending = false;
31
+ }
32
+ }
33
+ void check();
34
+ const timer = setInterval(() => {
35
+ void check();
36
+ }, POLL_INTERVAL_MS);
37
+ timer.unref();
38
+ return {
39
+ close() {
40
+ closed = true;
41
+ clearInterval(timer);
42
+ }
43
+ };
44
+ }
@@ -0,0 +1,116 @@
1
+ import { open, readFile, unlink, mkdir, realpath, lstat } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { hostname } from 'node:os';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { isRecord } from "../../shared/utils/guards.js";
6
+ import { BadRequestError } from "../errors/http.js";
7
+ import { isSystemError } from "../utils/system-error.js";
8
+ // Same-process instances must also honor the filesystem writer lock.
9
+ const OWNERS = new Set();
10
+ async function readOwner(path) {
11
+ if ((await lstat(path)).isSymbolicLink()) {
12
+ throw new BadRequestError('Notes writer lock cannot be a symbolic link.');
13
+ }
14
+ const text = await readFile(path, 'utf8');
15
+ const value = JSON.parse(text);
16
+ if (!isRecord(value) ||
17
+ typeof value.pid !== 'number' ||
18
+ typeof value.host !== 'string' ||
19
+ typeof value.token !== 'string') {
20
+ throw new BadRequestError('Invalid notes writer lock. Verify its owner before removing it.');
21
+ }
22
+ return { pid: value.pid, host: value.host, token: value.token, text };
23
+ }
24
+ export class NotesWriter {
25
+ root = null;
26
+ token = randomUUID();
27
+ configuredRoot;
28
+ constructor(configuredRoot) {
29
+ this.configuredRoot = configuredRoot;
30
+ }
31
+ async acquire(create = false) {
32
+ if (this.root)
33
+ return this.assertOwner();
34
+ if (create)
35
+ await mkdir(this.configuredRoot, { recursive: true });
36
+ let root;
37
+ try {
38
+ root = await realpath(this.configuredRoot);
39
+ }
40
+ catch (error) {
41
+ if (isSystemError(error, 'ENOENT'))
42
+ return;
43
+ throw error;
44
+ }
45
+ if (OWNERS.has(root)) {
46
+ throw new BadRequestError('This notes storage already has an active writer.');
47
+ }
48
+ const path = join(root, '.githuman-writer.lock');
49
+ const data = JSON.stringify({
50
+ pid: process.pid,
51
+ host: hostname(),
52
+ token: this.token
53
+ });
54
+ for (let attempt = 0; attempt < 2; attempt++) {
55
+ try {
56
+ const file = await open(path, 'wx', 0o600);
57
+ try {
58
+ await file.writeFile(data);
59
+ await file.sync();
60
+ }
61
+ finally {
62
+ await file.close();
63
+ }
64
+ this.root = root;
65
+ OWNERS.add(root);
66
+ return;
67
+ }
68
+ catch (error) {
69
+ if (!isSystemError(error, 'EEXIST'))
70
+ throw error;
71
+ const owner = await readOwner(path);
72
+ const old = owner.text;
73
+ if (owner.host !== hostname() ||
74
+ !Number.isSafeInteger(owner.pid) ||
75
+ owner.pid <= 0) {
76
+ throw new BadRequestError('Notes writer lock belongs to another host or has invalid ownership.');
77
+ }
78
+ try {
79
+ process.kill(owner.pid, 0);
80
+ }
81
+ catch (killError) {
82
+ if (isSystemError(killError, 'ESRCH')) {
83
+ if ((await readFile(path, 'utf8')) === old)
84
+ await unlink(path);
85
+ continue;
86
+ }
87
+ throw killError;
88
+ }
89
+ throw new BadRequestError('This notes storage already has an active writer.');
90
+ }
91
+ }
92
+ throw new BadRequestError('Could not acquire the notes writer lock.');
93
+ }
94
+ async assertOwner() {
95
+ if (!this.root)
96
+ throw new BadRequestError('Notes writer is not active.');
97
+ const data = await readOwner(join(this.root, '.githuman-writer.lock'));
98
+ if (data.token !== this.token) {
99
+ throw new BadRequestError('Notes writer ownership changed.');
100
+ }
101
+ }
102
+ async close() {
103
+ if (!this.root)
104
+ return;
105
+ try {
106
+ await this.assertOwner();
107
+ await unlink(join(this.root, '.githuman-writer.lock'));
108
+ }
109
+ catch (error) {
110
+ if (!isSystemError(error, 'ENOENT'))
111
+ throw error;
112
+ }
113
+ OWNERS.delete(this.root);
114
+ this.root = null;
115
+ }
116
+ }
@@ -0,0 +1,150 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { appendFile, readFile } from 'node:fs/promises';
4
+ import { relative, resolve } from 'node:path';
5
+ import { createTestDatabase } from "../db/index.js";
6
+ import { NotesIdentityService } from "./notes-identity.service.js";
7
+ import { NotesRepository } from "../repositories/notes.repo.js";
8
+ import { noteMetadata } from "../../shared/notes/document.js";
9
+ import { BadRequestError } from "../errors/http.js";
10
+ import { errorMessage } from "../../shared/utils/error-message.js";
11
+ import { MAX_NOTE_ASSET_BYTES } from "../../shared/notes/constants.js";
12
+ import { detectNoteAssetFormat } from "../utils/note-asset-format.js";
13
+ // Git error text depends on the locale. The "not a git repository" check below needs English.
14
+ const GIT_ENV = { ...process.env, LC_ALL: 'C' };
15
+ const execFileAsync = promisify(execFile);
16
+ export class NotesService {
17
+ repository;
18
+ inspected;
19
+ exclusion;
20
+ identity;
21
+ snapshotValue = '';
22
+ warning = null;
23
+ projectPath;
24
+ root;
25
+ local;
26
+ bus;
27
+ constructor(projectPath, root, local, bus, db = createTestDatabase()) {
28
+ this.projectPath = projectPath;
29
+ this.root = root;
30
+ this.local = local;
31
+ this.bus = bus;
32
+ this.repository = new NotesRepository(root);
33
+ this.identity = new NotesIdentityService(this.repository, db);
34
+ }
35
+ async visibility(ensureIgnored = false) {
36
+ try {
37
+ const { stdout: gitRoot } = await execFileAsync('git', ['rev-parse', '--show-toplevel'], { cwd: this.projectPath, env: GIT_ENV });
38
+ const path = relative(gitRoot.trim(), this.root).replaceAll('\\', '/');
39
+ if (path.startsWith('../'))
40
+ return;
41
+ const { stdout: tracked } = await execFileAsync('git', ['ls-files', '--', path + '/'], { cwd: gitRoot.trim(), env: GIT_ENV });
42
+ if (tracked && this.local) {
43
+ this.warning =
44
+ 'Some notes are already tracked by Git. Ignoring them does not remove them from Git.';
45
+ }
46
+ if (!this.local || !ensureIgnored)
47
+ return;
48
+ const { stdout: exclude } = await execFileAsync('git', ['rev-parse', '--git-path', 'info/exclude'], { cwd: this.projectPath, env: GIT_ENV });
49
+ const target = resolve(this.projectPath, exclude.trim());
50
+ const rule = '/' + path.replace(/[*?[\]#! ]/g, char => '\\' + char) + '/';
51
+ const content = await readFile(target, 'utf8').catch(() => '');
52
+ if (!content.split(/\r?\n/).includes(rule)) {
53
+ await appendFile(target, '\n' + rule + '\n');
54
+ }
55
+ }
56
+ catch (error) {
57
+ if (!(error instanceof Error &&
58
+ 'stderr' in error &&
59
+ String(error.stderr).includes('not a git repository'))) {
60
+ this.warning = 'Could not verify Git exclusion: ' + errorMessage(error);
61
+ }
62
+ }
63
+ }
64
+ async index() {
65
+ this.inspected ??= this.visibility();
66
+ await this.inspected;
67
+ const index = await this.identity.index();
68
+ this.snapshotValue = JSON.stringify([
69
+ index,
70
+ this.identity.catalog
71
+ .all()
72
+ .map(note => [note.id, note.version, note.presence, note.deletedAt]),
73
+ this.warning
74
+ ]);
75
+ return { ...index, warning: this.warning };
76
+ }
77
+ async snapshot() {
78
+ await this.index();
79
+ return this.snapshotValue;
80
+ }
81
+ read(path) {
82
+ return this.identity.readPath(path);
83
+ }
84
+ async save(path, text, version, id, storageId) {
85
+ await this.repository.path(path);
86
+ const metadata = noteMetadata(text);
87
+ if (metadata.error)
88
+ throw new BadRequestError(metadata.error);
89
+ this.exclusion ??= this.visibility(true);
90
+ await this.exclusion;
91
+ const note = id && storageId && version !== undefined
92
+ ? await this.identity.saveId(id, storageId, text, version)
93
+ : await this.identity.save(path, text, version);
94
+ this.bus.publish({ type: 'notes:changed' });
95
+ return note;
96
+ }
97
+ async delete(id, storageId) {
98
+ await this.identity.deleteId(id, storageId);
99
+ this.bus.publish({ type: 'notes:changed' });
100
+ }
101
+ async uploadAsset(bytes) {
102
+ if (!bytes.length || bytes.length > MAX_NOTE_ASSET_BYTES) {
103
+ throw new BadRequestError('Image must be under 10 MB');
104
+ }
105
+ const format = await detectNoteAssetFormat(bytes);
106
+ if (format.display !== 'image') {
107
+ throw new BadRequestError('Only image uploads are supported');
108
+ }
109
+ this.exclusion ??= this.visibility(true);
110
+ await this.exclusion;
111
+ const id = await this.identity.run(async () => {
112
+ await this.repository.path('assets/probe.png', false);
113
+ await this.identity.writer.acquire(true);
114
+ await this.identity.writer.assertOwner();
115
+ return this.repository.saveAsset(bytes, format.extension);
116
+ });
117
+ return { url: '/api/notes/assets/' + id };
118
+ }
119
+ async importAsset(chunks, extension, signal) {
120
+ this.exclusion ??= this.visibility(true);
121
+ await this.exclusion;
122
+ const staged = await this.repository.stageAsset(chunks, extension, signal);
123
+ try {
124
+ const id = await this.identity.run(async () => {
125
+ await this.identity.writer.acquire(true);
126
+ await this.identity.writer.assertOwner();
127
+ return staged.commit();
128
+ });
129
+ return '/api/notes/assets/' + id;
130
+ }
131
+ finally {
132
+ await staged.dispose();
133
+ }
134
+ }
135
+ readAsset(id) {
136
+ return this.repository.readAsset(id);
137
+ }
138
+ async rename(path, name, folder, target, id, revision) {
139
+ const result = await this.identity.rename(path, name, folder, target, id, revision);
140
+ this.bus.publish({ type: 'notes:changed' });
141
+ return result;
142
+ }
143
+ async folder(path) {
144
+ await this.repository.path(path, false);
145
+ this.exclusion ??= this.visibility(true);
146
+ await this.exclusion;
147
+ await this.identity.folder(path);
148
+ this.bus.publish({ type: 'notes:changed' });
149
+ }
150
+ }
@@ -24,8 +24,9 @@ function promptPrefix(shellName) {
24
24
  function promptSetup(shellName) {
25
25
  const assign = `PS1=${promptPrefix(shellName)}"$PS1"`;
26
26
  const marker = "printf '\\033]133;A\\007'";
27
- if (shellName === 'bash')
27
+ if (shellName === 'bash') {
28
28
  return `${assign}; history -d $HISTCMD || true; ${marker}`;
29
+ }
29
30
  return `${assign}; ${marker}`;
30
31
  }
31
32
  async function shellActivity(pid, prompt, rows) {
@@ -150,8 +151,9 @@ export class PipeBackend {
150
151
  this.child.stdout.setEncoding('utf8');
151
152
  this.child.stdout.on('data', (data) => {
152
153
  this.prompt.output(data);
153
- for (const listener of this.dataListeners)
154
+ for (const listener of this.dataListeners) {
154
155
  listener(data.replace(/\n/g, '\r\n'));
156
+ }
155
157
  });
156
158
  this.child.on('error', () => {
157
159
  for (const listener of this.exitListeners)
@@ -198,8 +200,9 @@ export class PipeBackend {
198
200
  }
199
201
  }
200
202
  export async function createBackend(cwd, cols, rows) {
201
- if (!terminalSupported())
203
+ if (!terminalSupported()) {
202
204
  throw new TerminalError('The terminal is not available on Windows.');
205
+ }
203
206
  const module = await loadPty();
204
207
  if (!module)
205
208
  return new PipeBackend(cwd);
@@ -45,12 +45,13 @@ export class TerminalChannel {
45
45
  switch (value.type) {
46
46
  case 'create': {
47
47
  const session = await this.registry.create(value.cols, value.rows);
48
- if (!this.stopped)
48
+ if (!this.stopped) {
49
49
  this.send({
50
50
  type: 'created',
51
51
  requestId: value.requestId,
52
52
  terminalId: session.id
53
53
  });
54
+ }
54
55
  break;
55
56
  }
56
57
  case 'subscribe':
@@ -83,13 +84,14 @@ export class TerminalChannel {
83
84
  }
84
85
  }
85
86
  catch (error) {
86
- if (!this.stopped)
87
+ if (!this.stopped) {
87
88
  this.send({
88
89
  type: 'error',
89
90
  ...('requestId' in value ? { requestId: value.requestId } : {}),
90
91
  code: 'GHT_TERMINAL',
91
92
  message: errorMessage(error)
92
93
  });
94
+ }
93
95
  }
94
96
  return undefined;
95
97
  })
@@ -134,11 +136,12 @@ export class TerminalChannel {
134
136
  this.disconnect();
135
137
  return;
136
138
  }
137
- for (const [key, bytes] of credit.frames)
139
+ for (const [key, bytes] of credit.frames) {
138
140
  if (key <= sequence) {
139
141
  credit.bytes -= bytes;
140
142
  credit.frames.delete(key);
141
143
  }
144
+ }
142
145
  this.flow(id, credit);
143
146
  }
144
147
  flow(id, credit) {