collabmd 0.1.35 → 0.1.36

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 (279) hide show
  1. package/dist/client/assets/{CodeMirrorEditor-UCMRAMCV-CvLFeHXu.js → CodeMirrorEditor-UCMRAMCV-DnBV8gCm.js} +1 -1
  2. package/dist/client/assets/{_baseFor-Bfo4esZ5.js → _baseFor-BSErPbF2.js} +1 -1
  3. package/dist/client/assets/{ar-SA-NXDWD63E-WivLHHDW.js → ar-SA-NXDWD63E-CTf_wpt3.js} +1 -1
  4. package/dist/client/assets/{arc-tRoMuWFi.js → arc-DD8jaAGl.js} +1 -1
  5. package/dist/client/assets/architecture-YZFGNWBL-DWWrde02.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-Q4EWVU46-CVdcOotN.js → architectureDiagram-Q4EWVU46-BftXiJA8.js} +1 -1
  7. package/dist/client/assets/{az-AZ-MXSORGZV-DOdq1TKC.js → az-AZ-MXSORGZV-C_C9i9u3.js} +1 -1
  8. package/dist/client/assets/{bg-BG-O6MV2KSF-CYucKuDS.js → bg-BG-O6MV2KSF-YmfvQf63.js} +1 -1
  9. package/dist/client/assets/{blockDiagram-DXYQGD6D-B_gNUKQ4.js → blockDiagram-DXYQGD6D-6UN-nR8-.js} +1 -1
  10. package/dist/client/assets/{bn-IN-MMW2ORAC-DYr8kqNL.js → bn-BD-T2VJNT7N-BD0ob0JE.js} +1 -1
  11. package/dist/client/assets/{bn-BD-T2VJNT7N-2OKWLCYa.js → bn-IN-MMW2ORAC-BTEFR4Ed.js} +1 -1
  12. package/dist/client/assets/{c4Diagram-AHTNJAMY-BSDwfduG.js → c4Diagram-AHTNJAMY-Gz7ZAK0d.js} +1 -1
  13. package/dist/client/assets/{ca-ES-MKBCGNOG-B9Aeb43I.js → ca-ES-MKBCGNOG-aLX_cjA0.js} +1 -1
  14. package/dist/client/assets/channel-XGCkuPi6.js +1 -0
  15. package/dist/client/assets/{chunk-2KRD3SAO-C6u8y11R.js → chunk-2KRD3SAO-CFcF2aNP.js} +1 -1
  16. package/dist/client/assets/{chunk-336JU56O-Ch9X5KoM.js → chunk-336JU56O-DvrrHsJT.js} +2 -2
  17. package/dist/client/assets/chunk-426QAEUC-CEbmCgux.js +1 -0
  18. package/dist/client/assets/{chunk-4BX2VUAB-2e7V-uuE.js → chunk-4BX2VUAB-D5YiFdu1.js} +1 -1
  19. package/dist/client/assets/{chunk-4TB4RGXK-BEsauZP5.js → chunk-4TB4RGXK-CoouNThi.js} +2 -2
  20. package/dist/client/assets/{chunk-55IACEB6-DLqevMSj.js → chunk-55IACEB6-VHgZjidg.js} +1 -1
  21. package/dist/client/assets/{chunk-5FUZZQ4R-6x8SlhH9.js → chunk-5FUZZQ4R-CxIIxXPy.js} +1 -1
  22. package/dist/client/assets/{chunk-5PVQY5BW-R5SE6dhE.js → chunk-5PVQY5BW-ciE7eHID.js} +1 -1
  23. package/dist/client/assets/{chunk-67CJDMHE-CjXw5hFa.js → chunk-67CJDMHE-CW-fbgQB.js} +1 -1
  24. package/dist/client/assets/{chunk-7N4EOEYR-DNHyuswJ.js → chunk-7N4EOEYR-DbAW6XnF.js} +1 -1
  25. package/dist/client/assets/{chunk-AA7GKIK3-DayE4IF_.js → chunk-AA7GKIK3-ClDHDAza.js} +1 -1
  26. package/dist/client/assets/{chunk-BSJP7CBP-CexlYBxB.js → chunk-BSJP7CBP-D2KkrA_B.js} +1 -1
  27. package/dist/client/assets/{chunk-CIAEETIT-CkDJ4DYI.js → chunk-CIAEETIT-BnB8a6HR.js} +1 -1
  28. package/dist/client/assets/{chunk-EDXVE4YY-BVR_nF3y.js → chunk-EDXVE4YY-M4QxyGNt.js} +1 -1
  29. package/dist/client/assets/{chunk-ENJZ2VHE-CFVWytAB.js → chunk-ENJZ2VHE-BQj_1YA_.js} +1 -1
  30. package/dist/client/assets/{chunk-FMBD7UC4-CMahlBa_.js → chunk-FMBD7UC4-qQT7MtX2.js} +1 -1
  31. package/dist/client/assets/{chunk-FOC6F5B3-DzCiCLGs.js → chunk-FOC6F5B3-Bw6XfKDP.js} +1 -1
  32. package/dist/client/assets/{chunk-ICPOFSXX-B9LPHgU7.js → chunk-ICPOFSXX-DqJi89rc.js} +1 -1
  33. package/dist/client/assets/{chunk-K5T4RW27-kmGSebVE.js → chunk-K5T4RW27-CQtjKF2g.js} +7 -7
  34. package/dist/client/assets/{chunk-KGLVRYIC-DVjPRsaR.js → chunk-KGLVRYIC-BWSPiKjp.js} +1 -1
  35. package/dist/client/assets/{chunk-LIHQZDEY-DKFj13sC.js → chunk-LIHQZDEY-DtYrHuCi.js} +1 -1
  36. package/dist/client/assets/{chunk-AXZTUZBK-DuMiJYO9.js → chunk-LOOWOR4L-C5BUwXgZ.js} +2 -2
  37. package/dist/client/assets/{chunk-ORNJ4GCN-D0uTAC-5.js → chunk-ORNJ4GCN-Csga44e5.js} +1 -1
  38. package/dist/client/assets/{chunk-OYMX7WX6-nWiNHNyD.js → chunk-OYMX7WX6-DvtmCasj.js} +1 -1
  39. package/dist/client/assets/chunk-QZHKN3VN-CHVH9_3S.js +1 -0
  40. package/dist/client/assets/{chunk-SRAX5OIU-CLVBQRWC.js → chunk-SRAX5OIU-yDw8XRsi.js} +1 -1
  41. package/dist/client/assets/{chunk-U2HBQHQK-Bue4Ufzn.js → chunk-U2HBQHQK-BBXgsocS.js} +1 -1
  42. package/dist/client/assets/{chunk-X2U36JSP-CRE6lp7n.js → chunk-X2U36JSP-D3OEPG7k.js} +1 -1
  43. package/dist/client/assets/{chunk-XPW4576I-BL5M-tF8.js → chunk-XPW4576I-BUfyrOtN.js} +1 -1
  44. package/dist/client/assets/chunk-YHkbxXgt.js +1 -0
  45. package/dist/client/assets/chunk-YKPWT25G-GISHuloq.js +35 -0
  46. package/dist/client/assets/{chunk-YZCP3GAM-D7Z5wUWA.js → chunk-YZCP3GAM-D1jf1WUI.js} +1 -1
  47. package/dist/client/assets/{chunk-Z5NKEFVG-HOkYqaKa.js → chunk-Z5NKEFVG-Vu8zBV4H.js} +1 -1
  48. package/dist/client/assets/{chunk-ZZ45TVLE-CBbl1Qec.js → chunk-ZZ45TVLE-DGIhE_ST.js} +1 -1
  49. package/dist/client/assets/classDiagram-6PBFFD2Q-D1KhAUL3.js +1 -0
  50. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BSyyJpnR.js +1 -0
  51. package/dist/client/assets/clone-DI0bYZ1L.js +1 -0
  52. package/dist/client/assets/{cose-bilkent-S5V4N54A-xxgR5OJ-.js → cose-bilkent-S5V4N54A-A8ssRFyt.js} +1 -1
  53. package/dist/client/assets/{cs-CZ-V77HGUP7-BDg-F0CJ.js → cs-CZ-V77HGUP7-Du3y53BB.js} +1 -1
  54. package/dist/client/assets/{cytoscape.esm-B3PRdePO.js → cytoscape.esm-BurByw8X.js} +2 -2
  55. package/dist/client/assets/{da-DK-I5RFNTM4-wtSQ0Jxa.js → da-DK-I5RFNTM4-CDJtjf5s.js} +1 -1
  56. package/dist/client/assets/{dagre-VaFYRFbD.js → dagre-Dn6rkrte.js} +1 -1
  57. package/dist/client/assets/{dagre-KV5264BT-C2K9mFg1.js → dagre-KV5264BT-COzej2en.js} +1 -1
  58. package/dist/client/assets/{de-CH-WLRDEVBV-C1swvmZy.js → de-CH-WLRDEVBV-CLV2tVw6.js} +1 -1
  59. package/dist/client/assets/{de-DE-DTI4WUSC-C0L6Nz8X.js → de-DE-DTI4WUSC-B53J0BQ8.js} +1 -1
  60. package/dist/client/assets/{diagram-5BDNPKRD-DE0M0jaM.js → diagram-5BDNPKRD-BuK-UBuN.js} +1 -1
  61. package/dist/client/assets/{diagram-G4DWMVQ6-BfaRuSTT.js → diagram-G4DWMVQ6-CbswIU9X.js} +1 -1
  62. package/dist/client/assets/{diagram-MMDJMWI5-91Bly_hN.js → diagram-MMDJMWI5-BE-yOVGX.js} +1 -1
  63. package/dist/client/assets/{diagram-TYMM5635-fBkLwZGN.js → diagram-TYMM5635-CT8BdT4s.js} +1 -1
  64. package/dist/client/assets/{dist-FC9LftvP.js → dist-B98fHn5m.js} +1 -1
  65. package/dist/client/assets/{dist-VPEWg0Ob.js → dist-BE8C_ouo.js} +1 -1
  66. package/dist/client/assets/{dist-r5I9Jgww.js → dist-BEKQo64E.js} +1 -1
  67. package/dist/client/assets/{dist-BMlUxhOz.js → dist-BOP-dC-R.js} +1 -1
  68. package/dist/client/assets/{dist-DidT3Jay.js → dist-BPEi1dEF.js} +1 -1
  69. package/dist/client/assets/{dist-DAKGEzm0.js → dist-BScEL4VP.js} +1 -1
  70. package/dist/client/assets/dist-BfVljg0L2.js +1 -0
  71. package/dist/client/assets/{dist-PPAeaw4t.js → dist-BiCgLddE.js} +1 -1
  72. package/dist/client/assets/{dist-CSNMpi-w.js → dist-Bn8WRPs1.js} +1 -1
  73. package/dist/client/assets/{dist-CSAH9iuE.js → dist-Brrlevif.js} +1 -1
  74. package/dist/client/assets/{dist-Dwy75xI6.js → dist-CBEw2W2P.js} +1 -1
  75. package/dist/client/assets/{dist-Bkn7JaJv.js → dist-CJWCCWEW.js} +1 -1
  76. package/dist/client/assets/{dist-D1-dK0Dk.js → dist-CQVTTk81.js} +1 -1
  77. package/dist/client/assets/{dist-BSED50tI.js → dist-CaMl1R6P.js} +3 -3
  78. package/dist/client/assets/dist-CcG3Y7Wp.js +1 -0
  79. package/dist/client/assets/dist-CghBwrO3.js +1 -0
  80. package/dist/client/assets/dist-Csbx9lQ-.js +1 -0
  81. package/dist/client/assets/dist-D34Xzrah.js +1 -0
  82. package/dist/client/assets/dist-DLyohOXi.js +1 -0
  83. package/dist/client/assets/{dist-CqU98NzX.js → dist-DPCUH5MO.js} +1 -1
  84. package/dist/client/assets/{dist-74GtnE06.js → dist-DQiph4qx.js} +1 -1
  85. package/dist/client/assets/{dist-C0e9kfNh.js → dist-DWRyOl2w.js} +1 -1
  86. package/dist/client/assets/{dist-cQSK4Xor.js → dist-DZ2t5lUX.js} +1 -1
  87. package/dist/client/assets/{dist-D0dQAc9b.js → dist-Dbg8P4C5.js} +1 -1
  88. package/dist/client/assets/{dist-Co8HE14w.js → dist-DdAqzGFU.js} +1 -1
  89. package/dist/client/assets/{dist-CDUQ5ELn.js → dist-DwvSvkOE.js} +1 -1
  90. package/dist/client/assets/{dist-Doo91MTa.js → dist-WXLmfDGg.js} +1 -1
  91. package/dist/client/assets/{dist-DdR2aYqj.js → dist-aE0_04fl.js} +1 -1
  92. package/dist/client/assets/{dist-Bzb9nPam.js → dist-mPPUaHhq.js} +1 -1
  93. package/dist/client/assets/{dist-DSRCADL9.js → dist-v3i1ifes.js} +1 -1
  94. package/dist/client/assets/{dist-DBzteOlY.js → dist-wkd-N1L5.js} +1 -1
  95. package/dist/client/assets/{drawio-editor-CWLEpwps.js → drawio-editor-D4Qoluwz.js} +1 -1
  96. package/dist/client/assets/{drawioEditor-5rJhxjA4.js → drawioEditor-B65FWq-b.js} +2 -2
  97. package/dist/client/assets/{editor-session-CLOuvy37.js → editor-session-DV80PhYL.js} +3 -3
  98. package/dist/client/assets/{el-GR-ZXDJ2OS4-BH0saXds.js → el-GR-ZXDJ2OS4-DFmRQv0o.js} +1 -1
  99. package/dist/client/assets/{en-IQZW2TQM-CzlcEpq3.js → en-CMP6EMR6-D9x8hVSu.js} +1 -1
  100. package/dist/client/assets/{erDiagram-SMLLAGMA-DUQcqEQ1.js → erDiagram-SMLLAGMA-6VFZA3Qb.js} +1 -1
  101. package/dist/client/assets/{es-ES-DYQHAD43-CyzL9Mni.js → es-ES-DYQHAD43-BiGWIrBG.js} +1 -1
  102. package/dist/client/assets/{eu-ES-QDSYU5Y3-D2-x316S.js → eu-ES-QDSYU5Y3-BjwS0kZi.js} +1 -1
  103. package/dist/client/assets/excalidraw-editor-BZCE9ydu.css +1 -0
  104. package/dist/client/assets/excalidraw-editor-hiJQt1cX.js +9 -0
  105. package/dist/client/assets/excalidraw-scene-MyNAJi2A.js +176 -0
  106. package/dist/client/assets/excalidrawEditor-BnbblxK4.js +2 -0
  107. package/dist/client/assets/{export-page-D2rCmrUH.js → export-page-BKDarp9x.js} +3 -3
  108. package/dist/client/assets/exportDocument-CULEGKmb.js +2 -0
  109. package/dist/client/assets/{fa-IR-26LZG4PM-D8-trU2x.js → fa-IR-26LZG4PM-BgANg2TD.js} +1 -1
  110. package/dist/client/assets/{fi-FI-6YS4KDE4-CCdRVFDQ.js → fi-FI-6YS4KDE4-CBL5f3oN.js} +1 -1
  111. package/dist/client/assets/{flatten-CX_XvnUf.js → flatten-DjXjUsvv.js} +1 -1
  112. package/dist/client/assets/{flowDiagram-DWJPFMVM-B_A5JyId.js → flowDiagram-DWJPFMVM-DO9RH8YQ.js} +1 -1
  113. package/dist/client/assets/{fr-FR-BRLLPTZC-Dqq-O5T_.js → fr-FR-BRLLPTZC-CRibNSLG.js} +1 -1
  114. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-HM3CD7bk.js → ganttDiagram-T4ZO3ILL-B-NMvHWN.js} +1 -1
  115. package/dist/client/assets/gitGraph-7Q5UKJZL-DWNinXu4.js +1 -0
  116. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-Dw7twZDm.js → gitGraphDiagram-UUTBAWPF-B95qkCUx.js} +1 -1
  117. package/dist/client/assets/{gl-ES-OKUZTLYG-BK7pbLbm.js → gl-ES-OKUZTLYG-DEDwa_xt.js} +1 -1
  118. package/dist/client/assets/{graphlib-DDD4Vdye.js → graphlib-CAwOVCJc.js} +1 -1
  119. package/dist/client/assets/{he-IL-26MGYIU7-C3BuYKyY.js → he-IL-26MGYIU7-47wxy70N.js} +1 -1
  120. package/dist/client/assets/{hi-IN-Q4ZOGTP2-BEvXWXnH.js → hi-IN-Q4ZOGTP2-bhV7c3dK.js} +1 -1
  121. package/dist/client/assets/{hu-HU-P2OXPSN5-BWWZeJNp.js → hu-HU-P2OXPSN5-xG-gBMzE.js} +1 -1
  122. package/dist/client/assets/{id-ID-2EIKXYN6-DxSKi_aJ.js → id-ID-2EIKXYN6-MQuYaNHo.js} +1 -1
  123. package/dist/client/assets/image-J2CAS7OM-DD4DaQTs.js +1 -0
  124. package/dist/client/assets/{image-blob-reduce.esm-DiuPkovC.js → image-blob-reduce.esm-D8JQdTFk.js} +1 -1
  125. package/dist/client/assets/{index-BbziNMqE.js → index-BjdYZiBM.js} +2 -2
  126. package/dist/client/assets/{index-BIt5EZXz.css → index-BryqP9QY.css} +1 -1
  127. package/dist/client/assets/info-OMHHGYJF-DtYG_rX0.js +1 -0
  128. package/dist/client/assets/{infoDiagram-42DDH7IO-DX-d3DMp.js → infoDiagram-42DDH7IO-C77Pt7T1.js} +1 -1
  129. package/dist/client/assets/{isEmpty-BKDbo1FH.js → isEmpty-CpfhGGBo.js} +1 -1
  130. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BHmsxpMs.js → ishikawaDiagram-UXIWVN3A-Cy1fwvvg.js} +1 -1
  131. package/dist/client/assets/{it-IT-4CFKVXI2-BmA8wW6q.js → it-IT-4CFKVXI2-v2kj-y7j.js} +1 -1
  132. package/dist/client/assets/{ja-JP-MAND2Z4V-CLS1jI9o.js → ja-JP-MAND2Z4V-BahXSQz-.js} +1 -1
  133. package/dist/client/assets/{journeyDiagram-VCZTEJTY-jxDSjgE1.js → journeyDiagram-VCZTEJTY-CYWaI6re.js} +1 -1
  134. package/dist/client/assets/{jsx-runtime-B_8UNt-W.js → jsx-runtime-1KUJVfx8.js} +1 -1
  135. package/dist/client/assets/{kaa-HQRTCXTJ-W15DEDPz.js → kaa-HQRTCXTJ-BF26ydFa.js} +1 -1
  136. package/dist/client/assets/{kab-KAB-JKIKKTGH-MLh1inqS.js → kab-KAB-JKIKKTGH-CEIH0F80.js} +1 -1
  137. package/dist/client/assets/{kanban-definition-6JOO6SKY-od3qY1mE.js → kanban-definition-6JOO6SKY-DY1xE7tm.js} +1 -1
  138. package/dist/client/assets/{kk-KZ-HXQ63ZFC-BtFrPeLs.js → kk-KZ-HXQ63ZFC-CxWjc9Sf.js} +1 -1
  139. package/dist/client/assets/{km-KH-PKL3LSGX-xO5FyM9T.js → km-KH-PKL3LSGX-DJlCteEp.js} +1 -1
  140. package/dist/client/assets/{ko-KR-KXLREZX3-NZFoRybS.js → ko-KR-KXLREZX3-B1gtI_ci.js} +1 -1
  141. package/dist/client/assets/{ku-TR-MJ4IER3X-B5eVPIpK.js → ku-TR-MJ4IER3X-Bf41y2v2.js} +1 -1
  142. package/dist/client/assets/{line-CZsGUM4H.js → line-BCItEgq3.js} +1 -1
  143. package/dist/client/assets/{linear-B2dOdA24.js → linear-dsIQT_iU.js} +1 -1
  144. package/dist/client/assets/{lt-LT-H6WFQ6FJ-D_7_OeJD.js → lt-LT-H6WFQ6FJ-DA1or-Eo.js} +1 -1
  145. package/dist/client/assets/{lv-LV-GOCK6SJW-CnpKr0oQ.js → lv-LV-GOCK6SJW-qa0BoYMm.js} +1 -1
  146. package/dist/client/assets/main-QJcxyvDa.js +1404 -0
  147. package/dist/client/assets/{mermaid-parser.core-DtIbxv6L.js → mermaid-parser.core-CsTVsPcD.js} +2 -2
  148. package/dist/client/assets/{mermaid-runtime-vVJfjscE.js → mermaid-runtime-BGlnoUXY.js} +3 -3
  149. package/dist/client/assets/{mindmap-definition-QFDTVHPH-C9qrdAqo.js → mindmap-definition-QFDTVHPH-DB_M5r-j.js} +1 -1
  150. package/dist/client/assets/{mr-IN-IXVLDMEQ-Pht2X_GK.js → mr-IN-IXVLDMEQ-DXFRgvgb.js} +1 -1
  151. package/dist/client/assets/{my-MM-YG6MYTTD-Cgq4HhPO.js → my-MM-YG6MYTTD-BKDIfalJ.js} +1 -1
  152. package/dist/client/assets/{nb-NO-XGWJISD2-Bn_Bk2un.js → nb-NO-XGWJISD2-DbwbopH8.js} +1 -1
  153. package/dist/client/assets/{nl-NL-XVDVCPNC-YkWSJS94.js → nl-NL-XVDVCPNC-ChSneTSU.js} +1 -1
  154. package/dist/client/assets/{nn-NO-EMX5MJX5-DhP5eZJ7.js → nn-NO-EMX5MJX5-Cf7ZWKWZ.js} +1 -1
  155. package/dist/client/assets/{oc-FR-ROEINTVO-D3udMPnr.js → oc-FR-ROEINTVO-lM7Nl7hD.js} +1 -1
  156. package/dist/client/assets/{pa-IN-ZYN6SUDY-CR5YzFig.js → pa-IN-ZYN6SUDY-C9WNUTeR.js} +1 -1
  157. package/dist/client/assets/packet-4T2RLAQJ-CUVefhAv.js +1 -0
  158. package/dist/client/assets/percentages-5DKR754L-DuhOjTM2.js +1 -0
  159. package/dist/client/assets/{pica-BEurQXMu.js → pica-CgGaIJly.js} +1 -1
  160. package/dist/client/assets/pie-ZZUOXDRM-BHBJ1dLX.js +1 -0
  161. package/dist/client/assets/{pieDiagram-DEJITSTG-BHXxj7wl.js → pieDiagram-DEJITSTG-BOo-WzjZ.js} +1 -1
  162. package/dist/client/assets/{pl-PL-6VTG7PHC-_wZuoIDM.js → pl-PL-6VTG7PHC-DqRVepnC.js} +1 -1
  163. package/dist/client/assets/{preview-diagram-utils-D8VypUft.js → preview-diagram-utils-Nqpp4FNV.js} +1 -1
  164. package/dist/client/assets/preview-render-compiler-BCB8u6iM.js +1 -0
  165. package/dist/client/assets/{preview-render-compiler-BHeDhYho.js → preview-render-compiler-Tkc6JX1O.js} +1 -1
  166. package/dist/client/assets/{preview-render-profile-BF6os3x9.js → preview-render-profile-Bf6R7v4w.js} +2 -2
  167. package/dist/client/assets/{preview-render-worker-C4AFtMIr.js → preview-render-worker-1LBLqgKu.js} +2 -2
  168. package/dist/client/assets/{pt-BR-5YFKRR5G-M7gIm2Hi.js → pt-BR-5YFKRR5G-CopLYEA6.js} +1 -1
  169. package/dist/client/assets/{pt-PT-GEIXXZ67-CcxWpkGj.js → pt-PT-GEIXXZ67-BjZ_Lphg.js} +1 -1
  170. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CWW7LORF.js → quadrantDiagram-34T5L4WZ-2vfo0l5S.js} +1 -1
  171. package/dist/client/assets/radar-PYXPWWZC-2jR4qZNU.js +1 -0
  172. package/dist/client/assets/{reduce-C6FuFJbE.js → reduce-ChpYjEUB.js} +1 -1
  173. package/dist/client/assets/{requirementDiagram-MS252O5E-CTljNdQH.js → requirementDiagram-MS252O5E-CtUIQnWa.js} +1 -1
  174. package/dist/client/assets/{ro-RO-E2P6TFZI-qR9noVzP.js → ro-RO-E2P6TFZI-DakDz5Xu.js} +1 -1
  175. package/dist/client/assets/{room-DI7DXGeS.js → room-z2w9b96b.js} +1 -1
  176. package/dist/client/assets/{ru-RU-YBIIY43T-CE4S_RX8.js → ru-RU-YBIIY43T-BMvCT-XT.js} +1 -1
  177. package/dist/client/assets/{sankeyDiagram-XADWPNL6-CCfFkC74.js → sankeyDiagram-XADWPNL6-tcSeuifE.js} +1 -1
  178. package/dist/client/assets/{sequenceDiagram-FGHM5R23-BD9vj6WG.js → sequenceDiagram-FGHM5R23-DtF-1Kt2.js} +1 -1
  179. package/dist/client/assets/{si-LK-673C2CI2-Bv6Avh3z.js → si-LK-673C2CI2-hGdH1S9E.js} +1 -1
  180. package/dist/client/assets/{sk-SK-CKZHQ4AP-Cnlpp55z.js → sk-SK-CKZHQ4AP-DiRA-fj8.js} +1 -1
  181. package/dist/client/assets/{sl-SI-557H2GWM-BfGq7AQF.js → sl-SI-557H2GWM-DN94n_1-.js} +1 -1
  182. package/dist/client/assets/src-BuiVcSz-.js +1 -0
  183. package/dist/client/assets/{stateDiagram-FHFEXIEX-CpiX3KPd.js → stateDiagram-FHFEXIEX-DKk7Sb0f.js} +1 -1
  184. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DJUaiADr.js +1 -0
  185. package/dist/client/assets/subset-shared.chunk-CWNYNLru.js +1 -0
  186. package/dist/client/assets/{subset-worker.chunk-BSCP6IiC.js → subset-worker.chunk-kj1e3wjW.js} +1 -1
  187. package/dist/client/assets/{sv-SE-XOPKKDWS-B2_Vk-sp.js → sv-SE-XOPKKDWS-CkHUNABj.js} +1 -1
  188. package/dist/client/assets/{ta-IN-XBH7DQI3-BXQMZZIx.js → ta-IN-XBH7DQI3-W-157oVK.js} +1 -1
  189. package/dist/client/assets/{th-TH-TOCWH3R4-C-ZIYMfH.js → th-TH-TOCWH3R4-BsU-Pydr.js} +1 -1
  190. package/dist/client/assets/{timeline-definition-GMOUNBTQ-BafRAkSF.js → timeline-definition-GMOUNBTQ-Bs6U53Mo.js} +1 -1
  191. package/dist/client/assets/{tr-TR-GXW6CYY5-dl702PMA.js → tr-TR-GXW6CYY5-CTddyq89.js} +1 -1
  192. package/dist/client/assets/treeView-SZITEDCU-DN5dP2ol.js +1 -0
  193. package/dist/client/assets/treemap-W4RFUUIX-Dva7DBJR.js +1 -0
  194. package/dist/client/assets/{uk-UA-IJKTNUDG-CUYQvSSF.js → uk-UA-IJKTNUDG-1LzXkII3.js} +1 -1
  195. package/dist/client/assets/{uz-UZ-TBZZEA4V-B9qw0PXX.js → uz-UZ-TBZZEA4V-kqm-qcUj.js} +1 -1
  196. package/dist/client/assets/{vennDiagram-DHZGUBPP-DSngq_Zj.js → vennDiagram-DHZGUBPP-DNnIjRae.js} +1 -1
  197. package/dist/client/assets/{vi-VN-MULSGNVB-DqxZ_sVc.js → vi-VN-MULSGNVB-C3H2AUhm.js} +1 -1
  198. package/dist/client/assets/wardley-RL74JXVD-CDZW5uzY.js +1 -0
  199. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CAEd2kJG.js → wardleyDiagram-NUSXRM2D-C-scJfPY.js} +1 -1
  200. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BScQpnru.js → xychartDiagram-5P7HB3ND-D3nQoxhz.js} +1 -1
  201. package/dist/client/assets/{yjs-provider-reset-guard-_Q3mj6tP.js → yjs-provider-reset-guard-DSPzcvAO.js} +2 -2
  202. package/dist/client/assets/{zh-CN-MVX3E32U-BM0aLuGc.js → zh-CN-MVX3E32U-BULPbsyl.js} +1 -1
  203. package/dist/client/assets/{zh-HK-JNU5RLIE-dmG_YD0d.js → zh-HK-JNU5RLIE-bNGmeSaW.js} +1 -1
  204. package/dist/client/assets/{zh-TW-LENVCA6T-LFgbiOi3.js → zh-TW-LENVCA6T-CQDp064P.js} +1 -1
  205. package/dist/client/drawio-editor.html +1 -1
  206. package/dist/client/excalidraw-editor.html +1 -1
  207. package/dist/client/export-document.html +1 -1
  208. package/dist/client/index.html +2 -2
  209. package/package.json +11 -11
  210. package/src/client/application/app-shell/presence-feature.js +265 -34
  211. package/src/client/application/app-shell/ui-feature-shell.js +26 -3
  212. package/src/client/application/app-shell/ui-feature-tab-activity.js +1 -0
  213. package/src/client/application/app-shell/workspace-feature.js +5 -2
  214. package/src/client/application/app-shell-elements.js +3 -0
  215. package/src/client/application/diagram-preview-export.js +304 -0
  216. package/src/client/application/mermaid-preview-hydrator.js +153 -23
  217. package/src/client/application/plantuml-preview-hydrator.js +155 -20
  218. package/src/client/application/preview-diagram-utils.js +48 -8
  219. package/src/client/application/preview-renderer.js +2 -0
  220. package/src/client/application/quick-switcher-loader.js +4 -1
  221. package/src/client/application/workspace-route-controller.js +35 -2
  222. package/src/client/application/workspace-state-store.js +1 -0
  223. package/src/client/bootstrap/collabmd-app-shell.js +9 -0
  224. package/src/client/domain/diagram-action-icons.js +21 -0
  225. package/src/client/domain/excalidraw-api-scene-sync.js +70 -0
  226. package/src/client/excalidraw-editor.js +106 -18
  227. package/src/client/infrastructure/excalidraw-room-client.js +41 -4
  228. package/src/client/presentation/app-shell-template.js +18 -1
  229. package/src/client/presentation/file-explorer-controller.js +10 -0
  230. package/src/client/presentation/file-explorer-view.js +6 -0
  231. package/src/client/presentation/image-lightbox-controller.js +290 -29
  232. package/src/client/styles/features/collaboration-presence.css +212 -0
  233. package/src/client/styles/features/diagram-preview.css +71 -4
  234. package/src/client/styles/features/embedded-preview.css +4 -6
  235. package/src/client/styles/features/media-lightbox.css +2 -0
  236. package/src/server/config/env.js +4 -0
  237. package/src/server/create-app-server.js +2 -0
  238. package/src/server/domain/bases/base-query-service.js +89 -7
  239. package/src/server/domain/collaboration/collaboration-room.js +10 -1
  240. package/src/server/infrastructure/http/create-request-handler.js +1 -0
  241. package/src/server/infrastructure/http/create-vault-api-handler.js +2 -0
  242. package/src/server/infrastructure/http/create-vault-api-query-handler.js +100 -26
  243. package/src/server/infrastructure/persistence/vault-file-store.js +136 -0
  244. package/src/server/infrastructure/workspace/file-system-sync-service.js +9 -11
  245. package/dist/client/assets/architecture-YZFGNWBL-CtU43_5T.js +0 -1
  246. package/dist/client/assets/channel-BDb1g_mJ.js +0 -1
  247. package/dist/client/assets/chunk-426QAEUC-CwJ7loGf.js +0 -1
  248. package/dist/client/assets/chunk-DpHwWhuW.js +0 -1
  249. package/dist/client/assets/chunk-NU2UP6EN-UVL-wkIc.js +0 -35
  250. package/dist/client/assets/chunk-QZHKN3VN-BnOS2iQW.js +0 -1
  251. package/dist/client/assets/classDiagram-6PBFFD2Q-MIbgV5UL.js +0 -1
  252. package/dist/client/assets/classDiagram-v2-HSJHXN6E-Bb70vg2O.js +0 -1
  253. package/dist/client/assets/clone-D6JWrlxJ.js +0 -1
  254. package/dist/client/assets/dist-B3lARV_e.js +0 -1
  255. package/dist/client/assets/dist-BCvzlTeb.js +0 -1
  256. package/dist/client/assets/dist-Bq8Z0WUT.js +0 -1
  257. package/dist/client/assets/dist-C8wdW7W4.js +0 -1
  258. package/dist/client/assets/dist-CHSg_nUM.js +0 -1
  259. package/dist/client/assets/dist-CZPTvk_g2.js +0 -1
  260. package/dist/client/assets/excalidraw-editor-Dj3i59LW.css +0 -1
  261. package/dist/client/assets/excalidraw-editor-ukEKPqGJ.js +0 -9
  262. package/dist/client/assets/excalidraw-scene-CDK3oDnm.js +0 -176
  263. package/dist/client/assets/excalidrawEditor-BJ8QLmWc.js +0 -2
  264. package/dist/client/assets/exportDocument-Dunnjgii.js +0 -2
  265. package/dist/client/assets/gitGraph-7Q5UKJZL-BrBlB49D.js +0 -1
  266. package/dist/client/assets/image-XLNPCCAD-BFNybKza.js +0 -1
  267. package/dist/client/assets/info-OMHHGYJF-BcmgcSx1.js +0 -1
  268. package/dist/client/assets/main-CN0kbtGk.js +0 -1369
  269. package/dist/client/assets/packet-4T2RLAQJ-DYYSvRuJ.js +0 -1
  270. package/dist/client/assets/percentages-5DKR754L-D7n4T776.js +0 -1
  271. package/dist/client/assets/pie-ZZUOXDRM-cG65Mc64.js +0 -1
  272. package/dist/client/assets/preview-render-compiler-D3QM1Ryz.js +0 -1
  273. package/dist/client/assets/radar-PYXPWWZC-Bmpup_xw.js +0 -1
  274. package/dist/client/assets/src-Pgt9Gp65.js +0 -1
  275. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D_5ufPqT.js +0 -1
  276. package/dist/client/assets/subset-shared.chunk-C9HjwTXy.js +0 -1
  277. package/dist/client/assets/treeView-SZITEDCU-NbjhK5cw.js +0 -1
  278. package/dist/client/assets/treemap-W4RFUUIX-DJzRbb-x.js +0 -1
  279. package/dist/client/assets/wardley-RL74JXVD-DTNoH_BY.js +0 -1
