@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,811 @@
1
+ import PQueue from 'p-queue';
2
+ import { noteBasename, noteChildPath, noteParentPath } from "../../shared/notes/note-path.js";
3
+ import { lstat } from 'node:fs/promises';
4
+ import { isSystemError } from "../utils/system-error.js";
5
+ import { randomUUID } from 'node:crypto';
6
+ import { NotesCatalogRepository } from "../repositories/notes-catalog.repo.js";
7
+ import { NotesOperationsService } from "./notes-operations.service.js";
8
+ import { NotesWriter } from "./notes-writer.service.js";
9
+ import { noteIdentity, withNoteIdentity } from "../../shared/notes/identity.js";
10
+ import { noteMetadata } from "../../shared/notes/document.js";
11
+ import { noteReferences, referenceTarget, sameReference } from "../../shared/notes/references.js";
12
+ import { BadRequestError, NotFoundError } from "../errors/http.js";
13
+ import { NoteConflictError, NoteExistsError } from "../errors/notes.js";
14
+ import { sha256Hex } from "../utils/hash.js";
15
+ import { safeDecodeURIComponent } from "../../shared/utils/safe-decode.js";
16
+ // A second complete scan confirms absence, instead of treating an atomic save as deletion.
17
+ const ABSENCE_GRACE_MS = 2500;
18
+ // Longest file name that common file systems accept, in bytes.
19
+ const MAX_FILENAME_BYTES = 255;
20
+ function newRecord(id, path, version) {
21
+ const now = new Date().toISOString();
22
+ return {
23
+ id,
24
+ path,
25
+ version,
26
+ presence: 'present',
27
+ createdAt: now,
28
+ updatedAt: now,
29
+ deletedAt: null,
30
+ redirectId: null
31
+ };
32
+ }
33
+ function portablePath(path) {
34
+ for (const part of path.split('/')) {
35
+ if (/[<>:"\\|?*]/.test(part) ||
36
+ part.split('').some(char => char.charCodeAt(0) < 32) ||
37
+ /[. ]$/.test(part) ||
38
+ /^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/i.test(part)) {
39
+ throw new BadRequestError('The destination name is not portable to Windows.');
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Path a deleted note moves to. The suffix frees the original name for a new
45
+ * note. The stem is cut by bytes so the name fits the file system limit.
46
+ */
47
+ function deletedNotePath(path, uuid) {
48
+ const suffix = `_deleted_${uuid}.md`;
49
+ const stem = noteBasename(path).replace(/\.md$/i, '');
50
+ const room = MAX_FILENAME_BYTES - Buffer.byteLength(suffix);
51
+ let cut = stem;
52
+ while (Buffer.byteLength(cut) > room) {
53
+ cut = cut.slice(0, -1);
54
+ }
55
+ return noteChildPath(noteParentPath(path), cut + suffix);
56
+ }
57
+ export class NotesIdentityService {
58
+ catalog;
59
+ writer;
60
+ operations;
61
+ // One action at a time: catalog and file changes must not interleave.
62
+ queue = new PQueue({ concurrency: 1 });
63
+ cache = new Map();
64
+ absentSince = new Map();
65
+ // Signature of each file that failed to read, so a poll does not read it again.
66
+ unreadable = new Map();
67
+ closed = false;
68
+ files;
69
+ constructor(files, db) {
70
+ this.files = files;
71
+ this.catalog = new NotesCatalogRepository(db, files.root);
72
+ this.writer = new NotesWriter(files.root);
73
+ this.operations = new NotesOperationsService({
74
+ files,
75
+ catalog: this.catalog,
76
+ writer: this.writer,
77
+ record: id => this.record(id),
78
+ changed: (id, note) => {
79
+ this.catalog.put({
80
+ ...this.record(id),
81
+ version: note.version,
82
+ updatedAt: new Date().toISOString()
83
+ });
84
+ this.indexLinks(id, note.text);
85
+ this.cache.set(note.path, {
86
+ signature: '',
87
+ note: { version: note.version },
88
+ identity: noteIdentity(note.text),
89
+ metadata: noteMetadata(note.text)
90
+ });
91
+ }
92
+ });
93
+ }
94
+ run(action) {
95
+ return this.queue.add(async () => {
96
+ if (this.closed)
97
+ throw new BadRequestError('Notes storage is closing.');
98
+ return action();
99
+ });
100
+ }
101
+ async close() {
102
+ this.closed = true;
103
+ await this.queue.onIdle();
104
+ await this.writer.close();
105
+ }
106
+ record(id) {
107
+ let note = this.catalog.get(id);
108
+ const seen = new Set();
109
+ while (note?.redirectId && !seen.has(note.id)) {
110
+ seen.add(note.id);
111
+ note = this.catalog.get(note.redirectId);
112
+ }
113
+ if (!note)
114
+ throw new NotFoundError('Unknown note identity.');
115
+ return note;
116
+ }
117
+ issue(id, message, noteId, candidateId = null, kind = 'identity') {
118
+ this.catalog.issue({ id, message, noteId, candidateId, kind });
119
+ }
120
+ async synchronize() {
121
+ await this.files.path('probe.md');
122
+ try {
123
+ await lstat(this.files.root);
124
+ }
125
+ catch (error) {
126
+ if (isSystemError(error, 'ENOENT') && !this.catalog.all().length) {
127
+ return [];
128
+ }
129
+ throw new BadRequestError('Notes storage is unavailable. Existing identities are retained.');
130
+ }
131
+ await this.writer.acquire();
132
+ if (this.catalog.operations().length)
133
+ await this.operations.recover();
134
+ const entries = await this.files.list();
135
+ const documents = [];
136
+ // Read the whole snapshot successfully before changing any presence records.
137
+ for (const entry of entries) {
138
+ if (entry.folder)
139
+ continue;
140
+ let references;
141
+ let cached = this.cache.get(entry.path);
142
+ if (!cached || cached.signature !== entry.signature) {
143
+ if (this.unreadable.get(entry.path) === entry.signature)
144
+ continue;
145
+ let note;
146
+ try {
147
+ note = await this.files.read(entry.path);
148
+ }
149
+ catch (error) {
150
+ // One bad file must not stop the synchronization of other notes.
151
+ if (!(error instanceof BadRequestError))
152
+ throw error;
153
+ this.unreadable.set(entry.path, entry.signature);
154
+ this.issue('unreadable:' + entry.path, 'Skipped ' + entry.path + ': ' + error.message, null, null, 'metadata');
155
+ continue;
156
+ }
157
+ this.unreadable.delete(entry.path);
158
+ this.catalog.dismiss('unreadable:' + entry.path);
159
+ cached = {
160
+ signature: entry.signature,
161
+ note: { version: note.version },
162
+ identity: noteIdentity(note.text),
163
+ metadata: noteMetadata(note.text)
164
+ };
165
+ references = noteReferences(note.text);
166
+ this.cache.set(entry.path, cached);
167
+ }
168
+ documents.push({
169
+ path: entry.path,
170
+ note: cached.note,
171
+ yamlId: cached.identity.id,
172
+ identityWarning: cached.identity.warning,
173
+ ...(references ? { references } : {})
174
+ });
175
+ }
176
+ const entryPaths = new Set(entries.map(entry => entry.path));
177
+ for (const path of this.unreadable.keys()) {
178
+ if (entryPaths.has(path))
179
+ continue;
180
+ this.unreadable.delete(path);
181
+ this.catalog.dismiss('unreadable:' + path);
182
+ }
183
+ const records = this.catalog.all();
184
+ const byId = new Map(records.map(record => [record.id, record]));
185
+ const presentByPath = new Map(records
186
+ .filter(record => (record.presence === 'present' || record.deletedAt) &&
187
+ !record.redirectId)
188
+ .map(record => [record.path, record]));
189
+ const livePaths = new Set(documents.map(doc => doc.path));
190
+ const counts = new Map();
191
+ for (const doc of documents) {
192
+ if (doc.yamlId)
193
+ counts.set(doc.yamlId, (counts.get(doc.yamlId) ?? 0) + 1);
194
+ }
195
+ const changes = [];
196
+ const changed = new Set();
197
+ this.catalog.transaction(() => {
198
+ // Release paths first, so swaps and folder moves do not violate uniqueness.
199
+ for (const record of records) {
200
+ if (record.presence !== 'present' || livePaths.has(record.path)) {
201
+ continue;
202
+ }
203
+ if (documents.some(doc => doc.yamlId === record.id && counts.get(record.id) === 1)) {
204
+ this.catalog.put({ ...record, presence: 'missing' });
205
+ continue;
206
+ }
207
+ const since = this.absentSince.get(record.id);
208
+ if (since === undefined) {
209
+ this.catalog.reopen('missing:' + record.id);
210
+ this.absentSince.set(record.id, Date.now());
211
+ }
212
+ else if (Date.now() - since >= ABSENCE_GRACE_MS) {
213
+ this.catalog.put({ ...record, presence: 'missing' });
214
+ }
215
+ }
216
+ for (const doc of documents) {
217
+ const byPath = presentByPath.get(doc.path);
218
+ const hinted = doc.yamlId && counts.get(doc.yamlId) === 1
219
+ ? byId.get(doc.yamlId)
220
+ : undefined;
221
+ let record = byPath;
222
+ if (hinted && hinted !== byPath && !livePaths.has(hinted.path)) {
223
+ if (byPath)
224
+ this.catalog.put({ ...byPath, presence: 'missing' });
225
+ record = hinted;
226
+ }
227
+ else if (!record && hinted && hinted.path === doc.path) {
228
+ record = hinted;
229
+ }
230
+ if (!record) {
231
+ const id = doc.yamlId &&
232
+ counts.get(doc.yamlId) === 1 &&
233
+ !this.catalog.get(doc.yamlId)
234
+ ? doc.yamlId
235
+ : randomUUID();
236
+ record = newRecord(id, doc.path, doc.note.version);
237
+ this.catalog.put(record);
238
+ changed.add(record.id);
239
+ }
240
+ if (record.path !== doc.path) {
241
+ changes.push({ id: record.id, from: record.path, to: doc.path });
242
+ }
243
+ if (record.version !== doc.note.version)
244
+ changed.add(record.id);
245
+ if (record.path !== doc.path ||
246
+ record.presence !== 'present' ||
247
+ record.version !== doc.note.version) {
248
+ this.catalog.put({
249
+ ...record,
250
+ path: doc.path,
251
+ presence: 'present',
252
+ version: doc.note.version,
253
+ updatedAt: record.version === doc.note.version
254
+ ? record.updatedAt
255
+ : new Date().toISOString()
256
+ });
257
+ }
258
+ this.absentSince.delete(record.id);
259
+ this.catalog.dismiss('missing:' + record.id);
260
+ const identity = { id: doc.yamlId, warning: doc.identityWarning };
261
+ const warning = identity.warning ??
262
+ (identity.id && identity.id !== record.id
263
+ ? 'The YAML ID conflicts with this note or is duplicated. The catalog identity is retained.'
264
+ : null);
265
+ if (warning && !record.deletedAt) {
266
+ this.issue('metadata:' + record.id, warning, record.id, null, 'metadata');
267
+ }
268
+ else {
269
+ this.catalog.dismiss('metadata:' + record.id);
270
+ }
271
+ }
272
+ });
273
+ if (changes.length) {
274
+ const operation = await this.operations.prepare(changes, '', '', false, true);
275
+ await this.operations.execute(operation);
276
+ }
277
+ const current = this.catalog.all();
278
+ const documentsByPath = new Map(documents.map(doc => [doc.path, doc]));
279
+ const currentByPath = new Map(current
280
+ .filter(note => note.presence === 'present' && !note.redirectId)
281
+ .map(note => [note.path, note]));
282
+ const byVersion = new Map();
283
+ for (const candidate of current.filter(note => note.presence === 'present' && !note.redirectId)) {
284
+ const group = byVersion.get(candidate.version) ?? [];
285
+ group.push(candidate);
286
+ byVersion.set(candidate.version, group);
287
+ }
288
+ for (const old of current.filter(note => note.presence === 'missing' && !note.redirectId && !note.deletedAt)) {
289
+ const samePath = currentByPath.get(old.path);
290
+ const matches = (byVersion.get(old.version) ?? []).filter(candidate => !documentsByPath.get(candidate.path)?.yamlId);
291
+ const candidate = samePath ?? (matches.length === 1 ? matches[0] : undefined);
292
+ this.issue('missing:' + old.id, 'File missing: ' +
293
+ old.path +
294
+ '. Choose a moved file, or confirm deletion.', old.id, candidate?.id ?? null);
295
+ }
296
+ for (const doc of documents) {
297
+ const record = currentByPath.get(doc.path);
298
+ if (!record || record.deletedAt || !changed.has(record.id))
299
+ continue;
300
+ const references = changes.length
301
+ ? noteReferences((await this.files.read(doc.path)).text)
302
+ : (doc.references ?? []);
303
+ this.indexReferences(record.id, references, current);
304
+ }
305
+ for (const path of this.cache.keys()) {
306
+ if (!livePaths.has(path))
307
+ this.cache.delete(path);
308
+ }
309
+ // Unreadable files stay visible through their issue, not as openable entries.
310
+ const hiddenPaths = new Set(current.filter(note => note.deletedAt).map(note => note.path));
311
+ return entries.filter(entry => !hiddenPaths.has(entry.path) &&
312
+ this.unreadable.get(entry.path) !== entry.signature);
313
+ }
314
+ indexLinks(id, text, snapshot) {
315
+ this.indexReferences(id, noteReferences(text), snapshot);
316
+ }
317
+ indexReferences(id, refs, snapshot) {
318
+ const record = this.record(id);
319
+ const oldLinks = this.catalog.links(id);
320
+ if (!refs.length) {
321
+ if (oldLinks.length)
322
+ this.catalog.setLinks(id, []);
323
+ return;
324
+ }
325
+ const records = (snapshot ?? this.catalog.all()).filter(note => !note.redirectId);
326
+ const paths = records
327
+ .filter(note => note.presence === 'present')
328
+ .map(note => note.path);
329
+ const links = refs
330
+ .filter(ref => !ref.image)
331
+ .map(ref => {
332
+ const target = referenceTarget(ref, record.path, paths);
333
+ const old = oldLinks.filter(link => sameReference(link.reference, ref));
334
+ const duplicates = refs.filter(other => sameReference(ref, other)).length;
335
+ if (old.length === duplicates &&
336
+ old[0]?.targetId &&
337
+ !old.some(link => link.ambiguous) &&
338
+ new Set(old.map(link => link.targetId)).size === 1) {
339
+ return { ...old[0], reference: ref };
340
+ }
341
+ const changedOccurrence = old.length === 0 &&
342
+ oldLinks.some(link => link.reference.href === ref.href &&
343
+ link.targetId &&
344
+ (this.record(link.targetId).presence !== 'present' ||
345
+ this.record(link.targetId).path !== target?.path));
346
+ const ambiguous = changedOccurrence ||
347
+ (old.length > 0 &&
348
+ (old.length !== duplicates ||
349
+ new Set(old.map(link => link.targetId)).size > 1));
350
+ if (ambiguous) {
351
+ return {
352
+ reference: ref,
353
+ targetId: null,
354
+ targetPath: target?.path ?? null,
355
+ anchor: target?.anchor ?? '',
356
+ ambiguous: true
357
+ };
358
+ }
359
+ const present = target
360
+ ? records.find(note => note.presence === 'present' && note.path === target.path)
361
+ : undefined;
362
+ const historical = target && !present
363
+ ? [
364
+ ...new Set([
365
+ ...this.catalog.historical(target.path),
366
+ ...records
367
+ .filter(note => note.path === target.path && note.presence !== 'present')
368
+ .map(note => note.id)
369
+ ])
370
+ ]
371
+ : [];
372
+ return {
373
+ reference: ref,
374
+ targetId: present?.id ?? (historical.length === 1 ? historical[0] : null),
375
+ targetPath: target?.path ?? null,
376
+ anchor: target?.anchor ?? '',
377
+ ambiguous: historical.length > 1
378
+ };
379
+ })
380
+ .filter(link => link.targetPath?.toLowerCase().endsWith('.md') ||
381
+ link.reference.format === 'wiki');
382
+ this.catalog.setLinks(id, links);
383
+ }
384
+ revision(entries) {
385
+ return sha256Hex(JSON.stringify([
386
+ entries,
387
+ this.catalog
388
+ .all()
389
+ .map(note => [
390
+ note.id,
391
+ note.path,
392
+ note.version,
393
+ note.presence,
394
+ note.deletedAt
395
+ ])
396
+ ]));
397
+ }
398
+ index() {
399
+ return this.run(async () => {
400
+ const entries = await this.synchronize();
401
+ const records = this.catalog.all();
402
+ const recordsByPath = new Map(records
403
+ .filter(note => note.presence === 'present' && !note.redirectId)
404
+ .map(note => [note.path, note]));
405
+ return {
406
+ storage: this.files.root,
407
+ storageId: this.catalog.storageId,
408
+ revision: this.revision(entries),
409
+ issues: this.catalog
410
+ .issues()
411
+ .filter(issue => !issue.noteId || !this.record(issue.noteId).deletedAt),
412
+ entries: entries.map(entry => {
413
+ const record = recordsByPath.get(entry.path);
414
+ const metadata = entry.folder
415
+ ? { tags: [], error: null }
416
+ : (this.cache.get(entry.path)?.metadata ?? {
417
+ tags: [],
418
+ error: null
419
+ });
420
+ return {
421
+ path: entry.path,
422
+ folder: entry.folder,
423
+ ...metadata,
424
+ ...(record ? { id: record.id, presence: record.presence } : {})
425
+ };
426
+ })
427
+ };
428
+ });
429
+ }
430
+ descriptor(id, storageId) {
431
+ return this.run(async () => {
432
+ this.assertStorage(storageId);
433
+ await this.synchronize();
434
+ const registered = this.record(id);
435
+ const note = registered.deletedAt
436
+ ? { ...registered, presence: 'deleted' }
437
+ : registered.presence === 'present' &&
438
+ !(await this.files.exists(registered.path))
439
+ ? { ...registered, presence: 'missing' }
440
+ : registered;
441
+ const backlinks = this.catalog
442
+ .all()
443
+ .filter(source => source.presence === 'present' &&
444
+ !source.deletedAt &&
445
+ this.catalog
446
+ .links(source.id)
447
+ .some(link => !!link.targetId && this.record(link.targetId).id === note.id));
448
+ return { note, backlinks };
449
+ });
450
+ }
451
+ resolvePath(path) {
452
+ return this.run(async () => {
453
+ await this.synchronize();
454
+ const records = this.catalog.all().filter(note => !note.redirectId);
455
+ const candidates = new Set([
456
+ ...records.filter(note => note.path === path).map(note => note.id),
457
+ ...this.catalog.historical(path)
458
+ ]);
459
+ if (!candidates.size) {
460
+ throw new NotFoundError('The note path was not found.');
461
+ }
462
+ if (candidates.size !== 1) {
463
+ throw new BadRequestError('This path belongs to more than one note over time. Open a stable UUID link instead.');
464
+ }
465
+ const record = this.record([...candidates][0]);
466
+ return record.deletedAt
467
+ ? { ...record, presence: 'deleted' }
468
+ : record;
469
+ });
470
+ }
471
+ assertStorage(id) {
472
+ if (id !== this.catalog.storageId) {
473
+ throw new BadRequestError('This link belongs to a different notes storage.');
474
+ }
475
+ }
476
+ readId(id, storageId) {
477
+ return this.run(async () => {
478
+ this.assertStorage(storageId);
479
+ await this.synchronize();
480
+ const record = this.record(id);
481
+ if (record.presence !== 'present' || record.deletedAt) {
482
+ throw new NotFoundError('The note file is missing or deleted.');
483
+ }
484
+ return {
485
+ ...(await this.files.read(record.path)),
486
+ id: record.id,
487
+ storageId
488
+ };
489
+ });
490
+ }
491
+ readPath(path) {
492
+ return this.run(async () => {
493
+ await this.synchronize();
494
+ const record = this.catalog
495
+ .all()
496
+ .find(note => note.path === path &&
497
+ note.presence === 'present' &&
498
+ !note.redirectId);
499
+ if (!record || record.deletedAt) {
500
+ throw new NotFoundError('Note not found.');
501
+ }
502
+ return {
503
+ ...(await this.files.read(path)),
504
+ id: record.id,
505
+ storageId: this.catalog.storageId
506
+ };
507
+ });
508
+ }
509
+ save(path, text, version, id) {
510
+ return this.run(async () => {
511
+ const metadata = noteMetadata(text);
512
+ if (metadata.error)
513
+ throw new BadRequestError(metadata.error);
514
+ await this.files.path(path);
515
+ await this.writer.acquire(true);
516
+ await this.synchronize();
517
+ if (id) {
518
+ const identified = this.record(id);
519
+ if (identified.deletedAt) {
520
+ throw new NotFoundError('This note was deleted.');
521
+ }
522
+ path = identified.path;
523
+ }
524
+ let record = this.catalog
525
+ .all()
526
+ .find(note => note.path === path &&
527
+ note.presence === 'present' &&
528
+ !note.redirectId);
529
+ if (this.catalog.all().some(note => note.path === path && note.deletedAt)) {
530
+ throw new NoteExistsError();
531
+ }
532
+ if (version === undefined) {
533
+ portablePath(path);
534
+ if (record)
535
+ throw new NoteExistsError();
536
+ record = newRecord(randomUUID(), path, '');
537
+ text = withNoteIdentity(text, record.id);
538
+ }
539
+ if (!record)
540
+ throw new NotFoundError('Note identity not found.');
541
+ await this.writer.assertOwner();
542
+ const disk = await this.files.save(path, text, version);
543
+ this.catalog.put({
544
+ ...record,
545
+ version: disk.version,
546
+ updatedAt: new Date().toISOString()
547
+ });
548
+ this.indexLinks(record.id, disk.text);
549
+ return { ...disk, id: record.id, storageId: this.catalog.storageId };
550
+ });
551
+ }
552
+ saveId(id, storageId, text, version) {
553
+ this.assertStorage(storageId);
554
+ return this.save(this.record(id).path, text, version, id);
555
+ }
556
+ folder(path) {
557
+ return this.run(async () => {
558
+ portablePath(path);
559
+ await this.files.path(path, false);
560
+ await this.writer.acquire(true);
561
+ await this.writer.assertOwner();
562
+ await this.files.folder(path);
563
+ });
564
+ }
565
+ rename(path, name, folder, target, id, expectedRevision) {
566
+ return this.run(async () => {
567
+ const entries = await this.synchronize();
568
+ if (expectedRevision && expectedRevision !== this.revision(entries)) {
569
+ throw new NoteConflictError();
570
+ }
571
+ if (id && this.record(id).path !== path)
572
+ throw new NoteConflictError();
573
+ if (!folder &&
574
+ this.catalog.all().some(note => note.path === path && note.deletedAt)) {
575
+ throw new NotFoundError('This note was deleted.');
576
+ }
577
+ if (!target &&
578
+ (name !== name.trim() ||
579
+ name.includes('/') ||
580
+ name.includes('\\') ||
581
+ name.startsWith('.'))) {
582
+ throw new BadRequestError('Invalid entry name.');
583
+ }
584
+ const sourcePath = await this.files.path(path, !folder);
585
+ if (path.split('/')[0] === 'assets') {
586
+ throw new BadRequestError('Cannot rename attachments.');
587
+ }
588
+ const sourceStat = await lstat(sourcePath);
589
+ if (folder ? !sourceStat.isDirectory() : !sourceStat.isFile()) {
590
+ throw new BadRequestError('Entry type changed.');
591
+ }
592
+ const destination = target ?? noteChildPath(noteParentPath(path), name);
593
+ portablePath(destination);
594
+ await this.files.path(destination, !folder);
595
+ if (destination === path) {
596
+ return { operationId: randomUUID(), complete: true };
597
+ }
598
+ if (this.catalog
599
+ .all()
600
+ .some(note => note.deletedAt && note.path === destination)) {
601
+ throw new NoteExistsError();
602
+ }
603
+ if (await this.files.exists(destination)) {
604
+ const caseOnly = process.platform === 'win32' &&
605
+ path.toLowerCase() === destination.toLowerCase() &&
606
+ (await this.files.sameEntry(path, destination));
607
+ if (!caseOnly)
608
+ throw new NoteExistsError();
609
+ }
610
+ if (folder && destination.startsWith(path + '/')) {
611
+ throw new BadRequestError('Cannot move a directory into itself.');
612
+ }
613
+ const moves = this.catalog
614
+ .all()
615
+ .filter(note => note.presence === 'present' &&
616
+ (note.path === path || (folder && note.path.startsWith(path + '/'))))
617
+ .map(note => ({
618
+ id: note.id,
619
+ from: note.path,
620
+ to: destination + note.path.slice(path.length)
621
+ }));
622
+ const operation = await this.operations.prepare(moves, path, destination, folder, false);
623
+ let complete;
624
+ try {
625
+ complete = await this.operations.execute(operation);
626
+ }
627
+ catch (error) {
628
+ if (!operation.moved &&
629
+ !(await this.files.exists(destination)) &&
630
+ (!operation.temporary ||
631
+ !(await this.files.exists(operation.temporary)))) {
632
+ this.catalog.finishOperation(operation.id);
633
+ }
634
+ throw error;
635
+ }
636
+ return { operationId: operation.id, complete };
637
+ });
638
+ }
639
+ deleteId(id, storageId) {
640
+ return this.run(async () => {
641
+ this.assertStorage(storageId);
642
+ await this.synchronize();
643
+ const note = this.record(id);
644
+ if (note.deletedAt)
645
+ return;
646
+ await this.writer.acquire(true);
647
+ await this.writer.assertOwner();
648
+ if (this.catalog
649
+ .operations()
650
+ .some(operation => operation.moves.some(move => move.id === note.id) ||
651
+ operation.writes.some(write => write.id === note.id && !write.done))) {
652
+ throw new NoteConflictError();
653
+ }
654
+ // The note leaves its name, so a new note can use it. The catalog keeps the identity.
655
+ // Only a present file exists on disk and needs the move.
656
+ const path = deletedNotePath(note.path, randomUUID());
657
+ if (note.presence === 'present' && !note.redirectId) {
658
+ await this.files.moveEntry(note.path, path, false);
659
+ }
660
+ this.catalog.put({
661
+ ...note,
662
+ path,
663
+ deletedAt: new Date().toISOString()
664
+ });
665
+ this.catalog.dismiss('missing:' + note.id, true);
666
+ this.catalog.dismiss('metadata:' + note.id);
667
+ });
668
+ }
669
+ retry() {
670
+ return this.run(() => this.operations.retry());
671
+ }
672
+ resolveIdentity(issueId, restore, candidateId) {
673
+ return this.run(async () => {
674
+ await this.synchronize();
675
+ const issue = this.catalog.issues().find(item => item.id === issueId);
676
+ if (!issue || issue.kind !== 'identity' || !issue.noteId) {
677
+ throw new BadRequestError('Identity candidate is no longer available.');
678
+ }
679
+ const old = this.record(issue.noteId);
680
+ if (old.presence === 'present')
681
+ throw new NoteConflictError();
682
+ const selectedCandidate = candidateId ?? issue.candidateId;
683
+ if (!selectedCandidate) {
684
+ if (restore)
685
+ throw new BadRequestError('Select the moved file first.');
686
+ this.catalog.put({ ...old, presence: 'deleted' });
687
+ this.catalog.dismiss(issueId, true);
688
+ return;
689
+ }
690
+ const candidate = this.record(selectedCandidate);
691
+ if (candidate.presence !== 'present')
692
+ throw new NoteConflictError();
693
+ if (restore) {
694
+ await this.files.read(candidate.path);
695
+ const changes = [{ id: old.id, from: old.path, to: candidate.path }];
696
+ const previousLinks = this.catalog.links(old.id);
697
+ const candidateLinks = this.catalog.links(candidate.id).map(link => {
698
+ const previous = previousLinks.filter(item => sameReference(item.reference, link.reference));
699
+ return previous.length === 1
700
+ ? { ...previous[0], reference: link.reference }
701
+ : link;
702
+ });
703
+ const preview = this.catalog
704
+ .all()
705
+ .filter(note => note.id !== old.id && note.id !== candidate.id);
706
+ preview.push({
707
+ ...old,
708
+ path: candidate.path,
709
+ version: candidate.version,
710
+ presence: 'present'
711
+ });
712
+ const operation = await this.operations.prepare(changes, '', '', false, true, preview, {
713
+ previousId: old.id,
714
+ candidateId: candidate.id,
715
+ version: candidate.version,
716
+ links: candidateLinks
717
+ });
718
+ await this.operations.execute(operation);
719
+ }
720
+ this.catalog.dismiss(issueId, true);
721
+ });
722
+ }
723
+ resolve(sourceId, storageId, text, href, occurrence) {
724
+ return this.run(async () => {
725
+ this.assertStorage(storageId);
726
+ await this.synchronize();
727
+ const source = this.record(sourceId);
728
+ if (source.deletedAt)
729
+ throw new NotFoundError('This note was deleted.');
730
+ const paths = this.catalog
731
+ .all()
732
+ .filter(note => note.presence === 'present' && !note.redirectId && !note.deletedAt)
733
+ .map(note => note.path);
734
+ const refs = noteReferences(text)
735
+ .flatMap(ref => (ref.displayStarts ?? [ref.start]).map(displayPosition => ({
736
+ ...ref,
737
+ displayPosition
738
+ })))
739
+ .sort((a, b) => a.displayPosition - b.displayPosition)
740
+ .filter(ref => !ref.image &&
741
+ (ref.href === href ||
742
+ (() => {
743
+ const target = referenceTarget(ref, source.path, paths);
744
+ const clicked = referenceTarget({ ...ref, format: 'markdown', href }, source.path, paths);
745
+ return (!!target &&
746
+ target.path === clicked?.path &&
747
+ target.anchor === clicked.anchor);
748
+ })()));
749
+ const links = this.catalog.links(source.id);
750
+ const candidates = occurrence === undefined ? refs : refs.slice(occurrence, occurrence + 1);
751
+ const targets = new Set();
752
+ let anchor = '';
753
+ for (const ref of candidates) {
754
+ const known = links.filter(link => sameReference(link.reference, ref));
755
+ if (known.some(link => link.ambiguous) ||
756
+ new Set(known.map(link => link.targetId)).size > 1) {
757
+ throw new BadRequestError('This link is ambiguous. Choose its target explicitly.');
758
+ }
759
+ const target = referenceTarget(ref, source.path, paths);
760
+ anchor = target?.anchor ?? '';
761
+ const old = known[0];
762
+ anchor = old?.anchor ?? anchor;
763
+ if (old?.targetId) {
764
+ targets.add(this.record(old.targetId).id);
765
+ continue;
766
+ }
767
+ // A changed context with the same old address must not hijack a missing target.
768
+ if (links.some(link => link.reference.href === ref.href &&
769
+ link.targetId &&
770
+ (this.record(link.targetId).presence !== 'present' ||
771
+ this.record(link.targetId).path !== target?.path))) {
772
+ throw new BadRequestError('The previous target is missing and this occurrence cannot be identified safely.');
773
+ }
774
+ const present = this.catalog
775
+ .all()
776
+ .find(note => note.presence === 'present' &&
777
+ note.path === target?.path &&
778
+ !note.redirectId);
779
+ if (present)
780
+ targets.add(present.id);
781
+ }
782
+ if (!refs.length && href.startsWith('#')) {
783
+ targets.add(source.id);
784
+ anchor = href.slice(1);
785
+ }
786
+ if (targets.size !== 1) {
787
+ throw new BadRequestError('This link is missing or ambiguous. Edit its destination to choose a note.');
788
+ }
789
+ return { id: [...targets][0], anchor: safeDecodeURIComponent(anchor) };
790
+ });
791
+ }
792
+ graph() {
793
+ return this.run(async () => {
794
+ await this.synchronize();
795
+ const nodes = this.catalog
796
+ .all()
797
+ .filter(note => !note.redirectId && !note.deletedAt);
798
+ const edges = nodes
799
+ .filter(note => note.presence === 'present')
800
+ .flatMap(note => this.catalog.links(note.id).flatMap(link => link.targetId && !this.record(link.targetId).deletedAt
801
+ ? [
802
+ {
803
+ sourceId: note.id,
804
+ targetId: this.record(link.targetId).id
805
+ }
806
+ ]
807
+ : []));
808
+ return { storageId: this.catalog.storageId, nodes, edges };
809
+ });
810
+ }
811
+ }