@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,402 @@
1
+ import { constants } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { lstat, link, mkdir, open, readdir, realpath, rename, unlink, writeFile } from 'node:fs/promises';
4
+ import { basename, dirname, join, resolve, sep } from 'node:path';
5
+ import { BadRequestError, NotFoundError } from "../errors/http.js";
6
+ import { isSystemError } from "../utils/system-error.js";
7
+ import { sha256Hex } from "../utils/hash.js";
8
+ import { NoteConflictError, NoteExistsError } from "../errors/notes.js";
9
+ import { MAX_NOTE_ASSET_BYTES } from "../../shared/notes/constants.js";
10
+ // Bound file reads before allocating document buffers.
11
+ const MAX_BYTES = 1000000;
12
+ export class NotesRepository {
13
+ root;
14
+ writes = new Map();
15
+ constructor(root) {
16
+ this.root = resolve(root);
17
+ }
18
+ async path(path, markdown = true) {
19
+ if (!path ||
20
+ path.includes('\\') ||
21
+ path.includes('\0') ||
22
+ path.startsWith('/') ||
23
+ path
24
+ .split('/')
25
+ .some(part => !part || part === '.' || part === '..' || part.startsWith('.')) ||
26
+ (markdown && !path.toLowerCase().endsWith('.md'))) {
27
+ throw new BadRequestError('Invalid note path');
28
+ }
29
+ const target = resolve(this.root, path);
30
+ if (!target.startsWith(this.root + sep)) {
31
+ throw new BadRequestError('Path leaves the notes folder');
32
+ }
33
+ // Reject links inside the notes folder. The configured root and its parents may be links.
34
+ let current = this.root;
35
+ for (const part of target
36
+ .slice(this.root.length)
37
+ .split(sep)
38
+ .filter(Boolean)) {
39
+ current = join(current, part);
40
+ try {
41
+ if ((await lstat(current)).isSymbolicLink()) {
42
+ throw new BadRequestError('Symbolic links are not supported in the notes path');
43
+ }
44
+ }
45
+ catch (error) {
46
+ if (!isSystemError(error, 'ENOENT'))
47
+ throw error;
48
+ }
49
+ }
50
+ return target;
51
+ }
52
+ async read(path) {
53
+ const target = await this.path(path);
54
+ try {
55
+ const file = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
56
+ try {
57
+ const stat = await file.stat();
58
+ if (!stat.isFile() || stat.size > MAX_BYTES) {
59
+ throw new BadRequestError('Note must be a Markdown file under 1 MB');
60
+ }
61
+ const bytes = await file.readFile();
62
+ if (bytes.byteLength > MAX_BYTES) {
63
+ throw new BadRequestError('Note exceeds 1 MB');
64
+ }
65
+ let text;
66
+ try {
67
+ text = new TextDecoder('utf-8', {
68
+ fatal: true,
69
+ ignoreBOM: true
70
+ }).decode(bytes);
71
+ }
72
+ catch {
73
+ throw new BadRequestError('Note is not valid UTF-8');
74
+ }
75
+ return { path, text, version: sha256Hex(text) };
76
+ }
77
+ finally {
78
+ await file.close();
79
+ }
80
+ }
81
+ catch (error) {
82
+ if (isSystemError(error, 'ENOENT')) {
83
+ throw new NotFoundError('Note not found');
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+ async list() {
89
+ const result = [];
90
+ await this.path('probe.md');
91
+ const walk = async (dir, prefix) => {
92
+ const entries = (await readdir(dir, { withFileTypes: true })).filter(entry => !entry.name.startsWith('.') &&
93
+ !entry.isSymbolicLink() &&
94
+ entry.name !== 'node_modules' &&
95
+ (prefix || entry.name !== 'assets'));
96
+ for (const entry of entries.filter(item => item.isDirectory())) {
97
+ const path = prefix + entry.name;
98
+ await this.path(path, false);
99
+ result.push({ path, folder: true, signature: 'folder' });
100
+ await walk(join(dir, entry.name), path + '/');
101
+ }
102
+ const files = entries.filter(entry => entry.isFile() && entry.name.toLowerCase().endsWith('.md'));
103
+ // Bound concurrent metadata reads while retaining path checks for each file.
104
+ for (let offset = 0; offset < files.length; offset += 16) {
105
+ const batch = await Promise.allSettled(files.slice(offset, offset + 16).map(async (entry) => {
106
+ const path = prefix + entry.name;
107
+ const target = await this.path(path);
108
+ const stat = await lstat(target, { bigint: true });
109
+ return {
110
+ path,
111
+ folder: false,
112
+ signature: `${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`
113
+ };
114
+ }));
115
+ for (const item of batch) {
116
+ if (item.status === 'rejected')
117
+ throw item.reason;
118
+ result.push(item.value);
119
+ }
120
+ }
121
+ };
122
+ await walk(this.root, '');
123
+ return result.sort((a, b) => a.path.localeCompare(b.path));
124
+ }
125
+ async serial(path, run) {
126
+ const prior = this.writes.get(path) ?? Promise.resolve();
127
+ const next = prior.catch(() => { }).then(run);
128
+ this.writes.set(path, next);
129
+ try {
130
+ return await next;
131
+ }
132
+ finally {
133
+ if (this.writes.get(path) === next)
134
+ this.writes.delete(path);
135
+ }
136
+ }
137
+ async save(path, text, expected) {
138
+ return this.serial(path, async () => {
139
+ if (Buffer.byteLength(text) > MAX_BYTES) {
140
+ throw new BadRequestError('Note exceeds 1 MB');
141
+ }
142
+ const target = await this.path(path);
143
+ await mkdir(dirname(target), { recursive: true });
144
+ await this.path(path);
145
+ let current;
146
+ if (expected !== undefined) {
147
+ try {
148
+ current = await this.read(path);
149
+ }
150
+ catch (error) {
151
+ if (error instanceof NotFoundError)
152
+ throw new NoteConflictError();
153
+ throw error;
154
+ }
155
+ if (current.version !== expected)
156
+ throw new NoteConflictError();
157
+ if (current.text === text)
158
+ return current;
159
+ // Retain one previous version per note, without accumulating autosave history.
160
+ const backups = join(this.root, '.history');
161
+ await mkdir(backups, { recursive: true });
162
+ if ((await lstat(backups)).isSymbolicLink() ||
163
+ !(await realpath(backups)).startsWith((await realpath(this.root)) + sep)) {
164
+ throw new BadRequestError('Invalid backup folder');
165
+ }
166
+ const backupTemp = join(backups, '.backup-' + randomUUID());
167
+ try {
168
+ await writeFile(backupTemp, current.text, { flag: 'wx', mode: 0o600 });
169
+ await rename(backupTemp, join(backups, sha256Hex(path) + '.md'));
170
+ }
171
+ finally {
172
+ await unlink(backupTemp).catch(error => {
173
+ if (!isSystemError(error, 'ENOENT'))
174
+ throw error;
175
+ });
176
+ }
177
+ }
178
+ const temporary = join(dirname(target), '.note-' + randomUUID());
179
+ try {
180
+ const file = await open(temporary, 'wx', 0o600);
181
+ try {
182
+ await file.writeFile(text);
183
+ await file.sync();
184
+ }
185
+ finally {
186
+ await file.close();
187
+ }
188
+ await this.path(path);
189
+ if (expected === undefined) {
190
+ try {
191
+ await link(temporary, target);
192
+ }
193
+ catch (error) {
194
+ if (isSystemError(error, 'EEXIST'))
195
+ throw new NoteExistsError();
196
+ throw error;
197
+ }
198
+ }
199
+ else {
200
+ if ((await this.read(path)).version !== expected) {
201
+ throw new NoteConflictError();
202
+ }
203
+ await rename(temporary, target);
204
+ }
205
+ }
206
+ finally {
207
+ await unlink(temporary).catch(error => {
208
+ if (!isSystemError(error, 'ENOENT'))
209
+ throw error;
210
+ });
211
+ }
212
+ return { path, text, version: sha256Hex(text) };
213
+ });
214
+ }
215
+ async saveAsset(bytes, extension) {
216
+ const id = sha256Hex(bytes) + '.' + extension;
217
+ const target = await this.path('assets/' + id, false);
218
+ await mkdir(dirname(target), { recursive: true });
219
+ await this.path('assets/' + id, false);
220
+ const temporary = join(dirname(target), '.asset-' + randomUUID());
221
+ try {
222
+ const file = await open(temporary, 'wx', 0o600);
223
+ try {
224
+ await file.writeFile(bytes);
225
+ await file.sync();
226
+ }
227
+ finally {
228
+ await file.close();
229
+ }
230
+ await this.path('assets/' + id, false);
231
+ try {
232
+ await link(temporary, target);
233
+ }
234
+ catch (error) {
235
+ if (!isSystemError(error, 'EEXIST'))
236
+ throw error;
237
+ }
238
+ }
239
+ finally {
240
+ await unlink(temporary).catch(error => {
241
+ if (!isSystemError(error, 'ENOENT'))
242
+ throw error;
243
+ });
244
+ }
245
+ return id;
246
+ }
247
+ async readAsset(id) {
248
+ const target = await this.path('assets/' + id, false);
249
+ try {
250
+ const file = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
251
+ try {
252
+ const stat = await file.stat();
253
+ if (!stat.isFile() || stat.size > MAX_NOTE_ASSET_BYTES) {
254
+ throw new BadRequestError('Invalid image attachment');
255
+ }
256
+ return await file.readFile();
257
+ }
258
+ finally {
259
+ await file.close();
260
+ }
261
+ }
262
+ catch (error) {
263
+ if (isSystemError(error, 'ENOENT')) {
264
+ throw new NotFoundError('Attachment not found');
265
+ }
266
+ throw error;
267
+ }
268
+ }
269
+ async sameEntry(path, other) {
270
+ const a = await lstat(await this.path(path, false));
271
+ const b = await lstat(await this.path(other, false));
272
+ return a.dev === b.dev && a.ino === b.ino;
273
+ }
274
+ async exactEntry(path) {
275
+ const target = await this.path(path, false);
276
+ try {
277
+ return (await readdir(dirname(target))).includes(basename(target));
278
+ }
279
+ catch (error) {
280
+ if (isSystemError(error, 'ENOENT'))
281
+ return false;
282
+ throw error;
283
+ }
284
+ }
285
+ async exists(path) {
286
+ const target = await this.path(path, false);
287
+ try {
288
+ await lstat(target);
289
+ return true;
290
+ }
291
+ catch (error) {
292
+ if (isSystemError(error, 'ENOENT'))
293
+ return false;
294
+ throw error;
295
+ }
296
+ }
297
+ async moveEntry(path, destination, folder) {
298
+ const source = await this.path(path, !folder);
299
+ const target = await this.path(destination, !folder);
300
+ if (destination.split('/')[0] === 'assets' ||
301
+ path.split('/')[0] === 'assets') {
302
+ throw new BadRequestError('Cannot move attachments');
303
+ }
304
+ if (folder && destination.startsWith(path + '/')) {
305
+ throw new BadRequestError('Cannot move a folder into itself');
306
+ }
307
+ if (path === destination)
308
+ return;
309
+ if (await this.exists(destination))
310
+ throw new NoteExistsError();
311
+ const stat = await lstat(source);
312
+ if (folder ? !stat.isDirectory() : !stat.isFile()) {
313
+ throw new BadRequestError('Entry type changed');
314
+ }
315
+ await mkdir(dirname(target), { recursive: true });
316
+ await this.path(destination, !folder);
317
+ if (folder) {
318
+ await rename(source, target);
319
+ }
320
+ else {
321
+ try {
322
+ await link(source, target);
323
+ }
324
+ catch (error) {
325
+ if (isSystemError(error, 'EEXIST'))
326
+ throw new NoteExistsError();
327
+ throw error;
328
+ }
329
+ await unlink(source);
330
+ }
331
+ }
332
+ async renameEntry(path, name, folder) {
333
+ return this.serial('rename', async () => {
334
+ if (!name.trim() ||
335
+ name !== name.trim() ||
336
+ /[/\\\0]/.test(name) ||
337
+ name.startsWith('.')) {
338
+ throw new BadRequestError('Invalid entry name');
339
+ }
340
+ if (path.split('/')[0] === 'assets') {
341
+ throw new BadRequestError('Cannot rename attachments');
342
+ }
343
+ const targetPath = [...path.split('/').slice(0, -1), name].join('/');
344
+ if (targetPath.split('/')[0] === 'assets') {
345
+ throw new BadRequestError('Reserved folder name');
346
+ }
347
+ const source = await this.path(path, !folder);
348
+ const target = await this.path(targetPath, !folder);
349
+ let stat;
350
+ try {
351
+ stat = await lstat(source);
352
+ }
353
+ catch (error) {
354
+ if (isSystemError(error, 'ENOENT')) {
355
+ throw new NotFoundError('Note entry not found');
356
+ }
357
+ throw error;
358
+ }
359
+ if (folder ? !stat.isDirectory() : !stat.isFile()) {
360
+ throw new BadRequestError('Entry type changed');
361
+ }
362
+ if (path === targetPath)
363
+ return targetPath;
364
+ try {
365
+ await lstat(target);
366
+ throw new NoteExistsError();
367
+ }
368
+ catch (error) {
369
+ if (!isSystemError(error, 'ENOENT'))
370
+ throw error;
371
+ }
372
+ if (folder) {
373
+ await rename(source, target);
374
+ }
375
+ else {
376
+ try {
377
+ await link(source, target);
378
+ }
379
+ catch (error) {
380
+ if (isSystemError(error, 'EEXIST'))
381
+ throw new NoteExistsError();
382
+ throw error;
383
+ }
384
+ await unlink(source);
385
+ }
386
+ return targetPath;
387
+ });
388
+ }
389
+ async folder(path) {
390
+ const target = await this.path(path, false);
391
+ await mkdir(dirname(target), { recursive: true });
392
+ await this.path(path, false);
393
+ try {
394
+ await mkdir(target);
395
+ }
396
+ catch (error) {
397
+ if (isSystemError(error, 'EEXIST'))
398
+ throw new NoteExistsError();
399
+ throw error;
400
+ }
401
+ }
402
+ }
@@ -0,0 +1,167 @@
1
+ import multipart from '@fastify/multipart';
2
+ import { MAX_NOTE_ASSET_BYTES } from "../../shared/notes/constants.js";
3
+ import { noteAssetFormat } from "../../shared/notes/asset-formats.js";
4
+ import { NoteRecordSchema, NoteIdQuery, NoteDescriptorSchema, NoteResolveBody, NoteResolutionSchema, NoteGraphSchema, ResolveIdentityBody, NoteMutationSchema, UploadNoteAssetBody, NoteAssetSchema, NoteAssetContentSchema, NoteAssetParams, NoteQuery, NoteSchema, NotesIndexSchema, CreateNoteBody, SaveNoteBody, RenameNoteBody, CreateFolderBody } from "../../shared/notes/schemas.js";
5
+ import { ERROR_RESPONSES, NoContentSchema } from "../../shared/http/schemas.js";
6
+ import { BadRequestError } from "../errors/http.js";
7
+ import { createNotesOriginHook } from "../hooks/notes/same-origin.js";
8
+ export async function notesRoutes(app, { service, loopbackBind = false }) {
9
+ await app.register(multipart, {
10
+ attachFieldsToBody: true,
11
+ limits: { fileSize: MAX_NOTE_ASSET_BYTES, files: 1, fields: 0, parts: 1 }
12
+ });
13
+ const typed = app.withTypeProvider();
14
+ app.addHook('onRequest', createNotesOriginHook({ loopbackBind }));
15
+ app.post('/api/notes/assets', {
16
+ preValidation: async (request) => {
17
+ if (!request.isMultipart()) {
18
+ throw new BadRequestError('Expected multipart/form-data');
19
+ }
20
+ },
21
+ schema: {
22
+ consumes: ['multipart/form-data'],
23
+ tags: ['notes'],
24
+ summary: 'Store a note image',
25
+ // The multipart validator rewrites file schemas during compilation.
26
+ body: structuredClone(UploadNoteAssetBody),
27
+ response: { 201: NoteAssetSchema, ...ERROR_RESPONSES }
28
+ }
29
+ }, async (request, reply) => {
30
+ const asset = await service.uploadAsset(await request.body.file.toBuffer());
31
+ reply.code(201);
32
+ return asset;
33
+ });
34
+ app.get('/api/notes/assets/:id', {
35
+ schema: {
36
+ tags: ['notes'],
37
+ summary: 'Read a stored note image',
38
+ params: NoteAssetParams,
39
+ response: { 200: NoteAssetContentSchema, ...ERROR_RESPONSES }
40
+ }
41
+ }, async (request, reply) => {
42
+ const image = await service.readAsset(request.params.id);
43
+ const extension = request.params.id.split('.').at(-1);
44
+ const format = noteAssetFormat(extension);
45
+ if (!format)
46
+ throw new BadRequestError('Unsupported attachment format');
47
+ reply.type(format.mime);
48
+ reply.header('X-Content-Type-Options', 'nosniff');
49
+ return reply.send(image);
50
+ });
51
+ typed.get('/api/notes', {
52
+ schema: {
53
+ tags: ['notes'],
54
+ summary: 'List project notes',
55
+ response: { 200: NotesIndexSchema, ...ERROR_RESPONSES }
56
+ }
57
+ }, () => service.index());
58
+ typed.get('/api/notes/path', {
59
+ schema: {
60
+ tags: ['notes'],
61
+ summary: 'Resolve a current or historical note path',
62
+ querystring: NoteQuery,
63
+ response: { 200: NoteRecordSchema, ...ERROR_RESPONSES }
64
+ }
65
+ }, request => service.identity.resolvePath(request.query.path));
66
+ typed.get('/api/notes/identity', {
67
+ schema: {
68
+ tags: ['notes'],
69
+ summary: 'Read a stable note identity and backlinks',
70
+ querystring: NoteIdQuery,
71
+ response: { 200: NoteDescriptorSchema, ...ERROR_RESPONSES }
72
+ }
73
+ }, request => service.identity.descriptor(request.query.id, request.query.storageId));
74
+ typed.get('/api/notes/by-id', {
75
+ schema: {
76
+ tags: ['notes'],
77
+ summary: 'Read a note by stable identity',
78
+ querystring: NoteIdQuery,
79
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
80
+ }
81
+ }, request => service.identity.readId(request.query.id, request.query.storageId));
82
+ typed.get('/api/notes/graph', {
83
+ schema: {
84
+ tags: ['notes'],
85
+ summary: 'Read the graph of note relationships',
86
+ response: { 200: NoteGraphSchema, ...ERROR_RESPONSES }
87
+ }
88
+ }, () => service.identity.graph());
89
+ typed.post('/api/notes/resolve', {
90
+ schema: {
91
+ tags: ['notes'],
92
+ summary: 'Resolve an occurrence in the current note source',
93
+ body: NoteResolveBody,
94
+ response: { 200: NoteResolutionSchema, ...ERROR_RESPONSES }
95
+ }
96
+ }, request => service.identity.resolve(request.body.sourceId, request.body.storageId, request.body.text, request.body.href, request.body.occurrence));
97
+ typed.post('/api/notes/identity/resolve', {
98
+ schema: {
99
+ tags: ['notes'],
100
+ summary: 'Resolve a restored or moved file identity',
101
+ body: ResolveIdentityBody,
102
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
103
+ }
104
+ }, async (request, reply) => {
105
+ await service.identity.resolveIdentity(request.body.issueId, request.body.restore, request.body.candidateId);
106
+ reply.code(204).send(null);
107
+ });
108
+ typed.post('/api/notes/repair', {
109
+ schema: {
110
+ tags: ['notes'],
111
+ summary: 'Retry pending link repairs',
112
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
113
+ }
114
+ }, async (_request, reply) => {
115
+ await service.identity.retry();
116
+ reply.code(204).send(null);
117
+ });
118
+ typed.get('/api/notes/document', {
119
+ schema: {
120
+ tags: ['notes'],
121
+ summary: 'Read a note',
122
+ querystring: NoteQuery,
123
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
124
+ }
125
+ }, request => service.read(request.query.path));
126
+ typed.post('/api/notes/document', {
127
+ schema: {
128
+ tags: ['notes'],
129
+ summary: 'Create a note',
130
+ body: CreateNoteBody,
131
+ response: { 201: NoteSchema, ...ERROR_RESPONSES }
132
+ }
133
+ }, async (request, reply) => {
134
+ const note = await service.save(request.body.path, request.body.text);
135
+ reply.code(201);
136
+ return note;
137
+ });
138
+ typed.patch('/api/notes/document', {
139
+ schema: {
140
+ tags: ['notes'],
141
+ summary: 'Save a note if its version matches',
142
+ body: SaveNoteBody,
143
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
144
+ }
145
+ }, request => service.save(request.body.path, request.body.text, request.body.version, request.body.id, request.body.storageId));
146
+ typed.post('/api/notes/rename', {
147
+ schema: {
148
+ tags: ['notes'],
149
+ summary: 'Rename a note or folder',
150
+ body: RenameNoteBody,
151
+ response: { 200: NoteMutationSchema, ...ERROR_RESPONSES }
152
+ }
153
+ }, async (request) => {
154
+ return service.rename(request.body.path, request.body.name, request.body.folder, request.body.target, request.body.id, request.body.revision);
155
+ });
156
+ typed.post('/api/notes/folder', {
157
+ schema: {
158
+ tags: ['notes'],
159
+ summary: 'Create a notes folder',
160
+ body: CreateFolderBody,
161
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
162
+ }
163
+ }, async (request, reply) => {
164
+ await service.folder(request.body.path);
165
+ reply.code(204).send(null);
166
+ });
167
+ }
@@ -45,8 +45,9 @@ export async function terminalRoutes(app, options) {
45
45
  },
46
46
  onRequest: requireTerminalOrigin,
47
47
  preValidation: async (request) => {
48
- if (!tokens.consume(request.query.token, request.headers.origin ?? ''))
48
+ if (!tokens.consume(request.query.token, request.headers.origin ?? '')) {
49
49
  throw new ForbiddenError('Terminal connection token is invalid or expired.');
50
+ }
50
51
  }
51
52
  }, socket => {
52
53
  const connection = attachTerminalSocket(socket, options.registry, {
@@ -1,6 +1,7 @@
1
+ import { isMissingPathError } from "../utils/system-error.js";
1
2
  import { execFile, spawn } from 'node:child_process';
2
3
  import { promisify } from 'node:util';
3
- import { readFile } from 'node:fs/promises';
4
+ import { readFile, realpath } from 'node:fs/promises';
4
5
  import { basename, posix, resolve, sep } from 'node:path';
5
6
  import { simpleGit } from 'simple-git';
6
7
  import { GitFileNotFoundError } from "../errors/git.js";
@@ -49,13 +50,6 @@ function objectSpecFor(ref, path) {
49
50
  // the injection this would otherwise guard against.
50
51
  return ref === 'INDEX' ? `:${path}` : `${ref}:${path}`;
51
52
  }
52
- /** `ENOENT` and `ENOTDIR` both mean that the file does not exist. */
53
- function isMissingPathError(error) {
54
- return (!!error &&
55
- typeof error === 'object' &&
56
- 'code' in error &&
57
- (error.code === 'ENOENT' || error.code === 'ENOTDIR'));
58
- }
59
53
  /**
60
54
  * Checks one path with `ls-tree` instead of listing the whole tree. A failing
61
55
  * `ls-tree` means the ref is invalid, so the path is reported as present and
@@ -88,16 +82,25 @@ export async function getFileAtRef(repoPath, ref, path, strict = false) {
88
82
  if (ref === 'WORKTREE') {
89
83
  try {
90
84
  const absolutePath = resolveWithinRepo(repoPath, path);
91
- const buffer = await readFile(absolutePath);
85
+ let target = absolutePath;
86
+ if (strict) {
87
+ const [root, file] = await Promise.all([
88
+ realpath(repoPath),
89
+ realpath(absolutePath)
90
+ ]);
91
+ target = resolveWithinRepo(root, file);
92
+ }
93
+ const buffer = await readFile(target);
92
94
  const isBinary = isBinaryBuffer(buffer);
93
95
  return { content: isBinary ? '' : buffer.toString('utf-8'), isBinary };
94
96
  }
95
97
  catch (error) {
96
98
  if (strict) {
97
- if (isMissingPathError(error))
99
+ if (isMissingPathError(error)) {
98
100
  throw new GitFileNotFoundError('File was deleted or moved', {
99
101
  cause: error
100
102
  });
103
+ }
101
104
  throw error;
102
105
  }
103
106
  return { content: '', isBinary: false };
@@ -118,10 +121,11 @@ export async function getFileAtRef(repoPath, ref, path, strict = false) {
118
121
  }
119
122
  catch (error) {
120
123
  if (strict) {
121
- if (!(await pathExistsAtRef(repoPath, ref, path)))
124
+ if (!(await pathExistsAtRef(repoPath, ref, path))) {
122
125
  throw new GitFileNotFoundError('File was deleted or moved', {
123
126
  cause: error
124
127
  });
128
+ }
125
129
  throw error;
126
130
  }
127
131
  return { content: '', isBinary: false };