@@ -75,13 +75,12 @@
75
75
 
76
76
  .preview-content .mermaid-toolbar,
77
77
  .mermaid-maximized-root .mermaid-toolbar {
78
- justify-content: flex-end;
78
+ width: 100%;
79
79
  }
80
80
 
81
81
  .preview-content .mermaid-zoom-btn,
82
82
  .mermaid-maximized-root .mermaid-zoom-btn {
83
- min-width: 32px;
84
- line-height: 1.5;
83
+ flex-shrink: 0;
85
84
  }
86
85
 
87
86
  .preview-content .mermaid-zoom-btn:disabled,
@@ -174,13 +173,12 @@ body.mermaid-maximized-open::before {
174
173
 
175
174
  .preview-content .plantuml-toolbar,
176
175
  .plantuml-maximized-root .plantuml-toolbar {
177
- justify-content: flex-end;
176
+ width: 100%;
178
177
  }
179
178
 
180
179
  .preview-content .plantuml-tool-btn,
181
180
  .plantuml-maximized-root .plantuml-tool-btn {
182
- min-width: 32px;
183
- line-height: 1.5;
181
+ flex-shrink: 0;
184
182
  }
185
183
 
186
184
  .preview-content .plantuml-frame,
@@ -68,6 +68,8 @@
68
68
 
69
69
  .image-lightbox-btn {
70
70
  min-width: 0;
71
+ min-height: 40px;
72
+ min-width: 40px;
71
73
  padding: 8px 12px;
72
74
  border: none;
73
75
  border-radius: var(--radius-full);
@@ -322,6 +322,10 @@ export function loadConfig(overrides = {}) {
322
322
  httpRequestTimeoutMs: parsePositiveInt(process.env.HTTP_REQUEST_TIMEOUT_MS, 30_000),
323
323
  git,
324
324
  gitEnabled: git.enabled,
325
+ maxArchiveEntries: parsePositiveInt(process.env.COLLABMD_MAX_ARCHIVE_ENTRIES, 10_000),
326
+ maxBaseQueryRows: parsePositiveInt(process.env.COLLABMD_MAX_BASE_QUERY_ROWS, 5_000),
327
+ maxDownloadFileBytes: parsePositiveInt(process.env.COLLABMD_MAX_DOWNLOAD_FILE_BYTES, 268_435_456),
328
+ maxInitialSyncBytes: parsePositiveInt(process.env.COLLABMD_MAX_INITIAL_SYNC_BYTES, 16_777_216),
325
329
  perfLoggingEnabled: overrides.perfLoggingEnabled ?? isPerfLoggingEnabled(process.env.COLLABMD_PERF_LOGGING),
326
330
  port: parsePort(process.env.PORT, 1234),
327
331
  nodeEnv,
@@ -77,6 +77,7 @@ export function createAppServer(config = loadConfig()) {
77
77
  let fileSystemSyncService = null;
78
78
  let workspaceMutationCoordinator = null;
79
79
  const baseQueryService = new BaseQueryService({
80
+ maxResultRows: config.maxBaseQueryRows,
80
81
  vaultFileStore,
81
82
  workspaceStateProvider: () => workspaceMutationCoordinator?.workspaceState ?? null,
82
83
  workspaceStateSynchronizer: () => fileSystemSyncService?.flushPendingChanges?.(),
@@ -104,6 +105,7 @@ export function createAppServer(config = loadConfig()) {
104
105
  }),
105
106
  getHydrateDelayMs: () => testControls.wsRoomHydrateDelayMs,
106
107
  idleGraceMs: config.wsRoomIdleGraceMs,
108
+ maxInitialSyncBytes: config.maxInitialSyncBytes,
107
109
  maxBufferedAmountBytes: config.wsMaxBufferedAmountBytes,
108
110
  name,
109
111
  onEmpty,
@@ -53,6 +53,32 @@ function sortRows(rows, sortChain = []) {
53
53
  });
54
54
  }
55
55
 
56
+ function compareRowsBySortChain(left, right, sortChain = []) {
57
+ for (const sortConfig of sortChain) {
58
+ const delta = compareValues(left.rawCells[sortConfig.property], right.rawCells[sortConfig.property]);
59
+ if (delta !== 0) {
60
+ return sortConfig.direction === 'desc' ? -delta : delta;
61
+ }
62
+ }
63
+
64
+ return String(left.file.path ?? '').localeCompare(String(right.file.path ?? ''));
65
+ }
66
+
67
+ function insertSortedBounded(rows, row, {
68
+ limit,
69
+ sortChain = [],
70
+ }) {
71
+ let insertIndex = rows.findIndex((existingRow) => compareRowsBySortChain(row, existingRow, sortChain) < 0);
72
+ if (insertIndex === -1) {
73
+ insertIndex = rows.length;
74
+ }
75
+
76
+ rows.splice(insertIndex, 0, row);
77
+ if (rows.length > limit) {
78
+ rows.pop();
79
+ }
80
+ }
81
+
56
82
  function escapeRegExp(value = '') {
57
83
  return String(value ?? '').replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
58
84
  }
@@ -102,16 +128,19 @@ function pruneFilterNodeForProperty(filterNode, propertyId = '') {
102
128
 
103
129
  export class BaseQueryService {
104
130
  constructor({
131
+ maxResultRows = 5_000,
105
132
  vaultFileStore,
106
133
  workspaceStateProvider = null,
107
134
  workspaceStateSynchronizer = null,
108
135
  }) {
136
+ this.maxResultRows = maxResultRows;
109
137
  this.vaultFileStore = vaultFileStore;
110
138
  this.snapshotStore = new BaseIndexSnapshotStore({
111
139
  vaultFileStore,
112
140
  workspaceStateProvider,
113
141
  workspaceStateSynchronizer,
114
142
  });
143
+ this.definitionCache = new Map();
115
144
  this.propertyCatalogCache = new Map();
116
145
  }
117
146
 
@@ -202,6 +231,25 @@ export class BaseQueryService {
202
231
  return result;
203
232
  }
204
233
 
234
+ getCachedDefinition(cacheKey, baseSource) {
235
+ const sourceText = String(baseSource ?? '');
236
+ const existing = this.definitionCache.get(cacheKey);
237
+ if (existing?.source === sourceText) {
238
+ return existing.definition;
239
+ }
240
+
241
+ const definition = normalizeBaseDefinition(sourceText);
242
+ this.definitionCache.set(cacheKey, {
243
+ definition,
244
+ source: sourceText,
245
+ });
246
+ if (this.definitionCache.size > 50) {
247
+ const firstKey = this.definitionCache.keys().next().value;
248
+ this.definitionCache.delete(firstKey);
249
+ }
250
+ return definition;
251
+ }
252
+
205
253
  invalidatePropertyCatalogsExcept(scannedAt = '') {
206
254
  Array.from(this.propertyCatalogCache.keys()).forEach((cacheKey) => {
207
255
  if (cacheKey !== scannedAt) {
@@ -222,7 +270,7 @@ export class BaseQueryService {
222
270
  throw new Error('Base source not found');
223
271
  }
224
272
 
225
- const definition = normalizeBaseDefinition(baseSource);
273
+ const definition = this.getCachedDefinition(source === null ? `file:${basePath}` : `source:${sourcePath}:${basePath}`, baseSource);
226
274
  const snapshot = await this.ensureIndexSnapshot({
227
275
  basePath,
228
276
  sourcePath,
@@ -251,12 +299,24 @@ export class BaseQueryService {
251
299
  collectCandidateRows({
252
300
  activeView,
253
301
  astCache = new Map(),
302
+ columns = [],
254
303
  definition,
255
304
  evaluatedPropertyIds,
305
+ limit = null,
306
+ maxRows = this.maxResultRows,
256
307
  snapshot,
308
+ sortChain = [],
309
+ search = '',
257
310
  thisFile,
258
311
  }) {
259
312
  const rows = [];
313
+ const effectiveMaxRows = Number.isInteger(maxRows) && maxRows > 0 ? maxRows : Infinity;
314
+ const effectiveLimit = Number.isInteger(limit) && limit > 0 ? limit : Infinity;
315
+ const canBoundDuringCollection = Number.isFinite(Math.min(effectiveLimit, effectiveMaxRows))
316
+ && sortChain.length > 0
317
+ && !activeView.groupBy?.property
318
+ && (activeView.summaries?.length ?? 0) === 0;
319
+ const hardLimit = Math.min(effectiveLimit, effectiveMaxRows);
260
320
 
261
321
  snapshot.filePaths.forEach((filePath) => {
262
322
  const row = snapshot.rowsByPath.get(filePath);
@@ -280,10 +340,23 @@ export class BaseQueryService {
280
340
  rawCells[propertyId] = getPropertyValue(propertyId, row, definition, snapshot, thisFile, rowContext);
281
341
  });
282
342
 
283
- rows.push({
343
+ const candidateRow = {
284
344
  file: row.file,
285
345
  rawCells,
286
- });
346
+ };
347
+ if (!rowMatchesSearch(candidateRow, columns, search)) {
348
+ return;
349
+ }
350
+
351
+ if (canBoundDuringCollection) {
352
+ insertSortedBounded(rows, candidateRow, {
353
+ limit: hardLimit,
354
+ sortChain,
355
+ });
356
+ return;
357
+ }
358
+
359
+ rows.push(candidateRow);
287
360
  });
288
361
 
289
362
  return rows;
@@ -378,15 +451,24 @@ export class BaseQueryService {
378
451
  });
379
452
 
380
453
  const astCache = new Map();
454
+ const sortChain = buildSortChain(context.activeView);
455
+ const requestedLimit = Number.isInteger(context.activeView.limit) && context.activeView.limit > 0
456
+ ? context.activeView.limit
457
+ : null;
381
458
  let rows = this.collectCandidateRows({
382
459
  ...context,
383
460
  astCache,
461
+ limit: requestedLimit,
462
+ maxRows: this.maxResultRows,
463
+ search,
464
+ sortChain,
384
465
  });
385
- rows = rows.filter((row) => rowMatchesSearch(row, context.columns, search));
386
- rows = sortRows(rows, buildSortChain(context.activeView));
466
+ rows = sortRows(rows, sortChain);
387
467
 
388
- if (context.activeView.limit != null) {
389
- rows = rows.slice(0, context.activeView.limit);
468
+ if (requestedLimit != null) {
469
+ rows = rows.slice(0, Math.min(requestedLimit, this.maxResultRows));
470
+ } else if (Number.isInteger(this.maxResultRows) && this.maxResultRows > 0) {
471
+ rows = rows.slice(0, this.maxResultRows);
390
472
  }
391
473
 
392
474
  const payload = buildQueryResultPayload({
@@ -165,6 +165,7 @@ export class CollaborationRoom {
165
165
  getHydrateDelayMs = null,
166
166
  name,
167
167
  idleGraceMs = 0,
168
+ maxInitialSyncBytes = 16_777_216,
168
169
  maxBufferedAmountBytes,
169
170
  perfLoggingEnabled = false,
170
171
  vaultFileStore,
@@ -173,6 +174,7 @@ export class CollaborationRoom {
173
174
  }) {
174
175
  this.name = name;
175
176
  this.idleGraceMs = idleGraceMs;
177
+ this.maxInitialSyncBytes = maxInitialSyncBytes;
176
178
  this.maxBufferedAmountBytes = maxBufferedAmountBytes;
177
179
  this.perfLoggingEnabled = perfLoggingEnabled;
178
180
  this.documentStore = documentStore ?? new CollaborationDocumentStore({
@@ -576,10 +578,17 @@ export class CollaborationRoom {
576
578
  this.debugMetrics.initialSyncCount += 1;
577
579
  this.debugMetrics.lastInitialSyncAt = Date.now();
578
580
  const initialSyncMessage = this.getInitialSyncMessage();
581
+ const initialSyncBytes = initialSyncMessage.byteLength;
582
+ if (initialSyncBytes > this.maxInitialSyncBytes) {
583
+ console.warn(
584
+ `[room:${this.name}] Initial sync payload is ${initialSyncBytes} bytes, exceeding ${this.maxInitialSyncBytes} bytes`,
585
+ );
586
+ }
579
587
  logPerfEvent(this.perfLoggingEnabled, `room:${this.name}`, {
580
- bytes: initialSyncMessage.byteLength,
588
+ bytes: initialSyncBytes,
581
589
  count: this.debugMetrics.initialSyncCount,
582
590
  event: 'initial-sync',
591
+ exceedsLimit: initialSyncBytes > this.maxInitialSyncBytes,
583
592
  });
584
593
  return sendMessage.call(this, ws, initialSyncMessage, this);
585
594
  }
@@ -63,6 +63,7 @@ export function createRequestHandler(
63
63
  const handleVaultApi = createVaultApiHandler({
64
64
  baseQueryService,
65
65
  backlinkIndex,
66
+ config,
66
67
  docxExporter,
67
68
  plantUmlRenderer,
68
69
  roomRegistry,
@@ -5,6 +5,7 @@ import { createVaultApiQueryHandler } from './create-vault-api-query-handler.js'
5
5
  export function createVaultApiHandler({
6
6
  baseQueryService = null,
7
7
  backlinkIndex,
8
+ config = {},
8
9
  docxExporter = null,
9
10
  plantUmlRenderer = null,
10
11
  roomRegistry = null,
@@ -14,6 +15,7 @@ export function createVaultApiHandler({
14
15
  const handleVaultApiQuery = createVaultApiQueryHandler({
15
16
  baseQueryService,
16
17
  backlinkIndex,
18
+ config,
17
19
  vaultFileStore,
18
20
  workspaceMutationCoordinator,
19
21
  });
@@ -1,5 +1,7 @@
1
1
  import archiver from 'archiver';
2
+ import { readdir } from 'node:fs/promises';
2
3
  import { basename } from 'node:path';
4
+ import { join } from 'node:path';
3
5
 
4
6
  import {
5
7
  isBaseFilePath,
@@ -9,11 +11,14 @@ import {
9
11
  isMermaidFilePath,
10
12
  isPlantUmlFilePath,
11
13
  } from '../../../domain/file-kind.js';
14
+ import { isIgnoredVaultEntry } from '../persistence/path-utils.js';
12
15
  import { parseJsonBody } from './request-body.js';
13
16
  import { jsonResponse, sendResponse, sendStreamResponse } from './http-response.js';
14
17
 
15
18
  const SVG_MIME_TYPE = 'image/svg+xml';
16
19
  const SVG_ATTACHMENT_CSP = "default-src 'none'; img-src 'self' data: blob:; style-src 'unsafe-inline'; sandbox";
20
+ const DEFAULT_MAX_ARCHIVE_ENTRIES = 10_000;
21
+ const DEFAULT_MAX_DOWNLOAD_FILE_BYTES = 268_435_456;
17
22
 
18
23
  function encodeContentDispositionFilename(fileName) {
19
24
  return encodeURIComponent(String(fileName ?? ''))
@@ -78,7 +83,8 @@ function selectReadOperation(vaultFileStore, filePath) {
78
83
  }
79
84
 
80
85
  async function streamDirectoryArchive(req, res, {
81
- entries = [],
86
+ maxEntries = 10_000,
87
+ rootAbsolutePath = '',
82
88
  rootName = 'archive',
83
89
  } = {}) {
84
90
  const archive = archiver('zip', {
@@ -99,22 +105,62 @@ async function streamDirectoryArchive(req, res, {
99
105
  stream: archive,
100
106
  });
101
107
 
102
- for (const entry of entries) {
103
- if (entry.type === 'directory') {
104
- const directoryName = entry.path
105
- ? `${rootName}/${entry.path}/`
106
- : `${rootName}/`;
107
- archive.append('', { name: directoryName });
108
- continue;
108
+ let entryCount = 0;
109
+ const appendEntry = () => {
110
+ entryCount += 1;
111
+ if (entryCount > maxEntries) {
112
+ throw new Error(`Directory archive exceeds ${maxEntries} entries`);
109
113
  }
114
+ };
110
115
 
111
- archive.file(entry.absolutePath, {
112
- name: `${rootName}/${entry.path}`,
113
- });
114
- }
116
+ try {
117
+ const visitDirectory = async (directoryAbsolutePath, relativeDirectoryPath = '') => {
118
+ const dirEntries = (await readdir(directoryAbsolutePath, { withFileTypes: true }))
119
+ .filter((entry) => !isIgnoredVaultEntry(entry.name))
120
+ .sort((left, right) => {
121
+ if (left.isDirectory() && !right.isDirectory()) return -1;
122
+ if (!left.isDirectory() && right.isDirectory()) return 1;
123
+ return left.name.localeCompare(right.name, undefined, { sensitivity: 'base' });
124
+ });
125
+
126
+ if (dirEntries.length === 0) {
127
+ appendEntry();
128
+ archive.append('', {
129
+ name: relativeDirectoryPath ? `${rootName}/${relativeDirectoryPath}/` : `${rootName}/`,
130
+ });
131
+ return;
132
+ }
115
133
 
116
- await archive.finalize();
117
- await responsePromise;
134
+ for (const entry of dirEntries) {
135
+ const childAbsolutePath = join(directoryAbsolutePath, entry.name);
136
+ const childRelativePath = relativeDirectoryPath
137
+ ? `${relativeDirectoryPath}/${entry.name}`
138
+ : entry.name;
139
+
140
+ if (entry.isDirectory()) {
141
+ await visitDirectory(childAbsolutePath, childRelativePath);
142
+ continue;
143
+ }
144
+
145
+ if (!entry.isFile()) {
146
+ continue;
147
+ }
148
+
149
+ appendEntry();
150
+ archive.file(childAbsolutePath, {
151
+ name: `${rootName}/${childRelativePath}`,
152
+ });
153
+ }
154
+ };
155
+
156
+ await visitDirectory(rootAbsolutePath);
157
+ await archive.finalize();
158
+ await responsePromise;
159
+ } catch (error) {
160
+ archive.destroy(error);
161
+ await responsePromise.catch(() => {});
162
+ throw error;
163
+ }
118
164
  }
119
165
 
120
166
  // --- Route handlers ---
@@ -257,7 +303,7 @@ async function handleFileRead(req, res, requestUrl, { vaultFileStore }) {
257
303
  }
258
304
  }
259
305
 
260
- async function handleFileDownload(req, res, requestUrl, { vaultFileStore }) {
306
+ async function handleFileDownload(req, res, requestUrl, { config, vaultFileStore }) {
261
307
  const filePath = requestUrl.searchParams.get('path');
262
308
  if (!filePath) {
263
309
  jsonResponse(req, res, 400, { error: 'Missing path parameter' });
@@ -265,19 +311,25 @@ async function handleFileDownload(req, res, requestUrl, { vaultFileStore }) {
265
311
  }
266
312
 
267
313
  try {
268
- const download = await vaultFileStore.readDownloadFile(filePath);
314
+ const download = await vaultFileStore.openDownloadFileStream(filePath, {
315
+ maxBytes: config.maxDownloadFileBytes ?? DEFAULT_MAX_DOWNLOAD_FILE_BYTES,
316
+ });
269
317
  if (!download) {
270
318
  jsonResponse(req, res, 404, { error: 'File not found' });
271
319
  return;
272
320
  }
321
+ if (download.error) {
322
+ jsonResponse(req, res, download.statusCode || 400, { error: download.error });
323
+ return;
324
+ }
273
325
 
274
- sendResponse(req, res, {
275
- body: download.content,
326
+ await sendStreamResponse(req, res, {
276
327
  headers: createDownloadHeaders(
277
328
  basename(String(download.path ?? 'download')),
278
329
  download.mimeType || 'application/octet-stream',
279
330
  ),
280
331
  statusCode: 200,
332
+ stream: download.stream,
281
333
  });
282
334
  } catch (error) {
283
335
  console.error('[api] Failed to download file:', error.message);
@@ -285,7 +337,7 @@ async function handleFileDownload(req, res, requestUrl, { vaultFileStore }) {
285
337
  }
286
338
  }
287
339
 
288
- async function handleDirectoryDownload(req, res, requestUrl, { vaultFileStore }) {
340
+ async function handleDirectoryDownload(req, res, requestUrl, { config, vaultFileStore }) {
289
341
  const directoryPath = requestUrl.searchParams.get('path');
290
342
  if (!directoryPath) {
291
343
  jsonResponse(req, res, 400, { error: 'Missing path parameter' });
@@ -293,14 +345,23 @@ async function handleDirectoryDownload(req, res, requestUrl, { vaultFileStore })
293
345
  }
294
346
 
295
347
  try {
296
- const result = await vaultFileStore.listDirectoryEntriesForDownload(directoryPath);
348
+ const result = await vaultFileStore.resolveDirectoryDownloadRoot(directoryPath);
297
349
  if (!result.ok) {
298
350
  jsonResponse(req, res, result.error === 'Directory not found' ? 404 : 400, { error: result.error });
299
351
  return;
300
352
  }
353
+ const maxArchiveEntries = config.maxArchiveEntries ?? DEFAULT_MAX_ARCHIVE_ENTRIES;
354
+ const entryCount = await vaultFileStore.countDirectoryDownloadEntries(result.absolute, {
355
+ maxEntries: maxArchiveEntries,
356
+ });
357
+ if (!entryCount.withinLimit) {
358
+ jsonResponse(req, res, 413, { error: `Directory archive exceeds ${maxArchiveEntries} entries` });
359
+ return;
360
+ }
301
361
 
302
362
  await streamDirectoryArchive(req, res, {
303
- entries: result.entries,
363
+ maxEntries: maxArchiveEntries,
364
+ rootAbsolutePath: result.absolute,
304
365
  rootName: result.rootName || 'archive',
305
366
  });
306
367
  } catch (error) {
@@ -313,7 +374,7 @@ async function handleDirectoryDownload(req, res, requestUrl, { vaultFileStore })
313
374
  }
314
375
  }
315
376
 
316
- async function handleAttachmentRead(req, res, requestUrl, { vaultFileStore }) {
377
+ async function handleAttachmentRead(req, res, requestUrl, { config, vaultFileStore }) {
317
378
  const filePath = requestUrl.searchParams.get('path');
318
379
  if (!filePath) {
319
380
  jsonResponse(req, res, 400, { error: 'Missing path parameter' });
@@ -326,16 +387,22 @@ async function handleAttachmentRead(req, res, requestUrl, { vaultFileStore }) {
326
387
  }
327
388
 
328
389
  try {
329
- const attachment = await vaultFileStore.readImageAttachmentFile(filePath);
390
+ const attachment = await vaultFileStore.openImageAttachmentReadStream(filePath, {
391
+ maxBytes: config.maxDownloadFileBytes ?? DEFAULT_MAX_DOWNLOAD_FILE_BYTES,
392
+ });
330
393
  if (!attachment) {
331
394
  jsonResponse(req, res, 404, { error: 'Attachment not found' });
332
395
  return;
333
396
  }
397
+ if (attachment.error) {
398
+ jsonResponse(req, res, attachment.statusCode || 400, { error: attachment.error });
399
+ return;
400
+ }
334
401
 
335
- sendResponse(req, res, {
336
- body: attachment.content,
402
+ await sendStreamResponse(req, res, {
337
403
  headers: createAttachmentHeaders(attachment),
338
404
  statusCode: 200,
405
+ stream: attachment.stream,
339
406
  });
340
407
  } catch (error) {
341
408
  console.error('[api] Failed to read attachment:', error.message);
@@ -384,10 +451,17 @@ function createRouteTable(context) {
384
451
  export function createVaultApiQueryHandler({
385
452
  baseQueryService = null,
386
453
  backlinkIndex,
454
+ config = {},
387
455
  vaultFileStore,
388
456
  workspaceMutationCoordinator = null,
389
457
  }) {
390
- const context = { baseQueryService, backlinkIndex, vaultFileStore, workspaceMutationCoordinator };
458
+ const context = {
459
+ baseQueryService,
460
+ backlinkIndex,
461
+ config,
462
+ vaultFileStore,
463
+ workspaceMutationCoordinator,
464
+ };
391
465
  const routes = createRouteTable(context);
392
466
 
393
467
  return async function handleVaultApiQuery(req, res, requestUrl) {
@@ -1,3 +1,4 @@
1
+ import { createReadStream } from 'fs';
1
2
  import { mkdir, readFile, readdir, rename, rm, rmdir, stat, writeFile } from 'fs/promises';
2
3
  import { basename, dirname, extname, join, relative, resolve } from 'path';
3
4
  import sharp from 'sharp';
@@ -483,6 +484,36 @@ export class VaultFileStore {
483
484
  }
484
485
  }
485
486
 
487
+ async openImageAttachmentReadStream(filePath, { maxBytes = Infinity } = {}) {
488
+ const absolute = this.resolveContentPath(filePath, { requireVaultFile: false });
489
+ if (!absolute || !isImageAttachmentFilePath(filePath)) {
490
+ return null;
491
+ }
492
+
493
+ try {
494
+ const info = await stat(absolute);
495
+ if (!info.isFile()) {
496
+ return null;
497
+ }
498
+ if (Number(info.size || 0) > maxBytes) {
499
+ return { error: 'Attachment is too large', statusCode: 413 };
500
+ }
501
+
502
+ return {
503
+ mimeType: IMAGE_EXTENSION_TO_MIME_TYPE[extname(filePath).toLowerCase()] || 'application/octet-stream',
504
+ path: filePath,
505
+ size: Number(info.size || 0),
506
+ stream: createReadStream(absolute),
507
+ };
508
+ } catch (error) {
509
+ if (error.code === 'ENOENT') {
510
+ return null;
511
+ }
512
+
513
+ throw error;
514
+ }
515
+ }
516
+
486
517
  async writeMarkdownFile(filePath, content, options = {}) {
487
518
  return this.writeContentFile(filePath, content, 'markdown', options);
488
519
  }
@@ -602,6 +633,45 @@ export class VaultFileStore {
602
633
  }
603
634
  }
604
635
 
636
+ async openDownloadFileStream(filePath, { maxBytes = Infinity } = {}) {
637
+ const normalizedPath = String(filePath ?? '').replace(/\\/g, '/').trim();
638
+ if (!normalizedPath || !isVaultFilePath(normalizedPath)) {
639
+ return null;
640
+ }
641
+
642
+ if (isImageAttachmentFilePath(normalizedPath)) {
643
+ return this.openImageAttachmentReadStream(normalizedPath, { maxBytes });
644
+ }
645
+
646
+ const absolute = this.resolveContentPath(normalizedPath, { requireVaultFile: false });
647
+ if (!absolute) {
648
+ return null;
649
+ }
650
+
651
+ try {
652
+ const info = await stat(absolute);
653
+ if (!info.isFile()) {
654
+ return null;
655
+ }
656
+ if (Number(info.size || 0) > maxBytes) {
657
+ return { error: 'File is too large to download', statusCode: 413 };
658
+ }
659
+
660
+ return {
661
+ mimeType: getDownloadMimeType(normalizedPath),
662
+ path: normalizedPath,
663
+ size: Number(info.size || 0),
664
+ stream: createReadStream(absolute),
665
+ };
666
+ } catch (error) {
667
+ if (error.code === 'ENOENT') {
668
+ return null;
669
+ }
670
+
671
+ throw error;
672
+ }
673
+ }
674
+
605
675
  async persistCollaborationState(filePath, {
606
676
  commentThreads = [],
607
677
  content = '',
@@ -861,6 +931,72 @@ export class VaultFileStore {
861
931
  };
862
932
  }
863
933
 
934
+ async resolveDirectoryDownloadRoot(dirPath) {
935
+ const normalizedPath = String(dirPath ?? '').replace(/\\/g, '/').trim();
936
+ const { absolute, error } = resolveVaultDirectoryPath(this.vaultDir, normalizedPath);
937
+ if (!absolute) {
938
+ return { ok: false, error };
939
+ }
940
+
941
+ try {
942
+ const info = await stat(absolute);
943
+ if (!info.isDirectory()) {
944
+ return { ok: false, error: 'Directory not found' };
945
+ }
946
+ } catch (statError) {
947
+ if (statError.code === 'ENOENT') {
948
+ return { ok: false, error: 'Directory not found' };
949
+ }
950
+
951
+ throw statError;
952
+ }
953
+
954
+ return {
955
+ absolute,
956
+ ok: true,
957
+ rootName: basename(normalizedPath),
958
+ };
959
+ }
960
+
961
+ async countDirectoryDownloadEntries(directoryAbsolutePath, { maxEntries = Infinity } = {}) {
962
+ let count = 0;
963
+ const visitDirectory = async (currentDirectoryPath) => {
964
+ const dirEntries = sortDirectoryEntries(await readdir(currentDirectoryPath, { withFileTypes: true }))
965
+ .filter((entry) => !isIgnoredVaultEntry(entry.name));
966
+
967
+ if (dirEntries.length === 0) {
968
+ count += 1;
969
+ return count <= maxEntries;
970
+ }
971
+
972
+ for (const entry of dirEntries) {
973
+ const childAbsolutePath = join(currentDirectoryPath, entry.name);
974
+ if (entry.isDirectory()) {
975
+ const withinLimit = await visitDirectory(childAbsolutePath);
976
+ if (!withinLimit) {
977
+ return false;
978
+ }
979
+ continue;
980
+ }
981
+
982
+ if (entry.isFile()) {
983
+ count += 1;
984
+ if (count > maxEntries) {
985
+ return false;
986
+ }
987
+ }
988
+ }
989
+
990
+ return true;
991
+ };
992
+
993
+ const withinLimit = await visitDirectory(directoryAbsolutePath);
994
+ return {
995
+ count,
996
+ withinLimit,
997
+ };
998
+ }
999
+
864
1000
  async deleteFile(filePath) {
865
1001
  const { absolute, error } = resolveVaultFilePath(this.vaultDir, filePath);
866
1002
  if (!absolute) {