@tequila99/githuman 0.6.0 → 0.8.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 (377) hide show
  1. package/README.md +55 -0
  2. package/dist/cli/config.js +13 -1
  3. package/dist/cli/server-runtime.js +3 -0
  4. package/dist/server/app.js +36 -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 +55 -0
  8. package/dist/server/errors/notes.js +3 -0
  9. package/dist/server/hooks/notes/same-origin.js +37 -0
  10. package/dist/server/hooks/terminal/local-origin.js +2 -1
  11. package/dist/server/repositories/notes-catalog.repo.js +128 -0
  12. package/dist/server/repositories/notes.repo.js +402 -0
  13. package/dist/server/routes/notes.js +167 -0
  14. package/dist/server/routes/terminal.js +2 -1
  15. package/dist/server/services/git.service.js +15 -11
  16. package/dist/server/services/notes-identity.service.js +726 -0
  17. package/dist/server/services/notes-operations.service.js +298 -0
  18. package/dist/server/services/notes-watcher.service.js +44 -0
  19. package/dist/server/services/notes-writer.service.js +116 -0
  20. package/dist/server/services/notes.service.js +125 -0
  21. package/dist/server/services/terminal/backend.js +6 -3
  22. package/dist/server/services/terminal/channel.js +6 -3
  23. package/dist/server/services/terminal/registry.js +10 -5
  24. package/dist/server/services/terminal/session.js +10 -5
  25. package/dist/server/services/terminal/tokens.js +4 -2
  26. package/dist/server/utils/hash.js +4 -0
  27. package/dist/server/utils/note-asset-format.js +19 -0
  28. package/dist/server/utils/system-error.js +14 -0
  29. package/dist/shared/events/types.js +1 -0
  30. package/dist/shared/notes/asset-formats.js +39 -0
  31. package/dist/shared/notes/constants.js +2 -0
  32. package/dist/shared/notes/document.js +118 -0
  33. package/dist/shared/notes/identity.js +24 -0
  34. package/dist/shared/notes/links.js +44 -0
  35. package/dist/shared/notes/reference-path.js +54 -0
  36. package/dist/shared/notes/references.js +190 -0
  37. package/dist/shared/notes/schemas.js +164 -0
  38. package/dist/shared/notes/types.js +1 -0
  39. package/dist/shared/utils/heading-slug.js +7 -0
  40. package/dist/shared/utils/is-uuid.js +5 -0
  41. package/dist/shared/utils/safe-decode.js +9 -0
  42. package/dist/web/assets/AppPage-cl-3VfAl.js +1 -0
  43. package/dist/web/assets/ClosePopup-D2_HzWXW.js +1 -0
  44. package/dist/web/assets/CreateButton-pmDRGwna.js +93 -0
  45. package/dist/web/assets/CreateReviewFabButton-CcXWnmSN.js +1 -0
  46. package/dist/web/assets/{ErrorNotFound-CYOZg8yN.js → ErrorNotFound-Cq0wNQMu.js} +1 -1
  47. package/dist/web/assets/IndexPage-CmBmgu4A.js +1 -0
  48. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  49. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  50. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  51. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  52. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  53. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  54. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  55. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  56. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  57. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  58. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  59. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  60. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  61. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  62. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  63. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  64. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  65. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  66. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  67. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  68. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  69. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  70. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  71. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  72. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  73. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  74. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  75. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  76. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  77. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  78. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  79. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  80. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  81. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  82. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  83. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  84. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  85. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  86. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  87. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  88. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  89. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  90. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  91. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  92. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  93. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  94. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  95. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  96. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  97. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  98. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  99. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  100. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  101. package/dist/web/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
  102. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  103. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  104. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  105. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  106. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  107. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  108. package/dist/web/assets/MainLayout-DM30ynlR.css +1 -0
  109. package/dist/web/assets/MainLayout-DOGoQ-1m.js +6 -0
  110. package/dist/web/assets/NoteVisualEditor-CClIKW0X.css +1 -0
  111. package/dist/web/assets/NoteVisualEditor-zjZr9aod.js +1056 -0
  112. package/dist/web/assets/NotesPage-C5VXA9Bb.css +1 -0
  113. package/dist/web/assets/NotesPage-Jeva3Jta.js +114 -0
  114. package/dist/web/assets/{PdfPreview-D3HwZ7pH.js → PdfPreview-CdgeqqEv.js} +1 -1
  115. package/dist/web/assets/QBtn-DoggJe1Q.js +1 -0
  116. package/dist/web/assets/QFab-DyUFgj0W.js +1 -0
  117. package/dist/web/assets/QForm-BiRkV9BY.js +4 -0
  118. package/dist/web/assets/QInput-DJgTh2Qy.js +1 -0
  119. package/dist/web/assets/QScrollArea-DfPW35JQ.js +1 -0
  120. package/dist/web/assets/QSelect-BpZ5KfWP.js +1 -0
  121. package/dist/web/assets/QSpace-BRLIXkHB.js +1 -0
  122. package/dist/web/assets/ReviewDetailPage-CpJPrSKO.js +1 -0
  123. package/dist/web/assets/ReviewsPage-BaxUZzUZ.js +1 -0
  124. package/dist/web/assets/{StatusSwitcher-DTD5inU1.css → StatusSwitcher-BCv_9MtI.css} +1 -1
  125. package/dist/web/assets/StatusSwitcher-D5UG_YWP.js +1 -0
  126. package/dist/web/assets/{TerminalView-B11UHZv3.css → TerminalView-C1jFlfLH.css} +1 -1
  127. package/dist/web/assets/TerminalView-DMJTXE-G.js +19 -0
  128. package/dist/web/assets/_baseMerge-BzZFQ6mI.js +1 -0
  129. package/dist/web/assets/{abnfDiagram-O67JEVCF-C5SYdl8k.js → abnfDiagram-O67JEVCF-CiOkMSRz.js} +1 -1
  130. package/dist/web/assets/agent-icon-Dx8WBYX_.js +1 -0
  131. package/dist/web/assets/agent-store-B1VK6S0D.js +1 -0
  132. package/dist/web/assets/apl-gfMMiTNf.js +1 -0
  133. package/dist/web/assets/app-info-store-D76h_OdJ.js +1 -0
  134. package/dist/web/assets/architecture-7GRP2DOG-Byc4Oz95.js +1 -0
  135. package/dist/web/assets/{architectureDiagram-NJMV4G6O-Bx8KGjS_.js → architectureDiagram-NJMV4G6O-BexHJffj.js} +1 -1
  136. package/dist/web/assets/asciiarmor-C_eisQCp.js +1 -0
  137. package/dist/web/assets/asn1-DCl_8MCW.js +1 -0
  138. package/dist/web/assets/asterisk-BEn0Cwpx.js +1 -0
  139. package/dist/web/assets/{blockDiagram-BEXU5L5S-B4ybhPir.js → blockDiagram-BEXU5L5S-CEyB_5a1.js} +1 -1
  140. package/dist/web/assets/brainfuck-D5EjA2JK.js +1 -0
  141. package/dist/web/assets/{c4Diagram-YGBWAQC7-D2Fzm9Zx.js → c4Diagram-YGBWAQC7-BRNWA7xI.js} +1 -1
  142. package/dist/web/assets/ccount-CcG1m-Yh.js +1 -0
  143. package/dist/web/assets/channel-DbRAFIx1.js +1 -0
  144. package/dist/web/assets/{chunk-3FUC2YCW-DLRX-HPs.js → chunk-3FUC2YCW-BcQR1JDF.js} +1 -1
  145. package/dist/web/assets/{chunk-5DYCD2WN-6HcgVbfR.js → chunk-5DYCD2WN-D6aQ1hFt.js} +1 -1
  146. package/dist/web/assets/{chunk-742MDFTN-CfTGkhlJ.js → chunk-742MDFTN-BRgrFtkf.js} +1 -1
  147. package/dist/web/assets/{chunk-7INBJB4K-COfv2qWf.js → chunk-7INBJB4K-3RBjXeq6.js} +1 -1
  148. package/dist/web/assets/{chunk-7M6MHVWA-D0FxecF4.js → chunk-7M6MHVWA-Malugc_N.js} +5 -5
  149. package/dist/web/assets/{chunk-7PRAP22T-DFjSoEy0.js → chunk-7PRAP22T-Bb3gaISA.js} +1 -1
  150. package/dist/web/assets/{chunk-GTNCS2PH-CGWGXDNO.js → chunk-GTNCS2PH-Cps7XhUC.js} +1 -1
  151. package/dist/web/assets/{chunk-GWA4HPMP-J0EaOPjD.js → chunk-GWA4HPMP-Bfp2Tg3_.js} +1 -1
  152. package/dist/web/assets/{chunk-MBY4JIJT-PzEUkWiy.js → chunk-MBY4JIJT-IfXbX1Ur.js} +1 -1
  153. package/dist/web/assets/{chunk-NETBCI7D-DHHOcLF0.js → chunk-NETBCI7D-r2fmahwk.js} +1 -1
  154. package/dist/web/assets/{chunk-O7XYJQB3-BHUId2wW.js → chunk-O7XYJQB3-sadsiWmA.js} +1 -1
  155. package/dist/web/assets/{chunk-UA2S7LBM-DXQ1DAj6.js → chunk-UA2S7LBM-DibfwtDn.js} +1 -1
  156. package/dist/web/assets/{chunk-WEXAMYUT-BMiibMha.js → chunk-WEXAMYUT-DBouMgEO.js} +1 -1
  157. package/dist/web/assets/{chunk-Z7XXMR3K-BcgKYeJS.js → chunk-Z7XXMR3K-fAHToON5.js} +1 -1
  158. package/dist/web/assets/{chunk-ZIGJFQKS-D6psU74o.js → chunk-ZIGJFQKS-CJG1UIHO.js} +1 -1
  159. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-6IwOSgSE.js → classDiagram-v2-NBCMYWYE-CPHP5ynj.js} +4 -4
  160. package/dist/web/assets/clike-D_6L3tnO.js +1 -0
  161. package/dist/web/assets/clojure-BflJGmDX.js +1 -0
  162. package/dist/web/assets/cmake-84nbDH0I.js +1 -0
  163. package/dist/web/assets/cobol-DHLQUbN7.js +1 -0
  164. package/dist/web/assets/coffeescript-De_zI4J0.js +1 -0
  165. package/dist/web/assets/commonlisp-DgAKBe0r.js +1 -0
  166. package/dist/web/assets/core-Bumj8M77.js +12 -0
  167. package/dist/web/assets/crystal-Bsdphhtm.js +1 -0
  168. package/dist/web/assets/css-CnaYoQib.js +1 -0
  169. package/dist/web/assets/{cynefin-OW5HDTMX-C77WtKve.js → cynefin-OW5HDTMX-BU7_LYGR.js} +1 -1
  170. package/dist/web/assets/{cynefinDiagram-VND7K2PF-BvIp3Y9w.js → cynefinDiagram-VND7K2PF-BXFEy9Io.js} +1 -1
  171. package/dist/web/assets/cypher-p9eYesGn.js +1 -0
  172. package/dist/web/assets/d-DWkQyNnU.js +1 -0
  173. package/dist/web/assets/dagre-6A5THRUB-aKpdpQ18.js +4 -0
  174. package/dist/web/assets/{diagram-22UHCM2B-DQUkKx9q.js → diagram-22UHCM2B-ddbRgIQ9.js} +8 -8
  175. package/dist/web/assets/{diagram-3UASUU5V-CTzVKsCf.js → diagram-3UASUU5V-vNYy6WP_.js} +1 -1
  176. package/dist/web/assets/{diagram-ATOU4E4O--Z3AVUjn.js → diagram-ATOU4E4O-B9Z3zFfp.js} +1 -1
  177. package/dist/web/assets/{diagram-CDSNMT55-BA-cRZ39.js → diagram-CDSNMT55-D3TaDPXU.js} +1 -1
  178. package/dist/web/assets/{diagram-MLGK6HIB-Bx_zLrYg.js → diagram-MLGK6HIB-DOXcXm2Y.js} +1 -1
  179. package/dist/web/assets/{diagram-MPIPVDR6-CWKuh-e-.js → diagram-MPIPVDR6-B5E2J8pO.js} +1 -1
  180. package/dist/web/assets/diff-ChtP43wD.js +1 -0
  181. package/dist/web/assets/dist-3B0OaUZ-.js +1 -0
  182. package/dist/web/assets/dist-BFbA030x.js +1 -0
  183. package/dist/web/assets/dist-BGzvKGEu.js +1 -0
  184. package/dist/web/assets/dist-BX8UA0fl.js +11 -0
  185. package/dist/web/assets/dist-BXUopzfH.js +2 -0
  186. package/dist/web/assets/dist-BY24_62d.js +1 -0
  187. package/dist/web/assets/dist-C3sBgvRy.js +1 -0
  188. package/dist/web/assets/dist-C9STut2J.js +1 -0
  189. package/dist/web/assets/dist-C9cJQvJH.js +23 -0
  190. package/dist/web/assets/dist-CRoJr3t6.js +6 -0
  191. package/dist/web/assets/dist-CqGqStPp.js +1 -0
  192. package/dist/web/assets/dist-Cxd9qKh7.js +1 -0
  193. package/dist/web/assets/dist-D2Ce45aH.js +13 -0
  194. package/dist/web/assets/dist-D7Llw4iH.js +1 -0
  195. package/dist/web/assets/dist-DSPzgfNo.js +1 -0
  196. package/dist/web/assets/dist-DUt0feNB.js +1 -0
  197. package/dist/web/assets/dist-DZQ2IsS9.js +1 -0
  198. package/dist/web/assets/dist-DpKJcdvz.js +1 -0
  199. package/dist/web/assets/dist-DuZv0LJj.js +1 -0
  200. package/dist/web/assets/dist-Kd0W7Y_b.js +9 -0
  201. package/dist/web/assets/dist-LFPPEX39.js +1 -0
  202. package/dist/web/assets/dist-PDsGXJi3.js +1 -0
  203. package/dist/web/assets/dist-lC0EKTFH.js +1 -0
  204. package/dist/web/assets/dockerfile-DJkHQgkl.js +1 -0
  205. package/dist/web/assets/dtd-DTF72YNO.js +1 -0
  206. package/dist/web/assets/dylan-BCDoL_-p.js +1 -0
  207. package/dist/web/assets/ebnf-D4c0_ac3.js +1 -0
  208. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-MyQLfQVI.js → ebnfDiagram-ZINNZB2B-D-OCB_ud.js} +1 -1
  209. package/dist/web/assets/ecl-BVIeWmwY.js +1 -0
  210. package/dist/web/assets/eiffel-BEjRio4Q.js +1 -0
  211. package/dist/web/assets/{elk-276RUBZZ-DBbqEAnd.js → elk-276RUBZZ-CzjP8RHy.js} +1 -1
  212. package/dist/web/assets/elm-Cshzl8qu.js +1 -0
  213. package/dist/web/assets/equal-arrays-GA_7ZCAb.js +1 -0
  214. package/dist/web/assets/{erDiagram-OPXOYQCR-CbRo_Rnk.js → erDiagram-OPXOYQCR-Clohps7p.js} +1 -1
  215. package/dist/web/assets/erlang-BV0c4Hdn.js +1 -0
  216. package/dist/web/assets/eventmodeling-NTZA5JFV-FWK4tnN_.js +1 -0
  217. package/dist/web/assets/factor-LKH5gN8L.js +1 -0
  218. package/dist/web/assets/fcl-ClOhbFR7.js +1 -0
  219. package/dist/web/assets/flowDiagram-KWPJA3E3-C10B5_6J.js +1 -0
  220. package/dist/web/assets/forth-D6RzAl64.js +1 -0
  221. package/dist/web/assets/fortran-Bt6PBEDR.js +1 -0
  222. package/dist/web/assets/{ganttDiagram-FUAMR5RP-D1gJBc1W.js → ganttDiagram-FUAMR5RP-ZWH4HYq5.js} +1 -1
  223. package/dist/web/assets/gas-BHEdbvp9.js +1 -0
  224. package/dist/web/assets/gherkin-oBAE_ms0.js +1 -0
  225. package/dist/web/assets/{gitGraph-4MIJSDKK-CIzz8UCs.js → gitGraph-4MIJSDKK-C3AfRnC8.js} +1 -1
  226. package/dist/web/assets/{gitGraphDiagram-X574FWY7-B5-RB5JZ.js → gitGraphDiagram-X574FWY7-BobkQARX.js} +1 -1
  227. package/dist/web/assets/graphlib-C64HifUL.js +1 -0
  228. package/dist/web/assets/groovy-CUpZ7dEl.js +1 -0
  229. package/dist/web/assets/haskell-BRsoo5mP.js +1 -0
  230. package/dist/web/assets/haxe-DjVOVvNP.js +1 -0
  231. package/dist/web/assets/http-CLVgA2GD.js +1 -0
  232. package/dist/web/assets/i18n-CC_rvfan.js +1 -0
  233. package/dist/web/assets/idl-BUZw3wgd.js +1 -0
  234. package/dist/web/assets/index-Ic8yFbJg.js +2 -0
  235. package/dist/web/assets/{info-A6RAGUB7-DF-JCGlS.js → info-A6RAGUB7-Dh_SAe09.js} +1 -1
  236. package/dist/web/assets/{infoDiagram-VRGFBTTK-z0Xepdo0.js → infoDiagram-VRGFBTTK-izDlhNTw.js} +1 -1
  237. package/dist/web/assets/{init-app-DAsY3Joz.js → init-app-C1a6BWWN.js} +1 -1
  238. package/dist/web/assets/{is-DssfAlMe.js → is-CE4jkrGQ.js} +1 -1
  239. package/dist/web/assets/isArrayLikeObject-BbaDyxId.js +1 -0
  240. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-UqXiqAoe.js → ishikawaDiagram-OU5B5YK6-BJ89XzVJ.js} +1 -1
  241. package/dist/web/assets/javascript-Bu8mWwtz.js +1 -0
  242. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-CPGeWRg-.js → journeyDiagram-ZHPQQLJL-poV-f08K.js} +1 -1
  243. package/dist/web/assets/julia-CQfgDRfP.js +1 -0
  244. package/dist/web/assets/{kanban-definition-PNTS6WVX-CMq9WuD5.js → kanban-definition-PNTS6WVX-Cn3FEnH7.js} +1 -1
  245. package/dist/web/assets/{line-cZB9xcQk.js → line-bE9Mqcqy.js} +1 -1
  246. package/dist/web/assets/livescript-BXxG0Yva.js +1 -0
  247. package/dist/web/assets/lua-DPCEkfD_.js +1 -0
  248. package/dist/web/assets/markdown-D85iiL_9.js +15 -0
  249. package/dist/web/assets/mathematica-u8YMmbU0.js +1 -0
  250. package/dist/web/assets/mbox-BEMVcqjs.js +1 -0
  251. package/dist/web/assets/{mermaid-parser.core-D8h1T47v.js → mermaid-parser.core-Cua7H5pF.js} +3 -3
  252. package/dist/web/assets/{mermaid.core-CdpJws-B.js → mermaid.core-BMPOA8T-.js} +7 -7
  253. package/dist/web/assets/{mindmap-definition-NLK3R4M7-B01KHb5Z.js → mindmap-definition-NLK3R4M7-DaTGr8Fs.js} +1 -1
  254. package/dist/web/assets/mirc-C9zpzAZU.js +1 -0
  255. package/dist/web/assets/mllike-B45xgp2S.js +1 -0
  256. package/dist/web/assets/modelica-CqHI_q-D.js +1 -0
  257. package/dist/web/assets/mscgen-qgSLujhx.js +1 -0
  258. package/dist/web/assets/mumps-BSzj6Xyz.js +1 -0
  259. package/dist/web/assets/nginx-8H7HVH4w.js +1 -0
  260. package/dist/web/assets/nsis-viPvXFiq.js +1 -0
  261. package/dist/web/assets/ntriples-B9h1VB_g.js +1 -0
  262. package/dist/web/assets/octave-D2Q8cUp1.js +1 -0
  263. package/dist/web/assets/oz-BwTct_5T.js +1 -0
  264. package/dist/web/assets/{packet-AYTQ26CC-CagxQ791.js → packet-AYTQ26CC-DmLfghd9.js} +1 -1
  265. package/dist/web/assets/pascal-Dp_KdFgX.js +1 -0
  266. package/dist/web/assets/{pegDiagram-GJSIUBJH-amhZRgGf.js → pegDiagram-GJSIUBJH-CTlxXcbf.js} +1 -1
  267. package/dist/web/assets/perl-BkSkiI9m.js +1 -0
  268. package/dist/web/assets/{pie-WAS4IAKB-zv3udP2i.js → pie-WAS4IAKB-52oNsEtc.js} +1 -1
  269. package/dist/web/assets/{pieDiagram-5QR66LMP-CjPzT-fa.js → pieDiagram-5QR66LMP-CbJtB1te.js} +1 -1
  270. package/dist/web/assets/pig-AF4Hwhju.js +1 -0
  271. package/dist/web/assets/pinia-BlCzIRX-.js +1 -0
  272. package/dist/web/assets/powershell-BcIto6Sf.js +1 -0
  273. package/dist/web/assets/properties-C357ku5U.js +1 -0
  274. package/dist/web/assets/protobuf-D1ij_kNL.js +1 -0
  275. package/dist/web/assets/pug-3K-JMW4g.js +1 -0
  276. package/dist/web/assets/puppet-BZyAS7I4.js +1 -0
  277. package/dist/web/assets/purify.es--06brOip.js +3 -0
  278. package/dist/web/assets/python-B_1Jd8HM.js +1 -0
  279. package/dist/web/assets/q-B9NhS7L_.js +1 -0
  280. package/dist/web/assets/{quadrantDiagram-O4NWA36T-7YpPBn0t.js → quadrantDiagram-O4NWA36T-DCg-uD08.js} +1 -1
  281. package/dist/web/assets/r-CxDmU1I4.js +1 -0
  282. package/dist/web/assets/{radar-RG4KPBEZ-DZ-nSYpA.js → radar-RG4KPBEZ-9uA3AOZR.js} +1 -1
  283. package/dist/web/assets/{railroad-74A4TZTK-CoylUO-u.js → railroad-74A4TZTK-BPxQfpxv.js} +1 -1
  284. package/dist/web/assets/railroad-abnf-HS5TGJTU-CPVHNg05.js +1 -0
  285. package/dist/web/assets/railroad-ebnf-LZEXJU2U-DAjTaOUg.js +1 -0
  286. package/dist/web/assets/railroad-peg-WCYAUIDC-DkxyKsj4.js +1 -0
  287. package/dist/web/assets/{railroadDiagram-XR7U4H2S-DlHZky0v.js → railroadDiagram-XR7U4H2S-CwBNr6fr.js} +1 -1
  288. package/dist/web/assets/{requirementDiagram-PLB6GJNP-Dn9whLhB.js → requirementDiagram-PLB6GJNP-BbtZt0St.js} +1 -1
  289. package/dist/web/assets/rpm-gpDK5sbt.js +1 -0
  290. package/dist/web/assets/ruby-ChfNwlCY.js +1 -0
  291. package/dist/web/assets/{safe-storage-B3bJArYP.js → safe-storage-Bn-sHAYw.js} +1 -1
  292. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-jIf4UAnN.js → sankeyDiagram-IPEJSGJF-DRWdMnFl.js} +1 -1
  293. package/dist/web/assets/sas-C2OyD2Y6.js +1 -0
  294. package/dist/web/assets/scheme-D9qYN0V5.js +1 -0
  295. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-CJahxeyg.js → sequenceDiagram-PO4LG4MO-C4tWeWpt.js} +1 -1
  296. package/dist/web/assets/shell-DwuoZtxw.js +1 -0
  297. package/dist/web/assets/sieve-tLWDfEkZ.js +1 -0
  298. package/dist/web/assets/simple-mode-DXiLM1-G.js +1 -0
  299. package/dist/web/assets/smalltalk-BLp5-jwC.js +1 -0
  300. package/dist/web/assets/solr-B78wSQJQ.js +1 -0
  301. package/dist/web/assets/sparql-Dz-mlCAC.js +1 -0
  302. package/dist/web/assets/spreadsheet-CTIncYxj.js +1 -0
  303. package/dist/web/assets/sql-DuQMFDvy.js +1 -0
  304. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-DArbI5q3.js → stateDiagram-v2-GCMORJYK-pE0T1U5E.js} +2 -2
  305. package/dist/web/assets/stex-DNzd62Q_.js +1 -0
  306. package/dist/web/assets/stylus-BOVgKnng.js +1 -0
  307. package/dist/web/assets/swift-DKB6_1j6.js +1 -0
  308. package/dist/web/assets/{swimlanes-2SLR337P-wyJPqm8c.js → swimlanes-2SLR337P-DKky6E3p.js} +1 -1
  309. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CPhibEqM.js +8 -0
  310. package/dist/web/assets/tcl-BGfruO1u.js +1 -0
  311. package/dist/web/assets/textile-C4jVoHG1.js +1 -0
  312. package/dist/web/assets/theme-B-g7wQEs.js +1 -0
  313. package/dist/web/assets/tiddlywiki-VlUiK86J.js +1 -0
  314. package/dist/web/assets/tiki-AK6FwT8q.js +1 -0
  315. package/dist/web/assets/{timeline-definition-EJHVYXUP-Bdh9ma4o.js → timeline-definition-EJHVYXUP-CsRzcMLr.js} +1 -1
  316. package/dist/web/assets/toml-BPTmHmyx.js +1 -0
  317. package/dist/web/assets/{treeView-Q6P3EWNA-CEiXTwqS.js → treeView-Q6P3EWNA-CQYqoPG3.js} +1 -1
  318. package/dist/web/assets/{treemap-WGGIJYW6-am0aIfU-.js → treemap-WGGIJYW6-BzFw4gFs.js} +1 -1
  319. package/dist/web/assets/troff-C2dAgh7P.js +1 -0
  320. package/dist/web/assets/ttcn-PPSTk1ow.js +1 -0
  321. package/dist/web/assets/ttcn-cfg-2b4Gb5sE.js +1 -0
  322. package/dist/web/assets/turtle-Cx3rYX2g.js +1 -0
  323. package/dist/web/assets/{use-app-theme-FYzHIigd.js → use-app-theme-9qFZUBqz.js} +1 -1
  324. package/dist/web/assets/use-dark-BHObfh0O.js +1 -0
  325. package/dist/web/assets/use-panel-width-Dyh49bod.js +1 -0
  326. package/dist/web/assets/use-router-link-BGc0noIO.js +1 -0
  327. package/dist/web/assets/use-server-events-24_soFuj.js +1 -0
  328. package/dist/web/assets/use-virtual-scroll-BSXYZjZ5.js +1 -0
  329. package/dist/web/assets/{usecaseDiagram-POWQR4AR-r3Qf5Bj-.js → usecaseDiagram-POWQR4AR-IMfiBw6h.js} +1 -1
  330. package/dist/web/assets/vb-BthdM_4N.js +1 -0
  331. package/dist/web/assets/vbscript-D9f6rSsU.js +1 -0
  332. package/dist/web/assets/velocity-CqftRsjg.js +1 -0
  333. package/dist/web/assets/{vennDiagram-UO4OBE2U-eOWNPvLI.js → vennDiagram-UO4OBE2U-JKdzpNy2.js} +1 -1
  334. package/dist/web/assets/verilog-CjI9Y6RI.js +1 -0
  335. package/dist/web/assets/vhdl-kS471soi.js +1 -0
  336. package/dist/web/assets/{wardley-WFR3VGLG-BdipY77V.js → wardley-WFR3VGLG-B9Yiwlp8.js} +1 -1
  337. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-sFpl0u9C.js → wardleyDiagram-VNRHLVJA-BVoc2hGS.js} +1 -1
  338. package/dist/web/assets/webidl-D5jiJvOU.js +1 -0
  339. package/dist/web/assets/xquery-BKJx4mf9.js +1 -0
  340. package/dist/web/assets/{xychartDiagram-PMCCYNJV-CtLqW--F.js → xychartDiagram-PMCCYNJV-B_XV1aim.js} +1 -1
  341. package/dist/web/assets/yacas-CXXgtw0p.js +1 -0
  342. package/dist/web/assets/z80-Co3PbHdE.js +1 -0
  343. package/dist/web/index.html +6 -3
  344. package/package.json +8 -2
  345. package/dist/web/assets/AppPage-CiRB13sb.js +0 -93
  346. package/dist/web/assets/CreateReviewFabButton-DTvX9Psc.js +0 -1
  347. package/dist/web/assets/IndexPage-hI7bcJ6L.js +0 -1
  348. package/dist/web/assets/MainLayout-DCexR9_Y.css +0 -1
  349. package/dist/web/assets/MainLayout-baVxjyeL.js +0 -11
  350. package/dist/web/assets/QBtn-CzrOO1zA.js +0 -1
  351. package/dist/web/assets/QScrollArea-Cu8zdp0y.js +0 -1
  352. package/dist/web/assets/QSelect-DQ_RRYIL.js +0 -1
  353. package/dist/web/assets/QSpace-CEuZSBS2.js +0 -13
  354. package/dist/web/assets/ReviewDetailPage-CayB2dtb.js +0 -1
  355. package/dist/web/assets/ReviewsPage-DWfEVjWO.js +0 -1
  356. package/dist/web/assets/StatusSwitcher--zNcE2-Y.js +0 -1
  357. package/dist/web/assets/TerminalView-DvVbWk3f.js +0 -19
  358. package/dist/web/assets/agent-store-DX4gIEHa.js +0 -1
  359. package/dist/web/assets/app-info-store-Vy_Pq2yR.js +0 -1
  360. package/dist/web/assets/architecture-7GRP2DOG-Dpxe25D8.js +0 -1
  361. package/dist/web/assets/channel--0tdF_Sb.js +0 -1
  362. package/dist/web/assets/core-C37KF-j0.js +0 -12
  363. package/dist/web/assets/dagre-6A5THRUB-DN7jrLfY.js +0 -4
  364. package/dist/web/assets/eventmodeling-NTZA5JFV-DMla2cPF.js +0 -1
  365. package/dist/web/assets/flowDiagram-KWPJA3E3-Bas61_mX.js +0 -1
  366. package/dist/web/assets/graphlib-DS17s2tU.js +0 -1
  367. package/dist/web/assets/i18n-B1_G449i.js +0 -1
  368. package/dist/web/assets/index-DEwYKvIG.js +0 -2
  369. package/dist/web/assets/pinia-DWj71cM9.js +0 -1
  370. package/dist/web/assets/railroad-abnf-HS5TGJTU-C20Z1OCq.js +0 -1
  371. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BltGHYDI.js +0 -1
  372. package/dist/web/assets/railroad-peg-WCYAUIDC-BT3QI-zw.js +0 -1
  373. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DgRybibH.js +0 -8
  374. package/dist/web/assets/theme-CFC7Mb_O.js +0 -1
  375. package/dist/web/assets/use-panel-width-e-yXva8i.js +0 -1
  376. package/dist/web/assets/use-server-events-znzHKZZx.js +0 -1
  377. package/dist/web/assets/use-virtual-scroll-CIwaJCUj.js +0 -1
@@ -0,0 +1,726 @@
1
+ import { lstat } from 'node:fs/promises';
2
+ import { isSystemError } from "../utils/system-error.js";
3
+ import { randomUUID } from 'node:crypto';
4
+ import { NotesCatalogRepository } from "../repositories/notes-catalog.repo.js";
5
+ import { NotesOperationsService } from "./notes-operations.service.js";
6
+ import { NotesWriter } from "./notes-writer.service.js";
7
+ import { noteIdentity, withNoteIdentity } from "../../shared/notes/identity.js";
8
+ import { noteMetadata } from "../../shared/notes/document.js";
9
+ import { noteReferences, referenceTarget, sameReference } from "../../shared/notes/references.js";
10
+ import { BadRequestError, NotFoundError } from "../errors/http.js";
11
+ import { NoteConflictError, NoteExistsError } from "../errors/notes.js";
12
+ import { sha256Hex } from "../utils/hash.js";
13
+ import { safeDecodeURIComponent } from "../../shared/utils/safe-decode.js";
14
+ // A second complete scan confirms absence, instead of treating an atomic save as deletion.
15
+ const ABSENCE_GRACE_MS = 2500;
16
+ function newRecord(id, path, version) {
17
+ const now = new Date().toISOString();
18
+ return {
19
+ id,
20
+ path,
21
+ version,
22
+ presence: 'present',
23
+ createdAt: now,
24
+ updatedAt: now,
25
+ redirectId: null
26
+ };
27
+ }
28
+ function portablePath(path) {
29
+ for (const part of path.split('/')) {
30
+ if (/[<>:"\\|?*]/.test(part) ||
31
+ part.split('').some(char => char.charCodeAt(0) < 32) ||
32
+ /[. ]$/.test(part) ||
33
+ /^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/i.test(part)) {
34
+ throw new BadRequestError('The destination name is not portable to Windows.');
35
+ }
36
+ }
37
+ }
38
+ export class NotesIdentityService {
39
+ catalog;
40
+ writer;
41
+ operations;
42
+ queue = Promise.resolve();
43
+ cache = new Map();
44
+ absentSince = new Map();
45
+ // Signature of each file that failed to read, so a poll does not read it again.
46
+ unreadable = new Map();
47
+ closed = false;
48
+ files;
49
+ constructor(files, db) {
50
+ this.files = files;
51
+ this.catalog = new NotesCatalogRepository(db, files.root);
52
+ this.writer = new NotesWriter(files.root);
53
+ this.operations = new NotesOperationsService({
54
+ files,
55
+ catalog: this.catalog,
56
+ writer: this.writer,
57
+ record: id => this.record(id),
58
+ changed: (id, note) => {
59
+ this.catalog.put({
60
+ ...this.record(id),
61
+ version: note.version,
62
+ updatedAt: new Date().toISOString()
63
+ });
64
+ this.indexLinks(id, note.text);
65
+ this.cache.set(note.path, {
66
+ signature: '',
67
+ note: { version: note.version },
68
+ identity: noteIdentity(note.text),
69
+ metadata: noteMetadata(note.text)
70
+ });
71
+ }
72
+ });
73
+ }
74
+ run(action) {
75
+ const next = this.queue
76
+ .catch(() => { })
77
+ .then(async () => {
78
+ if (this.closed)
79
+ throw new BadRequestError('Notes storage is closing.');
80
+ return action();
81
+ });
82
+ this.queue = next;
83
+ return next;
84
+ }
85
+ async close() {
86
+ this.closed = true;
87
+ await this.queue.catch(() => { });
88
+ await this.writer.close();
89
+ }
90
+ record(id) {
91
+ let note = this.catalog.get(id);
92
+ const seen = new Set();
93
+ while (note?.redirectId && !seen.has(note.id)) {
94
+ seen.add(note.id);
95
+ note = this.catalog.get(note.redirectId);
96
+ }
97
+ if (!note)
98
+ throw new NotFoundError('Unknown note identity.');
99
+ return note;
100
+ }
101
+ issue(id, message, noteId, candidateId = null, kind = 'identity') {
102
+ this.catalog.issue({ id, message, noteId, candidateId, kind });
103
+ }
104
+ async synchronize() {
105
+ await this.files.path('probe.md');
106
+ try {
107
+ await lstat(this.files.root);
108
+ }
109
+ catch (error) {
110
+ if (isSystemError(error, 'ENOENT') && !this.catalog.all().length) {
111
+ return [];
112
+ }
113
+ throw new BadRequestError('Notes storage is unavailable. Existing identities are retained.');
114
+ }
115
+ await this.writer.acquire();
116
+ if (this.catalog.operations().length)
117
+ await this.operations.recover();
118
+ const entries = await this.files.list();
119
+ const documents = [];
120
+ // Read the whole snapshot successfully before changing any presence records.
121
+ for (const entry of entries) {
122
+ if (entry.folder)
123
+ continue;
124
+ let references;
125
+ let cached = this.cache.get(entry.path);
126
+ if (!cached || cached.signature !== entry.signature) {
127
+ if (this.unreadable.get(entry.path) === entry.signature)
128
+ continue;
129
+ let note;
130
+ try {
131
+ note = await this.files.read(entry.path);
132
+ }
133
+ catch (error) {
134
+ // One bad file must not stop the synchronization of other notes.
135
+ if (!(error instanceof BadRequestError))
136
+ throw error;
137
+ this.unreadable.set(entry.path, entry.signature);
138
+ this.issue('unreadable:' + entry.path, 'Skipped ' + entry.path + ': ' + error.message, null, null, 'metadata');
139
+ continue;
140
+ }
141
+ this.unreadable.delete(entry.path);
142
+ this.catalog.dismiss('unreadable:' + entry.path);
143
+ cached = {
144
+ signature: entry.signature,
145
+ note: { version: note.version },
146
+ identity: noteIdentity(note.text),
147
+ metadata: noteMetadata(note.text)
148
+ };
149
+ references = noteReferences(note.text);
150
+ this.cache.set(entry.path, cached);
151
+ }
152
+ documents.push({
153
+ path: entry.path,
154
+ note: cached.note,
155
+ yamlId: cached.identity.id,
156
+ identityWarning: cached.identity.warning,
157
+ ...(references ? { references } : {})
158
+ });
159
+ }
160
+ const entryPaths = new Set(entries.map(entry => entry.path));
161
+ for (const path of this.unreadable.keys()) {
162
+ if (entryPaths.has(path))
163
+ continue;
164
+ this.unreadable.delete(path);
165
+ this.catalog.dismiss('unreadable:' + path);
166
+ }
167
+ const records = this.catalog.all();
168
+ const byId = new Map(records.map(record => [record.id, record]));
169
+ const presentByPath = new Map(records
170
+ .filter(record => record.presence === 'present' && !record.redirectId)
171
+ .map(record => [record.path, record]));
172
+ const livePaths = new Set(documents.map(doc => doc.path));
173
+ const counts = new Map();
174
+ for (const doc of documents) {
175
+ if (doc.yamlId)
176
+ counts.set(doc.yamlId, (counts.get(doc.yamlId) ?? 0) + 1);
177
+ }
178
+ const changes = [];
179
+ const changed = new Set();
180
+ this.catalog.transaction(() => {
181
+ // Release paths first, so swaps and folder moves do not violate uniqueness.
182
+ for (const record of records) {
183
+ if (record.presence !== 'present' || livePaths.has(record.path)) {
184
+ continue;
185
+ }
186
+ if (documents.some(doc => doc.yamlId === record.id && counts.get(record.id) === 1)) {
187
+ this.catalog.put({ ...record, presence: 'missing' });
188
+ continue;
189
+ }
190
+ const since = this.absentSince.get(record.id);
191
+ if (since === undefined) {
192
+ this.catalog.reopen('missing:' + record.id);
193
+ this.absentSince.set(record.id, Date.now());
194
+ }
195
+ else if (Date.now() - since >= ABSENCE_GRACE_MS) {
196
+ this.catalog.put({ ...record, presence: 'missing' });
197
+ }
198
+ }
199
+ for (const doc of documents) {
200
+ const byPath = presentByPath.get(doc.path);
201
+ const hinted = doc.yamlId && counts.get(doc.yamlId) === 1
202
+ ? byId.get(doc.yamlId)
203
+ : undefined;
204
+ let record = byPath;
205
+ if (hinted && hinted !== byPath && !livePaths.has(hinted.path)) {
206
+ if (byPath)
207
+ this.catalog.put({ ...byPath, presence: 'missing' });
208
+ record = hinted;
209
+ }
210
+ else if (!record && hinted && hinted.path === doc.path) {
211
+ record = hinted;
212
+ }
213
+ if (!record) {
214
+ const id = doc.yamlId &&
215
+ counts.get(doc.yamlId) === 1 &&
216
+ !this.catalog.get(doc.yamlId)
217
+ ? doc.yamlId
218
+ : randomUUID();
219
+ record = newRecord(id, doc.path, doc.note.version);
220
+ this.catalog.put(record);
221
+ changed.add(record.id);
222
+ }
223
+ if (record.path !== doc.path) {
224
+ changes.push({ id: record.id, from: record.path, to: doc.path });
225
+ }
226
+ if (record.version !== doc.note.version)
227
+ changed.add(record.id);
228
+ if (record.path !== doc.path ||
229
+ record.presence !== 'present' ||
230
+ record.version !== doc.note.version) {
231
+ this.catalog.put({
232
+ ...record,
233
+ path: doc.path,
234
+ presence: 'present',
235
+ version: doc.note.version,
236
+ updatedAt: record.version === doc.note.version
237
+ ? record.updatedAt
238
+ : new Date().toISOString()
239
+ });
240
+ }
241
+ this.absentSince.delete(record.id);
242
+ this.catalog.dismiss('missing:' + record.id);
243
+ const identity = { id: doc.yamlId, warning: doc.identityWarning };
244
+ const warning = identity.warning ??
245
+ (identity.id && identity.id !== record.id
246
+ ? 'The YAML ID conflicts with this note or is duplicated. The catalog identity is retained.'
247
+ : null);
248
+ if (warning) {
249
+ this.issue('metadata:' + record.id, warning, record.id, null, 'metadata');
250
+ }
251
+ else {
252
+ this.catalog.dismiss('metadata:' + record.id);
253
+ }
254
+ }
255
+ });
256
+ if (changes.length) {
257
+ const operation = await this.operations.prepare(changes, '', '', false, true);
258
+ await this.operations.execute(operation);
259
+ }
260
+ const current = this.catalog.all();
261
+ const documentsByPath = new Map(documents.map(doc => [doc.path, doc]));
262
+ const currentByPath = new Map(current
263
+ .filter(note => note.presence === 'present' && !note.redirectId)
264
+ .map(note => [note.path, note]));
265
+ const byVersion = new Map();
266
+ for (const candidate of current.filter(note => note.presence === 'present' && !note.redirectId)) {
267
+ const group = byVersion.get(candidate.version) ?? [];
268
+ group.push(candidate);
269
+ byVersion.set(candidate.version, group);
270
+ }
271
+ for (const old of current.filter(note => note.presence === 'missing' && !note.redirectId)) {
272
+ const samePath = currentByPath.get(old.path);
273
+ const matches = (byVersion.get(old.version) ?? []).filter(candidate => !documentsByPath.get(candidate.path)?.yamlId);
274
+ const candidate = samePath ?? (matches.length === 1 ? matches[0] : undefined);
275
+ this.issue('missing:' + old.id, 'File missing: ' +
276
+ old.path +
277
+ '. Choose a moved file, or confirm deletion.', old.id, candidate?.id ?? null);
278
+ }
279
+ for (const doc of documents) {
280
+ const record = currentByPath.get(doc.path);
281
+ if (!record || !changed.has(record.id))
282
+ continue;
283
+ const references = changes.length
284
+ ? noteReferences((await this.files.read(doc.path)).text)
285
+ : (doc.references ?? []);
286
+ this.indexReferences(record.id, references, current);
287
+ }
288
+ for (const path of this.cache.keys()) {
289
+ if (!livePaths.has(path))
290
+ this.cache.delete(path);
291
+ }
292
+ // Unreadable files stay visible through their issue, not as openable entries.
293
+ return entries.filter(entry => this.unreadable.get(entry.path) !== entry.signature);
294
+ }
295
+ indexLinks(id, text, snapshot) {
296
+ this.indexReferences(id, noteReferences(text), snapshot);
297
+ }
298
+ indexReferences(id, refs, snapshot) {
299
+ const record = this.record(id);
300
+ const oldLinks = this.catalog.links(id);
301
+ if (!refs.length) {
302
+ if (oldLinks.length)
303
+ this.catalog.setLinks(id, []);
304
+ return;
305
+ }
306
+ const records = (snapshot ?? this.catalog.all()).filter(note => !note.redirectId);
307
+ const paths = records
308
+ .filter(note => note.presence === 'present')
309
+ .map(note => note.path);
310
+ const links = refs
311
+ .filter(ref => !ref.image)
312
+ .map(ref => {
313
+ const target = referenceTarget(ref, record.path, paths);
314
+ const old = oldLinks.filter(link => sameReference(link.reference, ref));
315
+ const duplicates = refs.filter(other => sameReference(ref, other)).length;
316
+ if (old.length === duplicates &&
317
+ old[0]?.targetId &&
318
+ !old.some(link => link.ambiguous) &&
319
+ new Set(old.map(link => link.targetId)).size === 1) {
320
+ return { ...old[0], reference: ref };
321
+ }
322
+ const changedOccurrence = old.length === 0 &&
323
+ oldLinks.some(link => link.reference.href === ref.href &&
324
+ link.targetId &&
325
+ (this.record(link.targetId).presence !== 'present' ||
326
+ this.record(link.targetId).path !== target?.path));
327
+ const ambiguous = changedOccurrence ||
328
+ (old.length > 0 &&
329
+ (old.length !== duplicates ||
330
+ new Set(old.map(link => link.targetId)).size > 1));
331
+ if (ambiguous) {
332
+ return {
333
+ reference: ref,
334
+ targetId: null,
335
+ targetPath: target?.path ?? null,
336
+ anchor: target?.anchor ?? '',
337
+ ambiguous: true
338
+ };
339
+ }
340
+ const present = target
341
+ ? records.find(note => note.presence === 'present' && note.path === target.path)
342
+ : undefined;
343
+ const historical = target && !present
344
+ ? [
345
+ ...new Set([
346
+ ...this.catalog.historical(target.path),
347
+ ...records
348
+ .filter(note => note.path === target.path && note.presence !== 'present')
349
+ .map(note => note.id)
350
+ ])
351
+ ]
352
+ : [];
353
+ return {
354
+ reference: ref,
355
+ targetId: present?.id ?? (historical.length === 1 ? historical[0] : null),
356
+ targetPath: target?.path ?? null,
357
+ anchor: target?.anchor ?? '',
358
+ ambiguous: historical.length > 1
359
+ };
360
+ })
361
+ .filter(link => link.targetPath?.toLowerCase().endsWith('.md') ||
362
+ link.reference.format === 'wiki');
363
+ this.catalog.setLinks(id, links);
364
+ }
365
+ revision(entries) {
366
+ return sha256Hex(JSON.stringify([
367
+ entries,
368
+ this.catalog
369
+ .all()
370
+ .map(note => [note.id, note.path, note.version, note.presence])
371
+ ]));
372
+ }
373
+ index() {
374
+ return this.run(async () => {
375
+ const entries = await this.synchronize();
376
+ const records = this.catalog.all();
377
+ const recordsByPath = new Map(records
378
+ .filter(note => note.presence === 'present' && !note.redirectId)
379
+ .map(note => [note.path, note]));
380
+ return {
381
+ storage: this.files.root,
382
+ storageId: this.catalog.storageId,
383
+ revision: this.revision(entries),
384
+ issues: this.catalog.issues(),
385
+ entries: entries.map(entry => {
386
+ const record = recordsByPath.get(entry.path);
387
+ const metadata = entry.folder
388
+ ? { tags: [], error: null }
389
+ : (this.cache.get(entry.path)?.metadata ?? {
390
+ tags: [],
391
+ error: null
392
+ });
393
+ return {
394
+ path: entry.path,
395
+ folder: entry.folder,
396
+ ...metadata,
397
+ ...(record ? { id: record.id, presence: record.presence } : {})
398
+ };
399
+ })
400
+ };
401
+ });
402
+ }
403
+ descriptor(id, storageId) {
404
+ return this.run(async () => {
405
+ this.assertStorage(storageId);
406
+ await this.synchronize();
407
+ const registered = this.record(id);
408
+ const note = registered.presence === 'present' &&
409
+ !(await this.files.exists(registered.path))
410
+ ? { ...registered, presence: 'missing' }
411
+ : registered;
412
+ const backlinks = this.catalog
413
+ .all()
414
+ .filter(source => source.presence === 'present' &&
415
+ this.catalog
416
+ .links(source.id)
417
+ .some(link => !!link.targetId && this.record(link.targetId).id === note.id));
418
+ return { note, backlinks };
419
+ });
420
+ }
421
+ resolvePath(path) {
422
+ return this.run(async () => {
423
+ await this.synchronize();
424
+ const records = this.catalog.all().filter(note => !note.redirectId);
425
+ const candidates = new Set([
426
+ ...records.filter(note => note.path === path).map(note => note.id),
427
+ ...this.catalog.historical(path)
428
+ ]);
429
+ if (!candidates.size) {
430
+ throw new NotFoundError('The note path was not found.');
431
+ }
432
+ if (candidates.size !== 1) {
433
+ throw new BadRequestError('This path belongs to more than one note over time. Open a stable UUID link instead.');
434
+ }
435
+ return this.record([...candidates][0]);
436
+ });
437
+ }
438
+ assertStorage(id) {
439
+ if (id !== this.catalog.storageId) {
440
+ throw new BadRequestError('This link belongs to a different notes storage.');
441
+ }
442
+ }
443
+ readId(id, storageId) {
444
+ return this.run(async () => {
445
+ this.assertStorage(storageId);
446
+ await this.synchronize();
447
+ const record = this.record(id);
448
+ if (record.presence !== 'present') {
449
+ throw new NotFoundError('The note file is missing or deleted.');
450
+ }
451
+ return {
452
+ ...(await this.files.read(record.path)),
453
+ id: record.id,
454
+ storageId
455
+ };
456
+ });
457
+ }
458
+ readPath(path) {
459
+ return this.run(async () => {
460
+ await this.synchronize();
461
+ const record = this.catalog
462
+ .all()
463
+ .find(note => note.path === path &&
464
+ note.presence === 'present' &&
465
+ !note.redirectId);
466
+ if (!record)
467
+ throw new NotFoundError('Note not found.');
468
+ return {
469
+ ...(await this.files.read(path)),
470
+ id: record.id,
471
+ storageId: this.catalog.storageId
472
+ };
473
+ });
474
+ }
475
+ save(path, text, version, id) {
476
+ return this.run(async () => {
477
+ const metadata = noteMetadata(text);
478
+ if (metadata.error)
479
+ throw new BadRequestError(metadata.error);
480
+ await this.files.path(path);
481
+ await this.writer.acquire(true);
482
+ await this.synchronize();
483
+ if (id)
484
+ path = this.record(id).path;
485
+ let record = this.catalog
486
+ .all()
487
+ .find(note => note.path === path &&
488
+ note.presence === 'present' &&
489
+ !note.redirectId);
490
+ if (version === undefined) {
491
+ portablePath(path);
492
+ if (record)
493
+ throw new NoteExistsError();
494
+ record = newRecord(randomUUID(), path, '');
495
+ text = withNoteIdentity(text, record.id);
496
+ }
497
+ if (!record)
498
+ throw new NotFoundError('Note identity not found.');
499
+ await this.writer.assertOwner();
500
+ const disk = await this.files.save(path, text, version);
501
+ this.catalog.put({
502
+ ...record,
503
+ version: disk.version,
504
+ updatedAt: new Date().toISOString()
505
+ });
506
+ this.indexLinks(record.id, disk.text);
507
+ return { ...disk, id: record.id, storageId: this.catalog.storageId };
508
+ });
509
+ }
510
+ saveId(id, storageId, text, version) {
511
+ this.assertStorage(storageId);
512
+ return this.save(this.record(id).path, text, version, id);
513
+ }
514
+ folder(path) {
515
+ return this.run(async () => {
516
+ portablePath(path);
517
+ await this.files.path(path, false);
518
+ await this.writer.acquire(true);
519
+ await this.writer.assertOwner();
520
+ await this.files.folder(path);
521
+ });
522
+ }
523
+ rename(path, name, folder, target, id, expectedRevision) {
524
+ return this.run(async () => {
525
+ const entries = await this.synchronize();
526
+ if (expectedRevision && expectedRevision !== this.revision(entries)) {
527
+ throw new NoteConflictError();
528
+ }
529
+ if (id && this.record(id).path !== path)
530
+ throw new NoteConflictError();
531
+ if (!target &&
532
+ (name !== name.trim() ||
533
+ name.includes('/') ||
534
+ name.includes('\\') ||
535
+ name.startsWith('.'))) {
536
+ throw new BadRequestError('Invalid entry name.');
537
+ }
538
+ const sourcePath = await this.files.path(path, !folder);
539
+ if (path.split('/')[0] === 'assets') {
540
+ throw new BadRequestError('Cannot rename attachments.');
541
+ }
542
+ const sourceStat = await lstat(sourcePath);
543
+ if (folder ? !sourceStat.isDirectory() : !sourceStat.isFile()) {
544
+ throw new BadRequestError('Entry type changed.');
545
+ }
546
+ const destination = target ?? [...path.split('/').slice(0, -1), name].join('/');
547
+ portablePath(destination);
548
+ await this.files.path(destination, !folder);
549
+ if (destination === path) {
550
+ return { operationId: randomUUID(), complete: true };
551
+ }
552
+ if (await this.files.exists(destination)) {
553
+ const caseOnly = process.platform === 'win32' &&
554
+ path.toLowerCase() === destination.toLowerCase() &&
555
+ (await this.files.sameEntry(path, destination));
556
+ if (!caseOnly)
557
+ throw new NoteExistsError();
558
+ }
559
+ if (folder && destination.startsWith(path + '/')) {
560
+ throw new BadRequestError('Cannot move a directory into itself.');
561
+ }
562
+ const moves = this.catalog
563
+ .all()
564
+ .filter(note => note.presence === 'present' &&
565
+ (note.path === path || (folder && note.path.startsWith(path + '/'))))
566
+ .map(note => ({
567
+ id: note.id,
568
+ from: note.path,
569
+ to: destination + note.path.slice(path.length)
570
+ }));
571
+ const operation = await this.operations.prepare(moves, path, destination, folder, false);
572
+ let complete;
573
+ try {
574
+ complete = await this.operations.execute(operation);
575
+ }
576
+ catch (error) {
577
+ if (!operation.moved &&
578
+ !(await this.files.exists(destination)) &&
579
+ (!operation.temporary ||
580
+ !(await this.files.exists(operation.temporary)))) {
581
+ this.catalog.finishOperation(operation.id);
582
+ }
583
+ throw error;
584
+ }
585
+ return { operationId: operation.id, complete };
586
+ });
587
+ }
588
+ retry() {
589
+ return this.run(() => this.operations.retry());
590
+ }
591
+ resolveIdentity(issueId, restore, candidateId) {
592
+ return this.run(async () => {
593
+ await this.synchronize();
594
+ const issue = this.catalog.issues().find(item => item.id === issueId);
595
+ if (!issue || issue.kind !== 'identity' || !issue.noteId) {
596
+ throw new BadRequestError('Identity candidate is no longer available.');
597
+ }
598
+ const old = this.record(issue.noteId);
599
+ if (old.presence === 'present')
600
+ throw new NoteConflictError();
601
+ const selectedCandidate = candidateId ?? issue.candidateId;
602
+ if (!selectedCandidate) {
603
+ if (restore)
604
+ throw new BadRequestError('Select the moved file first.');
605
+ this.catalog.put({ ...old, presence: 'deleted' });
606
+ this.catalog.dismiss(issueId, true);
607
+ return;
608
+ }
609
+ const candidate = this.record(selectedCandidate);
610
+ if (candidate.presence !== 'present')
611
+ throw new NoteConflictError();
612
+ if (restore) {
613
+ await this.files.read(candidate.path);
614
+ const changes = [{ id: old.id, from: old.path, to: candidate.path }];
615
+ const previousLinks = this.catalog.links(old.id);
616
+ const candidateLinks = this.catalog.links(candidate.id).map(link => {
617
+ const previous = previousLinks.filter(item => sameReference(item.reference, link.reference));
618
+ return previous.length === 1
619
+ ? { ...previous[0], reference: link.reference }
620
+ : link;
621
+ });
622
+ const preview = this.catalog
623
+ .all()
624
+ .filter(note => note.id !== old.id && note.id !== candidate.id);
625
+ preview.push({
626
+ ...old,
627
+ path: candidate.path,
628
+ version: candidate.version,
629
+ presence: 'present'
630
+ });
631
+ const operation = await this.operations.prepare(changes, '', '', false, true, preview, {
632
+ previousId: old.id,
633
+ candidateId: candidate.id,
634
+ version: candidate.version,
635
+ links: candidateLinks
636
+ });
637
+ await this.operations.execute(operation);
638
+ }
639
+ this.catalog.dismiss(issueId, true);
640
+ });
641
+ }
642
+ resolve(sourceId, storageId, text, href, occurrence) {
643
+ return this.run(async () => {
644
+ this.assertStorage(storageId);
645
+ await this.synchronize();
646
+ const source = this.record(sourceId);
647
+ const paths = this.catalog
648
+ .all()
649
+ .filter(note => note.presence === 'present' && !note.redirectId)
650
+ .map(note => note.path);
651
+ const refs = noteReferences(text)
652
+ .flatMap(ref => (ref.displayStarts ?? [ref.start]).map(displayPosition => ({
653
+ ...ref,
654
+ displayPosition
655
+ })))
656
+ .sort((a, b) => a.displayPosition - b.displayPosition)
657
+ .filter(ref => !ref.image &&
658
+ (ref.href === href ||
659
+ (() => {
660
+ const target = referenceTarget(ref, source.path, paths);
661
+ const clicked = referenceTarget({ ...ref, format: 'markdown', href }, source.path, paths);
662
+ return (!!target &&
663
+ target.path === clicked?.path &&
664
+ target.anchor === clicked.anchor);
665
+ })()));
666
+ const links = this.catalog.links(source.id);
667
+ const candidates = occurrence === undefined ? refs : refs.slice(occurrence, occurrence + 1);
668
+ const targets = new Set();
669
+ let anchor = '';
670
+ for (const ref of candidates) {
671
+ const known = links.filter(link => sameReference(link.reference, ref));
672
+ if (known.some(link => link.ambiguous) ||
673
+ new Set(known.map(link => link.targetId)).size > 1) {
674
+ throw new BadRequestError('This link is ambiguous. Choose its target explicitly.');
675
+ }
676
+ const target = referenceTarget(ref, source.path, paths);
677
+ anchor = target?.anchor ?? '';
678
+ const old = known[0];
679
+ anchor = old?.anchor ?? anchor;
680
+ if (old?.targetId) {
681
+ targets.add(this.record(old.targetId).id);
682
+ continue;
683
+ }
684
+ // A changed context with the same old address must not hijack a missing target.
685
+ if (links.some(link => link.reference.href === ref.href &&
686
+ link.targetId &&
687
+ (this.record(link.targetId).presence !== 'present' ||
688
+ this.record(link.targetId).path !== target?.path))) {
689
+ throw new BadRequestError('The previous target is missing and this occurrence cannot be identified safely.');
690
+ }
691
+ const present = this.catalog
692
+ .all()
693
+ .find(note => note.presence === 'present' &&
694
+ note.path === target?.path &&
695
+ !note.redirectId);
696
+ if (present)
697
+ targets.add(present.id);
698
+ }
699
+ if (!refs.length && href.startsWith('#')) {
700
+ targets.add(source.id);
701
+ anchor = href.slice(1);
702
+ }
703
+ if (targets.size !== 1) {
704
+ throw new BadRequestError('This link is missing or ambiguous. Edit its destination to choose a note.');
705
+ }
706
+ return { id: [...targets][0], anchor: safeDecodeURIComponent(anchor) };
707
+ });
708
+ }
709
+ graph() {
710
+ return this.run(async () => {
711
+ await this.synchronize();
712
+ const nodes = this.catalog.all().filter(note => !note.redirectId);
713
+ const edges = nodes
714
+ .filter(note => note.presence === 'present')
715
+ .flatMap(note => this.catalog.links(note.id).flatMap(link => link.targetId
716
+ ? [
717
+ {
718
+ sourceId: note.id,
719
+ targetId: this.record(link.targetId).id
720
+ }
721
+ ]
722
+ : []));
723
+ return { storageId: this.catalog.storageId, nodes, edges };
724
+ });
725
+ }
726
+ }