folderview 0.0.0-stage → 0.2.1

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 (450) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -2
  3. package/dist/cli.js +115 -0
  4. package/dist/client/assets/app.css +1 -0
  5. package/dist/client/assets/chunks/abap-EINPUMCJ.js +1 -0
  6. package/dist/client/assets/chunks/abnfDiagram-S7VZAUFO-W2CM2W6D.js +1 -0
  7. package/dist/client/assets/chunks/actionscript-3-O776AQUO.js +1 -0
  8. package/dist/client/assets/chunks/ada-WLOTXRTV.js +1 -0
  9. package/dist/client/assets/chunks/ahk-EF37A2DY.js +1 -0
  10. package/dist/client/assets/chunks/ahk2-FH4K3CKT.js +1 -0
  11. package/dist/client/assets/chunks/angular-html-ZALQFV2H.js +1 -0
  12. package/dist/client/assets/chunks/angular-ts-4IWMF7QO.js +1 -0
  13. package/dist/client/assets/chunks/apache-44AYOEWM.js +1 -0
  14. package/dist/client/assets/chunks/apex-XRYPID42.js +1 -0
  15. package/dist/client/assets/chunks/apl-5HBALFOI.js +1 -0
  16. package/dist/client/assets/chunks/applescript-2DZEHZEC.js +1 -0
  17. package/dist/client/assets/chunks/ara-ZGZQBLXL.js +1 -0
  18. package/dist/client/assets/chunks/architecture-WOLXFQ4H-EJGENNRL.js +1 -0
  19. package/dist/client/assets/chunks/architectureDiagram-YGSQEFNW-2VHWSKO6.js +36 -0
  20. package/dist/client/assets/chunks/asciidoc-GSQAKCPO.js +1 -0
  21. package/dist/client/assets/chunks/asm-BFURM5BK.js +1 -0
  22. package/dist/client/assets/chunks/astro-VJORWDZJ.js +1 -0
  23. package/dist/client/assets/chunks/awk-BSHTWXXX.js +1 -0
  24. package/dist/client/assets/chunks/ballerina-HAOQJ4B2.js +1 -0
  25. package/dist/client/assets/chunks/bat-OSH45EOF.js +1 -0
  26. package/dist/client/assets/chunks/beancount-RBATIHOV.js +1 -0
  27. package/dist/client/assets/chunks/berry-CZUIOUM4.js +1 -0
  28. package/dist/client/assets/chunks/bibtex-KCL2UWHE.js +1 -0
  29. package/dist/client/assets/chunks/bicep-SZJZB3SG.js +1 -0
  30. package/dist/client/assets/chunks/bird2-XBF5YQQV.js +1 -0
  31. package/dist/client/assets/chunks/blade-Q24LEVFC.js +1 -0
  32. package/dist/client/assets/chunks/blockDiagram-APKKGZFW-53LXSOOT.js +140 -0
  33. package/dist/client/assets/chunks/bpmnIcons-APHXDYXF-FMBRQNTY.js +1 -0
  34. package/dist/client/assets/chunks/bsl-TI44NWFB.js +1 -0
  35. package/dist/client/assets/chunks/c-NLHY4Y4N.js +1 -0
  36. package/dist/client/assets/chunks/c3-BUTEM6PO.js +1 -0
  37. package/dist/client/assets/chunks/c4Diagram-KJ2TOQU5-O53G4GQH.js +38 -0
  38. package/dist/client/assets/chunks/cadence-JIRTEU64.js +1 -0
  39. package/dist/client/assets/chunks/cairo-3W6NVDNW.js +1 -0
  40. package/dist/client/assets/chunks/chapel-BPBJ2E7K.js +1 -0
  41. package/dist/client/assets/chunks/chunk-25KVF5ZE.js +1 -0
  42. package/dist/client/assets/chunks/chunk-2CJKSQOI.js +1 -0
  43. package/dist/client/assets/chunks/chunk-2QIJRTWR.js +1 -0
  44. package/dist/client/assets/chunks/chunk-2ZCRJZGF.js +2 -0
  45. package/dist/client/assets/chunks/chunk-3ILNDZNY.js +213 -0
  46. package/dist/client/assets/chunks/chunk-3M2OOQ7S.js +1 -0
  47. package/dist/client/assets/chunks/chunk-3M57NTNK.js +1 -0
  48. package/dist/client/assets/chunks/chunk-3MR6DCZJ.js +1 -0
  49. package/dist/client/assets/chunks/chunk-3WGHS7F7.js +1 -0
  50. package/dist/client/assets/chunks/chunk-4JK75V33.js +1 -0
  51. package/dist/client/assets/chunks/chunk-4NXNVEWK.js +1 -0
  52. package/dist/client/assets/chunks/chunk-4RNNBDFQ.js +1 -0
  53. package/dist/client/assets/chunks/chunk-4T4NVH7V.js +1 -0
  54. package/dist/client/assets/chunks/chunk-4TFFBC35.js +1 -0
  55. package/dist/client/assets/chunks/chunk-4WYBE67P.js +1 -0
  56. package/dist/client/assets/chunks/chunk-56ITEUFA.js +1 -0
  57. package/dist/client/assets/chunks/chunk-5DXRE7IH.js +1 -0
  58. package/dist/client/assets/chunks/chunk-5IMADCNU.js +1 -0
  59. package/dist/client/assets/chunks/chunk-5XZPGKN4.js +1 -0
  60. package/dist/client/assets/chunks/chunk-6O32UKSP.js +1 -0
  61. package/dist/client/assets/chunks/chunk-7BAEB64Q.js +1 -0
  62. package/dist/client/assets/chunks/chunk-7FMWBHZO.js +1 -0
  63. package/dist/client/assets/chunks/chunk-7GFDITUT.js +1 -0
  64. package/dist/client/assets/chunks/chunk-7H4MLBHI.js +1 -0
  65. package/dist/client/assets/chunks/chunk-7OJ4MEEB.js +1 -0
  66. package/dist/client/assets/chunks/chunk-7RSEISFE.js +1 -0
  67. package/dist/client/assets/chunks/chunk-7YNZZQVT.js +1 -0
  68. package/dist/client/assets/chunks/chunk-AB7TIL2G.js +1 -0
  69. package/dist/client/assets/chunks/chunk-ADDF76FL.js +1 -0
  70. package/dist/client/assets/chunks/chunk-AF6UTCD4.js +321 -0
  71. package/dist/client/assets/chunks/chunk-AJ2K6ASU.js +1 -0
  72. package/dist/client/assets/chunks/chunk-AUBAENQZ.js +1 -0
  73. package/dist/client/assets/chunks/chunk-BJEYVZ65.js +1 -0
  74. package/dist/client/assets/chunks/chunk-BLRM6LXA.js +1 -0
  75. package/dist/client/assets/chunks/chunk-BOCTYCDZ.js +1 -0
  76. package/dist/client/assets/chunks/chunk-BU4VRYN7.js +1 -0
  77. package/dist/client/assets/chunks/chunk-CALYHV7D.js +1 -0
  78. package/dist/client/assets/chunks/chunk-CF4W3RZ4.js +1 -0
  79. package/dist/client/assets/chunks/chunk-DCZ2OBSL.js +1 -0
  80. package/dist/client/assets/chunks/chunk-DUFPBTHD.js +1 -0
  81. package/dist/client/assets/chunks/chunk-DUICIQBC.js +88 -0
  82. package/dist/client/assets/chunks/chunk-EH2N475V.js +1 -0
  83. package/dist/client/assets/chunks/chunk-EHLLNPZT.js +1 -0
  84. package/dist/client/assets/chunks/chunk-EIBFAW37.js +1 -0
  85. package/dist/client/assets/chunks/chunk-EO3ER3GN.js +1 -0
  86. package/dist/client/assets/chunks/chunk-FH24BIIA.js +1 -0
  87. package/dist/client/assets/chunks/chunk-FNOXGAL5.js +2 -0
  88. package/dist/client/assets/chunks/chunk-FTY6WLQF.js +3 -0
  89. package/dist/client/assets/chunks/chunk-FZFME6UK.js +1 -0
  90. package/dist/client/assets/chunks/chunk-FZJZZYYO.js +1 -0
  91. package/dist/client/assets/chunks/chunk-G4Z6L7OO.js +1 -0
  92. package/dist/client/assets/chunks/chunk-GNCEAN2B.js +1 -0
  93. package/dist/client/assets/chunks/chunk-GQJIKV3T.js +1 -0
  94. package/dist/client/assets/chunks/chunk-GUMNOKT2.js +1 -0
  95. package/dist/client/assets/chunks/chunk-GXRORAF3.js +1 -0
  96. package/dist/client/assets/chunks/chunk-H36ABDKY.js +1 -0
  97. package/dist/client/assets/chunks/chunk-HAR5NYVW.js +1 -0
  98. package/dist/client/assets/chunks/chunk-HIGPS4L2.js +1 -0
  99. package/dist/client/assets/chunks/chunk-HMOBGTME.js +1 -0
  100. package/dist/client/assets/chunks/chunk-HNO6SQ7M.js +1 -0
  101. package/dist/client/assets/chunks/chunk-HYVZ3GYN.js +1 -0
  102. package/dist/client/assets/chunks/chunk-I6TGFOQS.js +1 -0
  103. package/dist/client/assets/chunks/chunk-IHIOJK7I.js +1 -0
  104. package/dist/client/assets/chunks/chunk-IHJNUMSC.js +1 -0
  105. package/dist/client/assets/chunks/chunk-IP7PB2OC.js +1 -0
  106. package/dist/client/assets/chunks/chunk-ISDTMH2Y.js +1 -0
  107. package/dist/client/assets/chunks/chunk-J65M2LNT.js +1 -0
  108. package/dist/client/assets/chunks/chunk-JCWYZ7X2.js +62 -0
  109. package/dist/client/assets/chunks/chunk-JENWYTLI.js +1 -0
  110. package/dist/client/assets/chunks/chunk-JJ6YWJQS.js +1 -0
  111. package/dist/client/assets/chunks/chunk-JMUVGGX7.js +32 -0
  112. package/dist/client/assets/chunks/chunk-JNQUFMM4.js +1 -0
  113. package/dist/client/assets/chunks/chunk-JPTECOAL.js +1 -0
  114. package/dist/client/assets/chunks/chunk-JZNK4LKS.js +1 -0
  115. package/dist/client/assets/chunks/chunk-KPNXJBIW.js +1 -0
  116. package/dist/client/assets/chunks/chunk-KZO2GZL7.js +1 -0
  117. package/dist/client/assets/chunks/chunk-L2REESOQ.js +1 -0
  118. package/dist/client/assets/chunks/chunk-LJPJXR7R.js +1 -0
  119. package/dist/client/assets/chunks/chunk-LOEZMLCI.js +1 -0
  120. package/dist/client/assets/chunks/chunk-MLYC6FSE.js +1 -0
  121. package/dist/client/assets/chunks/chunk-MMDGEZYU.js +1 -0
  122. package/dist/client/assets/chunks/chunk-MYXE7CGE.js +1 -0
  123. package/dist/client/assets/chunks/chunk-NBH7YOBD.js +15 -0
  124. package/dist/client/assets/chunks/chunk-NCOVM3YF.js +1 -0
  125. package/dist/client/assets/chunks/chunk-NCWOBVU7.js +1 -0
  126. package/dist/client/assets/chunks/chunk-NLRIHHEH.js +1 -0
  127. package/dist/client/assets/chunks/chunk-NUPEOYK6.js +1 -0
  128. package/dist/client/assets/chunks/chunk-O4MVOA7X.js +1 -0
  129. package/dist/client/assets/chunks/chunk-OBHQQZ4H.js +1 -0
  130. package/dist/client/assets/chunks/chunk-OKPNJIIR.js +1 -0
  131. package/dist/client/assets/chunks/chunk-OLMPB57R.js +1 -0
  132. package/dist/client/assets/chunks/chunk-P2FYOAEV.js +1 -0
  133. package/dist/client/assets/chunks/chunk-PI3SNP4C.js +1 -0
  134. package/dist/client/assets/chunks/chunk-PRDTEROB.js +1 -0
  135. package/dist/client/assets/chunks/chunk-PZELHD2H.js +136 -0
  136. package/dist/client/assets/chunks/chunk-QR2Q34LM.js +1 -0
  137. package/dist/client/assets/chunks/chunk-R4GIXL6S.js +1 -0
  138. package/dist/client/assets/chunks/chunk-RORF4F5V.js +1 -0
  139. package/dist/client/assets/chunks/chunk-S73BYTLL.js +1 -0
  140. package/dist/client/assets/chunks/chunk-SFC5MRYR.js +1 -0
  141. package/dist/client/assets/chunks/chunk-SVM2H22K.js +2 -0
  142. package/dist/client/assets/chunks/chunk-SZ6UCGVD.js +1 -0
  143. package/dist/client/assets/chunks/chunk-SZYM6GIJ.js +127 -0
  144. package/dist/client/assets/chunks/chunk-TT5KJZKM.js +1 -0
  145. package/dist/client/assets/chunks/chunk-UFBKUYNH.js +1 -0
  146. package/dist/client/assets/chunks/chunk-UL57AD4T.js +1 -0
  147. package/dist/client/assets/chunks/chunk-UNEI7LMM.js +1 -0
  148. package/dist/client/assets/chunks/chunk-VNQBBFIY.js +1 -0
  149. package/dist/client/assets/chunks/chunk-WGVITPII.js +1 -0
  150. package/dist/client/assets/chunks/chunk-WRA6KJHX.js +10 -0
  151. package/dist/client/assets/chunks/chunk-WS5EHW43.js +1 -0
  152. package/dist/client/assets/chunks/chunk-XE6SLBBE.js +3 -0
  153. package/dist/client/assets/chunks/chunk-XF5T2P7U.js +72 -0
  154. package/dist/client/assets/chunks/chunk-XFRY5WKO.js +1 -0
  155. package/dist/client/assets/chunks/chunk-XWYV4OPA.js +1 -0
  156. package/dist/client/assets/chunks/chunk-YCE5ECKY.js +1 -0
  157. package/dist/client/assets/chunks/chunk-YIN5CSIF.js +1 -0
  158. package/dist/client/assets/chunks/chunk-YLFUX6XH.js +1 -0
  159. package/dist/client/assets/chunks/chunk-YNXJICZM.js +1 -0
  160. package/dist/client/assets/chunks/chunk-YOHF7NGD.js +1 -0
  161. package/dist/client/assets/chunks/chunk-YORIST7Z.js +1 -0
  162. package/dist/client/assets/chunks/chunk-YUL7CG4R.js +1 -0
  163. package/dist/client/assets/chunks/chunk-YUN7JSFJ.js +2 -0
  164. package/dist/client/assets/chunks/chunk-YZ7D2IT7.js +1 -0
  165. package/dist/client/assets/chunks/chunk-Z7LAPMFJ.js +1 -0
  166. package/dist/client/assets/chunks/chunk-ZE325LPC.js +1 -0
  167. package/dist/client/assets/chunks/chunk-ZN7TN6PO.js +1 -0
  168. package/dist/client/assets/chunks/chunk-ZST3EVPO.js +1 -0
  169. package/dist/client/assets/chunks/chunk-ZZHMDDZN.js +1 -0
  170. package/dist/client/assets/chunks/clarity-RKRYQ7WF.js +1 -0
  171. package/dist/client/assets/chunks/classDiagram-v2-L25IHNXF-Y67HVDZV.js +217 -0
  172. package/dist/client/assets/chunks/clojure-2X2DQECJ.js +1 -0
  173. package/dist/client/assets/chunks/cmake-LH5524XE.js +1 -0
  174. package/dist/client/assets/chunks/cobol-7DALQF3W.js +1 -0
  175. package/dist/client/assets/chunks/codeowners-52SZU5P5.js +1 -0
  176. package/dist/client/assets/chunks/codeql-EISH3KUP.js +1 -0
  177. package/dist/client/assets/chunks/coffee-VAWSGA7Y.js +1 -0
  178. package/dist/client/assets/chunks/common-lisp-4MVK2AN6.js +1 -0
  179. package/dist/client/assets/chunks/coq-4VRYZRZV.js +1 -0
  180. package/dist/client/assets/chunks/core-BRBMZSD2.js +12 -0
  181. package/dist/client/assets/chunks/cose-bilkent-JH36ORCC-BIOOHSAA.js +1 -0
  182. package/dist/client/assets/chunks/cpp-RFECMSKD.js +1 -0
  183. package/dist/client/assets/chunks/crystal-ZBGGFAOI.js +1 -0
  184. package/dist/client/assets/chunks/csharp-BZDPMKKW.js +1 -0
  185. package/dist/client/assets/chunks/css-3WWGM6UD.js +1 -0
  186. package/dist/client/assets/chunks/csv-AB4GNWSB.js +1 -0
  187. package/dist/client/assets/chunks/cue-2E46MZP2.js +1 -0
  188. package/dist/client/assets/chunks/cynefin-EF2NZ3EQ-TLFQLWUN.js +1 -0
  189. package/dist/client/assets/chunks/cynefinDiagram-L36ESFBY-FLPXCSSG.js +62 -0
  190. package/dist/client/assets/chunks/cypher-HR3ILFOL.js +1 -0
  191. package/dist/client/assets/chunks/d-TARSZPOL.js +1 -0
  192. package/dist/client/assets/chunks/dagre-SPEB5RXH-7EGEINJ2.js +4 -0
  193. package/dist/client/assets/chunks/dart-SALLJHVI.js +1 -0
  194. package/dist/client/assets/chunks/dax-WYMRGMXC.js +1 -0
  195. package/dist/client/assets/chunks/desktop-ND5CGLLJ.js +1 -0
  196. package/dist/client/assets/chunks/diagram-3B7GL73J-6USY4HZX.js +200 -0
  197. package/dist/client/assets/chunks/diagram-DNEMPZML-BNSOC4VW.js +24 -0
  198. package/dist/client/assets/chunks/diagram-L3MUGCDN-5V5FZYIG.js +41 -0
  199. package/dist/client/assets/chunks/diagram-LA4NIPWU-25VLY2CO.js +24 -0
  200. package/dist/client/assets/chunks/diagram-MHCOC4E2-G2LFP3JU.js +3 -0
  201. package/dist/client/assets/chunks/diagram-XPFHEONP-E6HBBIBV.js +30 -0
  202. package/dist/client/assets/chunks/diff-PWYGPM7B.js +1 -0
  203. package/dist/client/assets/chunks/docker-A4D7DT7W.js +1 -0
  204. package/dist/client/assets/chunks/dotenv-VWQWV2DY.js +1 -0
  205. package/dist/client/assets/chunks/dream-maker-M5YDQS35.js +1 -0
  206. package/dist/client/assets/chunks/ebnfDiagram-UZSYDL2S-BMXXZZPI.js +1 -0
  207. package/dist/client/assets/chunks/edge-3QXIDJHI.js +1 -0
  208. package/dist/client/assets/chunks/elixir-3OX2UHIZ.js +1 -0
  209. package/dist/client/assets/chunks/elk-XV6XMXQE-IPR65FON.js +27 -0
  210. package/dist/client/assets/chunks/elm-CTS65NCT.js +1 -0
  211. package/dist/client/assets/chunks/emacs-lisp-HHS2IDO7.js +1 -0
  212. package/dist/client/assets/chunks/engine-javascript-DNHSF67B.js +141 -0
  213. package/dist/client/assets/chunks/erDiagram-EYBAPDEH-3GEHFWHG.js +99 -0
  214. package/dist/client/assets/chunks/erb-ICYWK6HS.js +1 -0
  215. package/dist/client/assets/chunks/erlang-BDQPNCKX.js +1 -0
  216. package/dist/client/assets/chunks/eventmodeling-K75KTNOO-B27BJTZL.js +1 -0
  217. package/dist/client/assets/chunks/fennel-4ONBA2PH.js +1 -0
  218. package/dist/client/assets/chunks/fish-ATLPBZSD.js +1 -0
  219. package/dist/client/assets/chunks/flowDiagram-MALGAFMO-5PCLR4AE.js +1 -0
  220. package/dist/client/assets/chunks/fluent-GGMONBVW.js +1 -0
  221. package/dist/client/assets/chunks/fortran-fixed-form-5MSBDRWQ.js +1 -0
  222. package/dist/client/assets/chunks/fortran-free-form-TT3RBL75.js +1 -0
  223. package/dist/client/assets/chunks/fsharp-PAGHNW4O.js +1 -0
  224. package/dist/client/assets/chunks/ganttDiagram-23REXITY-VE5MVBTO.js +292 -0
  225. package/dist/client/assets/chunks/gdresource-5OTP326D.js +1 -0
  226. package/dist/client/assets/chunks/gdscript-VPWHSCLF.js +1 -0
  227. package/dist/client/assets/chunks/gdshader-FYLPXFE5.js +1 -0
  228. package/dist/client/assets/chunks/genie-UYMEEMA2.js +1 -0
  229. package/dist/client/assets/chunks/gherkin-SU7D5DMY.js +1 -0
  230. package/dist/client/assets/chunks/git-commit-5CF5QAG7.js +1 -0
  231. package/dist/client/assets/chunks/git-rebase-X72BAUZI.js +1 -0
  232. package/dist/client/assets/chunks/gitGraph-VSP46ZUC-J6CJ5YZF.js +1 -0
  233. package/dist/client/assets/chunks/gitGraphDiagram-STJD7YCW-7YJGT6ZP.js +106 -0
  234. package/dist/client/assets/chunks/gleam-LR42YRKO.js +1 -0
  235. package/dist/client/assets/chunks/glimmer-js-JPF67FJU.js +1 -0
  236. package/dist/client/assets/chunks/glimmer-ts-BYPAVIZT.js +1 -0
  237. package/dist/client/assets/chunks/glsl-GTGUULBN.js +1 -0
  238. package/dist/client/assets/chunks/gn-NMDQA4K3.js +1 -0
  239. package/dist/client/assets/chunks/gnuplot-UUAMYBCZ.js +1 -0
  240. package/dist/client/assets/chunks/go-4RERSTNP.js +1 -0
  241. package/dist/client/assets/chunks/graphql-5A6RK6YF.js +1 -0
  242. package/dist/client/assets/chunks/groovy-O7BJONCU.js +1 -0
  243. package/dist/client/assets/chunks/hack-7QGTNPDP.js +1 -0
  244. package/dist/client/assets/chunks/haml-ON5GAWN4.js +1 -0
  245. package/dist/client/assets/chunks/handlebars-6UOQSUBP.js +1 -0
  246. package/dist/client/assets/chunks/haskell-OIZXZ26Y.js +1 -0
  247. package/dist/client/assets/chunks/haxe-7FFVS4FO.js +1 -0
  248. package/dist/client/assets/chunks/hcl-XFUFQJHV.js +1 -0
  249. package/dist/client/assets/chunks/hjson-QDBHMTD5.js +1 -0
  250. package/dist/client/assets/chunks/hlsl-4LP25FFN.js +1 -0
  251. package/dist/client/assets/chunks/html-7RNQXAAV.js +1 -0
  252. package/dist/client/assets/chunks/html-derivative-ESOY72Q2.js +1 -0
  253. package/dist/client/assets/chunks/http-SAD4PMBD.js +1 -0
  254. package/dist/client/assets/chunks/hurl-ZSJJXPQH.js +1 -0
  255. package/dist/client/assets/chunks/hxml-HJCC37RK.js +1 -0
  256. package/dist/client/assets/chunks/hy-CLDE3P4K.js +1 -0
  257. package/dist/client/assets/chunks/imba-REB34U5V.js +1 -0
  258. package/dist/client/assets/chunks/info-OHQRW6UA-EQQKXR77.js +1 -0
  259. package/dist/client/assets/chunks/infoDiagram-KBXO4MBG-F6KGZCGS.js +2 -0
  260. package/dist/client/assets/chunks/ini-PDFEMGRA.js +1 -0
  261. package/dist/client/assets/chunks/ishikawaDiagram-M35NCXHK-5D6ZOZ34.js +70 -0
  262. package/dist/client/assets/chunks/java-KB6XENAD.js +1 -0
  263. package/dist/client/assets/chunks/javascript-3JX5T423.js +1 -0
  264. package/dist/client/assets/chunks/jinja-V4UNU6K4.js +1 -0
  265. package/dist/client/assets/chunks/jison-KH5XNLJP.js +1 -0
  266. package/dist/client/assets/chunks/journeyDiagram-OPLOXDXO-ANDOJZKW.js +139 -0
  267. package/dist/client/assets/chunks/json-HPXFRRPY.js +1 -0
  268. package/dist/client/assets/chunks/json5-LJM6TXWM.js +1 -0
  269. package/dist/client/assets/chunks/jsonc-UZWRU3JV.js +1 -0
  270. package/dist/client/assets/chunks/jsonl-RFZD5UN4.js +1 -0
  271. package/dist/client/assets/chunks/jsonnet-GFZABBL6.js +1 -0
  272. package/dist/client/assets/chunks/jssm-2YIGBISJ.js +1 -0
  273. package/dist/client/assets/chunks/jsx-KLV2ZWUT.js +1 -0
  274. package/dist/client/assets/chunks/julia-3TZDIPPO.js +1 -0
  275. package/dist/client/assets/chunks/just-UHCONBQS.js +1 -0
  276. package/dist/client/assets/chunks/kanban-definition-BZKZHSQY-GF5I5NVD.js +89 -0
  277. package/dist/client/assets/chunks/katex-PIN72PQZ.js +257 -0
  278. package/dist/client/assets/chunks/kdl-DARTFOCL.js +1 -0
  279. package/dist/client/assets/chunks/kotlin-7KT5DW6Y.js +1 -0
  280. package/dist/client/assets/chunks/kusto-EKYXZTC5.js +1 -0
  281. package/dist/client/assets/chunks/langs-GO3UQR4H.js +1 -0
  282. package/dist/client/assets/chunks/latex-T57MIHR5.js +1 -0
  283. package/dist/client/assets/chunks/lean-5QVPRMV6.js +1 -0
  284. package/dist/client/assets/chunks/less-K6QEH5TN.js +1 -0
  285. package/dist/client/assets/chunks/liquid-3LCNNEIV.js +1 -0
  286. package/dist/client/assets/chunks/llvm-O32ZIVRF.js +1 -0
  287. package/dist/client/assets/chunks/log-C4YPJ5BB.js +1 -0
  288. package/dist/client/assets/chunks/logo-JPMO43A6.js +1 -0
  289. package/dist/client/assets/chunks/lua-YKVSOG7K.js +1 -0
  290. package/dist/client/assets/chunks/luau-K4EECYSO.js +1 -0
  291. package/dist/client/assets/chunks/make-VHSYMHHW.js +1 -0
  292. package/dist/client/assets/chunks/markdown-GC5J46H4.js +1 -0
  293. package/dist/client/assets/chunks/marko-U45YFADC.js +1 -0
  294. package/dist/client/assets/chunks/matlab-AMOKC4LU.js +1 -0
  295. package/dist/client/assets/chunks/mdc-DIGA2UXP.js +1 -0
  296. package/dist/client/assets/chunks/mdx-BZUGCKQA.js +1 -0
  297. package/dist/client/assets/chunks/mermaid-WHDP6ZPA.js +1 -0
  298. package/dist/client/assets/chunks/mermaid.core-SQMPMUDN.js +10 -0
  299. package/dist/client/assets/chunks/mindmap-definition-I2QAS2VK-4NH5S56C.js +98 -0
  300. package/dist/client/assets/chunks/mipsasm-2S4CUGQV.js +1 -0
  301. package/dist/client/assets/chunks/mojo-ZOL2PNKT.js +1 -0
  302. package/dist/client/assets/chunks/moonbit-U3BDAIFE.js +1 -0
  303. package/dist/client/assets/chunks/move-YSXALBWA.js +1 -0
  304. package/dist/client/assets/chunks/narrat-DHFIAUJH.js +1 -0
  305. package/dist/client/assets/chunks/nextflow-ESAKNTI3.js +1 -0
  306. package/dist/client/assets/chunks/nextflow-groovy-FKDSPJUI.js +1 -0
  307. package/dist/client/assets/chunks/nginx-XYAYERLO.js +1 -0
  308. package/dist/client/assets/chunks/nim-XOWBVFDH.js +1 -0
  309. package/dist/client/assets/chunks/nix-RVSTQSHC.js +1 -0
  310. package/dist/client/assets/chunks/nsis-KMDUKQRL.js +1 -0
  311. package/dist/client/assets/chunks/nushell-SKJHHZ7U.js +1 -0
  312. package/dist/client/assets/chunks/objective-c-3B3KXQ7Q.js +1 -0
  313. package/dist/client/assets/chunks/objective-cpp-6JDULPJS.js +1 -0
  314. package/dist/client/assets/chunks/ocaml-TMRFXBVO.js +1 -0
  315. package/dist/client/assets/chunks/odin-IN3PLCK7.js +1 -0
  316. package/dist/client/assets/chunks/openscad-P52IKPNH.js +1 -0
  317. package/dist/client/assets/chunks/org-N3SJ2PIA.js +1 -0
  318. package/dist/client/assets/chunks/packet-JDAUHWVQ-4SWIKSKP.js +1 -0
  319. package/dist/client/assets/chunks/pascal-T7CUWCQ4.js +1 -0
  320. package/dist/client/assets/chunks/pegDiagram-FD4XPPHP-WDKZSEGN.js +1 -0
  321. package/dist/client/assets/chunks/perl-LCZ4KODH.js +1 -0
  322. package/dist/client/assets/chunks/php-4XHMBVR5.js +1 -0
  323. package/dist/client/assets/chunks/pie-XZMESJXO-PSSWOWGV.js +1 -0
  324. package/dist/client/assets/chunks/pieDiagram-J4AKIIQI-BDW5I5ON.js +39 -0
  325. package/dist/client/assets/chunks/pkl-OACG5B67.js +1 -0
  326. package/dist/client/assets/chunks/plsql-7E5C7CJX.js +1 -0
  327. package/dist/client/assets/chunks/po-ZSREFRRP.js +1 -0
  328. package/dist/client/assets/chunks/polar-4GBINVA7.js +1 -0
  329. package/dist/client/assets/chunks/postcss-B7RPVGFW.js +1 -0
  330. package/dist/client/assets/chunks/powerquery-6UFMTY4G.js +1 -0
  331. package/dist/client/assets/chunks/powershell-3STRPCNT.js +1 -0
  332. package/dist/client/assets/chunks/prisma-4TPSPIZ5.js +1 -0
  333. package/dist/client/assets/chunks/prolog-HEAYPKQW.js +1 -0
  334. package/dist/client/assets/chunks/proto-Y3Y3VKXM.js +1 -0
  335. package/dist/client/assets/chunks/pug-7PBPMG3M.js +1 -0
  336. package/dist/client/assets/chunks/puppet-FR35R4OI.js +1 -0
  337. package/dist/client/assets/chunks/purescript-OUHXQAOW.js +1 -0
  338. package/dist/client/assets/chunks/python-SNDG7FNU.js +1 -0
  339. package/dist/client/assets/chunks/qml-UVIOSWP4.js +1 -0
  340. package/dist/client/assets/chunks/qmldir-6JGUNYL5.js +1 -0
  341. package/dist/client/assets/chunks/qss-ZCC466IZ.js +1 -0
  342. package/dist/client/assets/chunks/quadrantDiagram-XTYBLID2-5VKXWVJ6.js +7 -0
  343. package/dist/client/assets/chunks/r-4XW4O3ZF.js +1 -0
  344. package/dist/client/assets/chunks/racket-YJAYXOOG.js +1 -0
  345. package/dist/client/assets/chunks/radar-ABXABTNO-3AXZCEY2.js +1 -0
  346. package/dist/client/assets/chunks/railroad-I3PHUGI6-2PLXYPOT.js +1 -0
  347. package/dist/client/assets/chunks/railroad-abnf-LNEFI6M7-M4DGICQX.js +1 -0
  348. package/dist/client/assets/chunks/railroad-ebnf-DWYD2UWJ-UEIHQMSZ.js +1 -0
  349. package/dist/client/assets/chunks/railroad-peg-JR7BVNR7-I7VLIBXM.js +1 -0
  350. package/dist/client/assets/chunks/railroadDiagram-MRQ6NCDL-Y6IYWEFL.js +1 -0
  351. package/dist/client/assets/chunks/raku-63NEL5JK.js +1 -0
  352. package/dist/client/assets/chunks/razor-J3CJP523.js +1 -0
  353. package/dist/client/assets/chunks/rbs-6GQLT7NW.js +1 -0
  354. package/dist/client/assets/chunks/reg-6Q3OEC2C.js +1 -0
  355. package/dist/client/assets/chunks/regexp-LPIMILT3.js +1 -0
  356. package/dist/client/assets/chunks/rel-TOQGUTHB.js +1 -0
  357. package/dist/client/assets/chunks/requirementDiagram-Y7LWUY3S-CIWXVVWJ.js +84 -0
  358. package/dist/client/assets/chunks/riscv-UELGBOXE.js +1 -0
  359. package/dist/client/assets/chunks/ron-FW7LCJJH.js +1 -0
  360. package/dist/client/assets/chunks/rosmsg-O63VHE3L.js +1 -0
  361. package/dist/client/assets/chunks/rst-3WJXGLA7.js +1 -0
  362. package/dist/client/assets/chunks/ruby-7S7L6JTH.js +1 -0
  363. package/dist/client/assets/chunks/rust-LIK4HRRI.js +1 -0
  364. package/dist/client/assets/chunks/sankeyDiagram-WGJFYUXA-OGNQPX5V.js +40 -0
  365. package/dist/client/assets/chunks/sas-FHDPZ2JH.js +1 -0
  366. package/dist/client/assets/chunks/sass-UI4HW2FN.js +1 -0
  367. package/dist/client/assets/chunks/scala-KN7RQSUJ.js +1 -0
  368. package/dist/client/assets/chunks/scheme-5R527YZ5.js +1 -0
  369. package/dist/client/assets/chunks/scss-KZ6DRE4A.js +1 -0
  370. package/dist/client/assets/chunks/sdbl-FHOCL5SL.js +1 -0
  371. package/dist/client/assets/chunks/sequenceDiagram-5NDRBG5M-QX2V24ZU.js +169 -0
  372. package/dist/client/assets/chunks/shaderlab-2TLHUS2H.js +1 -0
  373. package/dist/client/assets/chunks/shellscript-PIB6ZBDM.js +1 -0
  374. package/dist/client/assets/chunks/shellsession-5KL7NFNN.js +1 -0
  375. package/dist/client/assets/chunks/sizeCapture-INFHLROL-LCS2PIHM.js +1 -0
  376. package/dist/client/assets/chunks/smalltalk-JFLFU247.js +1 -0
  377. package/dist/client/assets/chunks/smithy-HONEXZAM.js +1 -0
  378. package/dist/client/assets/chunks/solidity-SJ3INJFZ.js +1 -0
  379. package/dist/client/assets/chunks/soy-ND4SWTM2.js +1 -0
  380. package/dist/client/assets/chunks/sparql-YB2JQBZD.js +1 -0
  381. package/dist/client/assets/chunks/splunk-UXIDDAWL.js +1 -0
  382. package/dist/client/assets/chunks/sql-K6OMG7SQ.js +1 -0
  383. package/dist/client/assets/chunks/ssh-config-I7KJY3KQ.js +1 -0
  384. package/dist/client/assets/chunks/stata-DNZ3DTFJ.js +1 -0
  385. package/dist/client/assets/chunks/stateDiagram-v2-FJ4FJNEA-LVAUWF3A.js +291 -0
  386. package/dist/client/assets/chunks/stylus-GWPR6IQY.js +1 -0
  387. package/dist/client/assets/chunks/surrealql-KQNHBWLC.js +1 -0
  388. package/dist/client/assets/chunks/svelte-YQKF25S4.js +1 -0
  389. package/dist/client/assets/chunks/swift-ZCIZUJXA.js +1 -0
  390. package/dist/client/assets/chunks/swimlanes-UMQ2AFYF-B3RQOARQ.js +1 -0
  391. package/dist/client/assets/chunks/swimlanesDiagram-OMPLPG4O-JOE33EOV.js +8 -0
  392. package/dist/client/assets/chunks/system-verilog-Y7E6FIO3.js +1 -0
  393. package/dist/client/assets/chunks/systemd-WHC7NBZP.js +1 -0
  394. package/dist/client/assets/chunks/talonscript-WC7SODAH.js +1 -0
  395. package/dist/client/assets/chunks/tasl-QHY2GTCB.js +1 -0
  396. package/dist/client/assets/chunks/tcl-VAMIL5PJ.js +1 -0
  397. package/dist/client/assets/chunks/temml-ISNSX3VV.js +96 -0
  398. package/dist/client/assets/chunks/templ-B3BHKWV4.js +1 -0
  399. package/dist/client/assets/chunks/terraform-Q4UEK6FK.js +1 -0
  400. package/dist/client/assets/chunks/tex-63OWCYAB.js +1 -0
  401. package/dist/client/assets/chunks/timeline-definition-SEUHKTQE-FDFTVAJG.js +120 -0
  402. package/dist/client/assets/chunks/toml-7Z4IPGES.js +1 -0
  403. package/dist/client/assets/chunks/treeView-D4JQ5SDB-ABSGRAGI.js +1 -0
  404. package/dist/client/assets/chunks/treemap-SAJKECNS-MYX2CBJN.js +1 -0
  405. package/dist/client/assets/chunks/ts-tags-DT62HE52.js +1 -0
  406. package/dist/client/assets/chunks/tsv-RTXHWADP.js +1 -0
  407. package/dist/client/assets/chunks/tsx-UANUBOC7.js +1 -0
  408. package/dist/client/assets/chunks/turtle-QVOUNUUL.js +1 -0
  409. package/dist/client/assets/chunks/twig-ZPC4BPDA.js +1 -0
  410. package/dist/client/assets/chunks/typescript-WGWKIMMW.js +1 -0
  411. package/dist/client/assets/chunks/typespec-TYCVR32J.js +1 -0
  412. package/dist/client/assets/chunks/typst-PIBFYUO6.js +1 -0
  413. package/dist/client/assets/chunks/usecaseDiagram-AFA6YXN4-KW7ADINR.js +370 -0
  414. package/dist/client/assets/chunks/v-3ERNWBSP.js +1 -0
  415. package/dist/client/assets/chunks/vala-UCBCT2GS.js +1 -0
  416. package/dist/client/assets/chunks/vb-DGI3VTPA.js +1 -0
  417. package/dist/client/assets/chunks/vennDiagram-NNMJR3RB-RDGLGOGM.js +34 -0
  418. package/dist/client/assets/chunks/verilog-G72NZROS.js +1 -0
  419. package/dist/client/assets/chunks/vhdl-PYGA77XE.js +1 -0
  420. package/dist/client/assets/chunks/viml-OOC5B5UU.js +1 -0
  421. package/dist/client/assets/chunks/vue-GTNHIOPG.js +1 -0
  422. package/dist/client/assets/chunks/vue-html-DKAMJV55.js +1 -0
  423. package/dist/client/assets/chunks/vue-vine-WTM5HREV.js +1 -0
  424. package/dist/client/assets/chunks/vyper-RHYEHOJM.js +1 -0
  425. package/dist/client/assets/chunks/wardley-7MLQ67FV-QJL2HSBT.js +1 -0
  426. package/dist/client/assets/chunks/wardleyDiagram-S7YFUSEH-A4XX3UJR.js +78 -0
  427. package/dist/client/assets/chunks/wasm-ZMM2Z5XV.js +1 -0
  428. package/dist/client/assets/chunks/wenyan-HF5UTKSJ.js +1 -0
  429. package/dist/client/assets/chunks/wgsl-O7D7OPO7.js +1 -0
  430. package/dist/client/assets/chunks/wikitext-T7BSWSKQ.js +1 -0
  431. package/dist/client/assets/chunks/wit-TKCHPPT6.js +1 -0
  432. package/dist/client/assets/chunks/wolfram-7MN66A7N.js +1 -0
  433. package/dist/client/assets/chunks/xml-NLTLAWOD.js +1 -0
  434. package/dist/client/assets/chunks/xsl-NZ4ECK3P.js +1 -0
  435. package/dist/client/assets/chunks/xychartDiagram-GGNKY7IX-OXJILWHR.js +7 -0
  436. package/dist/client/assets/chunks/yaml-HRJLEV3V.js +1 -0
  437. package/dist/client/assets/chunks/zenscript-4LLRSLPI.js +1 -0
  438. package/dist/client/assets/chunks/zig-Y7MUQFUO.js +1 -0
  439. package/dist/client/assets/main.js +186 -0
  440. package/dist/client/index.html +48 -0
  441. package/dist/config.js +322 -0
  442. package/dist/files.js +178 -0
  443. package/dist/git.js +365 -0
  444. package/dist/glob.js +89 -0
  445. package/dist/review.js +290 -0
  446. package/dist/search.js +136 -0
  447. package/dist/server.js +482 -0
  448. package/dist/watch.js +69 -0
  449. package/dist/zip.js +100 -0
  450. package/package.json +61 -4
package/dist/server.js ADDED
@@ -0,0 +1,482 @@
1
+ // The HTTP server: the page, the folder API, git, search, review comments and live-reload events.
2
+ import http from 'node:http';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import crypto from 'node:crypto';
8
+ import { NOISE, RAW_TYPES, inside, isHidden, list, serveRaw, toRel } from './files.js';
9
+ import { excluder, parseRules } from './glob.js';
10
+ import { writeZip, ZIP_LIMITS } from './zip.js';
11
+ import { Git } from './git.js';
12
+ import { FileIndex } from './search.js';
13
+ import { Review, agentPrompt } from './review.js';
14
+ import { watchTree } from './watch.js';
15
+ const here = path.dirname(fileURLToPath(import.meta.url));
16
+ const clientDir = path.resolve(here, 'client');
17
+ const version = readVersion();
18
+ const STATIC = {
19
+ '.html': 'text/html; charset=utf-8',
20
+ '.js': 'text/javascript; charset=utf-8',
21
+ '.css': 'text/css; charset=utf-8',
22
+ '.svg': 'image/svg+xml',
23
+ '.woff2': 'font/woff2',
24
+ '.woff': 'font/woff',
25
+ '.ttf': 'font/ttf',
26
+ };
27
+ export async function start(opts) {
28
+ const root = path.resolve(opts.root);
29
+ const scope = {
30
+ root,
31
+ hidden: new Set(['.git', ...(opts.hide ?? [])]),
32
+ hideSecrets: !opts.showSecrets,
33
+ excluded: excluder(parseRules(opts.exclude ?? [])),
34
+ };
35
+ const git = opts.git ? await Git.open(root) : undefined;
36
+ const base = opts.base ?? '/';
37
+ const token = opts.token ?? '';
38
+ const maxFile = (opts.maxFileMb ?? 5) * 1048576;
39
+ const index = new FileIndex(scope, git, (opts.maxFileMb ?? 5) * 1048576);
40
+ const review = new Review(path.resolve(root, opts.reviewDir), root);
41
+ const reviewRel = toRel(scope, review.jsonPath);
42
+ const author = opts.author || (git && (await git.userName())) || os.userInfo().username || 'reviewer';
43
+ const visible = (rel) => !rel.split('/').some((p) => isHidden(scope, p)) && !scope.excluded(rel);
44
+ // ── live events ──
45
+ const clients = new Set();
46
+ const broadcast = (type, data) => {
47
+ const msg = `event: ${type}\ndata: ${JSON.stringify(data)}\n\n`;
48
+ for (const c of clients)
49
+ c.write(msg);
50
+ };
51
+ let status;
52
+ // the review notes are ours, not the project's: keep them out of the changes
53
+ const reviewPrefix = reviewRel.startsWith('..') ? '' : toRel(scope, review.dir) + '/';
54
+ const gitStatus = () => (status ??= git
55
+ ? git.status().then((s) => ({
56
+ ...s,
57
+ changes: s.changes.filter((c) => (!reviewPrefix || !c.path.startsWith(reviewPrefix)) && visible(c.path)),
58
+ ignored: s.ignored.filter((i) => visible(i.replace(/\/$/, ''))),
59
+ }))
60
+ : Promise.reject(new Error('not a git repository')));
61
+ const stopWatch = opts.watch
62
+ ? watchTree(root, git ? path.join(git.top, '.git') : undefined, (b) => {
63
+ if (b.git)
64
+ status = undefined;
65
+ if (b.folders.length || b.files.length)
66
+ index.invalidate();
67
+ broadcast('change', b);
68
+ })
69
+ : () => undefined;
70
+ review.onChange(() => {
71
+ broadcast('comments', { count: review.all().length });
72
+ opts.onReviewChange?.(review.all().filter((c) => c.status === 'open').length, review.all().length);
73
+ });
74
+ review.watch();
75
+ const ping = setInterval(() => {
76
+ for (const c of clients)
77
+ c.write(': ping\n\n');
78
+ }, 25_000);
79
+ ping.unref();
80
+ const allowed = allowedHosts(opts);
81
+ const server = http.createServer((req, res) => {
82
+ handle(req, res).catch((err) => {
83
+ if (!res.headersSent)
84
+ json(res, 500, { error: err instanceof Error ? err.message : String(err) });
85
+ else
86
+ res.end();
87
+ });
88
+ });
89
+ async function handle(req, res) {
90
+ // DNS rebinding: a page on another site that resolves its name to this machine is refused
91
+ const host = (req.headers.host ?? '').replace(/:\d+$/, '').replace(/^\[|\]$/g, '').toLowerCase();
92
+ if (!allowed(host))
93
+ return json(res, 403, { error: `Host "${host}" is not allowed. Start with --allow-host ${host} to allow it.` });
94
+ const url = new URL(req.url ?? '/', 'http://x');
95
+ // served under a path (behind a proxy): everything lives below it
96
+ if (base !== '/') {
97
+ if (url.pathname === base.slice(0, -1))
98
+ return void res.writeHead(301, { location: base + url.search }).end();
99
+ if (!url.pathname.startsWith(base))
100
+ return json(res, 404, { error: 'not found' });
101
+ }
102
+ const p = '/' + url.pathname.slice(base.length);
103
+ // a token: once in the address (it becomes a cookie), as a bearer header, or the cookie
104
+ if (token) {
105
+ const given = url.searchParams.get('token') ?? bearer(req) ?? cookie(req, 'folderview_token') ?? '';
106
+ if (!same(given, token)) {
107
+ if (p === '/' || p === '/index.html')
108
+ return void res.writeHead(401, { 'content-type': 'text/html; charset=utf-8' }).end(LOCKED);
109
+ return json(res, 401, { error: 'This folderview needs its token: open the link with ?token=… once.' });
110
+ }
111
+ if (url.searchParams.has('token')) {
112
+ // keep it as a cookie and take it out of the address bar
113
+ url.searchParams.delete('token');
114
+ res.setHeader('set-cookie', `folderview_token=${encodeURIComponent(token)}; Path=${base}; HttpOnly; SameSite=Strict; Max-Age=31536000`);
115
+ if (p === '/' || p === '/index.html')
116
+ return void res.writeHead(302, { location: base + (url.search || '') }).end();
117
+ }
118
+ }
119
+ const q = (k) => url.searchParams.get(k) ?? '';
120
+ const method = req.method ?? 'GET';
121
+ if (method !== 'GET' && method !== 'HEAD') {
122
+ // writes come only from this page: a JSON body and our own origin
123
+ const origin = req.headers.origin;
124
+ if (origin && new URL(origin).host !== req.headers.host)
125
+ return json(res, 403, { error: 'Cross-origin write refused' });
126
+ if (opts.readOnly)
127
+ return json(res, 403, { error: 'This folderview is read-only' });
128
+ }
129
+ if (p === '/' || p === '/index.html')
130
+ return sendStatic(res, path.join(clientDir, 'index.html'));
131
+ if (p.startsWith('/assets/')) {
132
+ const abs = path.resolve(clientDir, '.' + p);
133
+ if (!abs.startsWith(clientDir + path.sep))
134
+ return json(res, 404, { error: 'not found' });
135
+ return sendStatic(res, abs);
136
+ }
137
+ if (p.startsWith('/raw/')) {
138
+ const rel = decodeURIComponent(p.slice(5));
139
+ if (q('download') === '1')
140
+ res.setHeader('content-disposition', disposition(path.basename(rel)));
141
+ return serveRaw(scope, rel, req, res, q('as') === 'page');
142
+ }
143
+ if (p === '/api/zip')
144
+ return zipFolder(q('p'), res);
145
+ if (p === '/api/info') {
146
+ return json(res, 200, {
147
+ name: opts.title || path.basename(root) || root,
148
+ // the home folder as ~, so a shared screen or screenshot doesn't show the user's account name
149
+ root: root.startsWith(os.homedir()) ? '~' + root.slice(os.homedir().length) : root,
150
+ version,
151
+ git: !!git,
152
+ gitPrefix: git?.prefix ?? '',
153
+ author,
154
+ readOnly: opts.readOnly,
155
+ review: {
156
+ json: reviewRel,
157
+ md: toRel(scope, review.mdPath),
158
+ dir: opts.reviewLabel ?? opts.reviewDir,
159
+ prompt: agentPrompt(toRel(scope, review.mdPath), reviewRel),
160
+ },
161
+ hostname: os.hostname(),
162
+ maxFile,
163
+ token: !!token,
164
+ });
165
+ }
166
+ if (p === '/api/list') {
167
+ const entries = list(scope, q('p'));
168
+ return entries ? json(res, 200, { entries }) : json(res, 404, { error: 'No such folder' });
169
+ }
170
+ if (p === '/api/stat') {
171
+ const abs = inside(scope, q('p'));
172
+ if (!abs || !fs.existsSync(abs))
173
+ return json(res, 404, { error: 'not found' });
174
+ const st = fs.statSync(abs);
175
+ return json(res, 200, { type: st.isDirectory() ? 'folder' : 'file', size: st.size, mtime: st.mtimeMs });
176
+ }
177
+ if (p === '/api/files')
178
+ return json(res, 200, { files: await index.files() });
179
+ if (p === '/api/search') {
180
+ if (!q('q'))
181
+ return json(res, 400, { error: 'Nothing to search for' });
182
+ const result = await index.search({
183
+ q: q('q'),
184
+ scope: q('scope'),
185
+ regex: q('regex') === '1',
186
+ caseSensitive: q('case') === '1',
187
+ word: q('word') === '1',
188
+ include: q('include'),
189
+ });
190
+ // the review notes would match every word that was commented on: leave them out
191
+ if (reviewPrefix)
192
+ result.files = result.files.filter((f) => !f.path.startsWith(reviewPrefix));
193
+ return json(res, 200, result);
194
+ }
195
+ if (p.startsWith('/api/git/')) {
196
+ if (!git)
197
+ return json(res, 404, { error: 'Not a git repository' });
198
+ const rel = q('p');
199
+ if (p !== '/api/git/status' && inside(scope, rel) === undefined)
200
+ return json(res, 404, { error: 'not found' });
201
+ switch (p) {
202
+ case '/api/git/status':
203
+ return json(res, 200, await gitStatus());
204
+ case '/api/git/diff':
205
+ return text(res, await git.diff(rel));
206
+ case '/api/git/show': {
207
+ const rev = q('rev') || 'HEAD';
208
+ if (!isRev(rev))
209
+ return json(res, 400, { error: 'bad revision' });
210
+ return text(res, await git.show(rel, rev));
211
+ }
212
+ case '/api/git/raw': {
213
+ const rev = q('rev') || 'HEAD';
214
+ if (!isRev(rev))
215
+ return json(res, 400, { error: 'bad revision' });
216
+ const buf = await git.showRaw(rel, rev);
217
+ const ext = path.extname(rel).slice(1).toLowerCase();
218
+ res.writeHead(200, { 'content-type': RAW_TYPES[ext] ?? 'application/octet-stream', 'cache-control': 'no-store', 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'", 'x-content-type-options': 'nosniff' });
219
+ return void res.end(buf);
220
+ }
221
+ case '/api/git/log':
222
+ {
223
+ // a page of the file's commits, and whether there are more
224
+ const all = await git.log(rel, 31, Math.max(0, Number(q('skip')) || 0));
225
+ return json(res, 200, { commits: all.slice(0, 30), more: all.length > 30 });
226
+ }
227
+ case '/api/git/history':
228
+ return json(res, 200, await git.history(Math.max(0, Number(q('skip')) || 0), 50, q('q').trim().slice(0, 200)));
229
+ case '/api/git/commit-info': {
230
+ const h = q('h');
231
+ if (!isRev(h))
232
+ return json(res, 400, { error: 'bad revision' });
233
+ const info = await git.commitInfo(h);
234
+ info.files = info.files.filter((f) => visible(f.path) && (!reviewPrefix || !f.path.startsWith(reviewPrefix)));
235
+ return json(res, 200, info);
236
+ }
237
+ case '/api/git/commit-diff': {
238
+ const h = q('h');
239
+ const from = q('from');
240
+ if (!isRev(h))
241
+ return json(res, 400, { error: 'bad revision' });
242
+ if (from && inside(scope, from) === undefined)
243
+ return json(res, 404, { error: 'not found' });
244
+ return text(res, await git.commitFileDiff(h, rel, from || undefined));
245
+ }
246
+ case '/api/git/commit': {
247
+ const h = q('h');
248
+ if (!isRev(h))
249
+ return json(res, 400, { error: 'bad revision' });
250
+ return text(res, await git.commitDiff(rel, h));
251
+ }
252
+ }
253
+ return json(res, 404, { error: 'not found' });
254
+ }
255
+ if (p === '/api/review.md') {
256
+ return text(res, fs.existsSync(review.mdPath) ? fs.readFileSync(review.mdPath, 'utf8') : 'No comments yet.\n', 'text/markdown; charset=utf-8');
257
+ }
258
+ if (p === '/api/comments' && method === 'GET')
259
+ return json(res, 200, { comments: review.all() });
260
+ const img = /^\/api\/comments\/([0-9a-f]+)\/image\.png$/.exec(p);
261
+ if (img) {
262
+ const abs = review.imagePath(img[1] ?? '');
263
+ if (!abs)
264
+ return json(res, 404, { error: 'not found' });
265
+ res.writeHead(200, { 'content-type': 'image/png', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff' });
266
+ return void fs.createReadStream(abs).pipe(res);
267
+ }
268
+ if (p === '/api/comments' && method === 'POST') {
269
+ const b = await body(req, 16_000_000);
270
+ const rel = str(b['path']);
271
+ if (inside(scope, rel) === undefined)
272
+ return json(res, 400, { error: 'No such file' });
273
+ if (!str(b['body']).trim())
274
+ return json(res, 400, { error: 'The comment is empty' });
275
+ const line = num(b['line']), endLine = num(b['endLine']);
276
+ const c = review.add({
277
+ path: rel,
278
+ body: str(b['body']),
279
+ author: str(b['author']) || author,
280
+ ...(line ? { line } : {}),
281
+ ...(endLine ? { endLine } : {}),
282
+ ...imageMarks(b),
283
+ ...(git ? { commit: (await gitStatus().catch(() => undefined))?.head ?? '' } : {}),
284
+ });
285
+ return json(res, 201, c);
286
+ }
287
+ if (p === '/api/comments' && method === 'DELETE' && q('resolved') === '1')
288
+ return json(res, 200, { removed: review.clearResolved() });
289
+ const m = /^\/api\/comments\/([0-9a-f]+)(\/replies)?$/.exec(p);
290
+ if (m) {
291
+ const id = m[1] ?? '';
292
+ if (m[2] && method === 'POST') {
293
+ const b = await body(req);
294
+ const c = review.reply(id, str(b['author']) || author, str(b['body']));
295
+ return c ? json(res, 200, c) : json(res, 404, { error: 'not found' });
296
+ }
297
+ if (method === 'PATCH') {
298
+ const b = await body(req);
299
+ const s = str(b['status']);
300
+ const patch = {};
301
+ if (typeof b['body'] === 'string')
302
+ patch.body = b['body'];
303
+ if (s === 'open' || s === 'resolved')
304
+ patch.status = s;
305
+ const c = review.update(id, patch);
306
+ return c ? json(res, 200, c) : json(res, 404, { error: 'not found' });
307
+ }
308
+ if (method === 'DELETE')
309
+ return review.remove(id) ? json(res, 200, { ok: true }) : json(res, 404, { error: 'not found' });
310
+ }
311
+ if (p === '/api/events') {
312
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store', connection: 'keep-alive' });
313
+ res.write(`event: hello\ndata: {"watch":${opts.watch}}\n\n`);
314
+ clients.add(res);
315
+ req.on('close', () => clients.delete(res));
316
+ return;
317
+ }
318
+ json(res, 404, { error: 'not found' });
319
+ }
320
+ /** a folder as a zip: what the listing shows (no hidden, excluded or noise folders), within the zip limits */
321
+ async function zipFolder(rel, res) {
322
+ const abs = inside(scope, rel);
323
+ if (!abs || !fs.existsSync(abs) || !fs.statSync(abs).isDirectory())
324
+ return json(res, 404, { error: 'No such folder' });
325
+ const name = rel ? path.basename(abs) : path.basename(root) || 'folder';
326
+ const files = [];
327
+ let bytes = 0;
328
+ const stack = [rel];
329
+ while (stack.length) {
330
+ const dir = stack.pop() ?? '';
331
+ for (const e of list(scope, dir) ?? []) {
332
+ const child = dir ? dir + '/' + e.name : e.name;
333
+ if (e.type === 'folder') {
334
+ if (!NOISE.has(e.name))
335
+ stack.push(child);
336
+ continue;
337
+ }
338
+ bytes += e.size;
339
+ files.push({ abs: path.join(root, child), name: path.posix.join(name, rel ? child.slice(rel.length + 1) : child) });
340
+ }
341
+ if (files.length > ZIP_LIMITS.files || bytes > ZIP_LIMITS.bytes)
342
+ return json(res, 413, { error: `Too big to zip here (${files.length.toLocaleString()} files, ${Math.round(bytes / 1048576)} MB so far)` });
343
+ }
344
+ files.sort((a, b) => a.name.localeCompare(b.name));
345
+ res.writeHead(200, { 'content-type': 'application/zip', 'content-disposition': disposition(name + '.zip'), 'cache-control': 'no-store' });
346
+ await writeZip(res, files);
347
+ res.end();
348
+ }
349
+ const port = await listen(server, opts.port, opts.host);
350
+ return {
351
+ server,
352
+ port,
353
+ close: async () => {
354
+ stopWatch();
355
+ review.close();
356
+ clearInterval(ping);
357
+ for (const c of clients)
358
+ c.end();
359
+ await new Promise((r) => server.close(() => r()));
360
+ },
361
+ };
362
+ }
363
+ /** listen on the port, or the next free one */
364
+ function listen(server, port, host, tries = 20) {
365
+ return new Promise((resolve, reject) => {
366
+ const onError = (err) => {
367
+ server.off('listening', onListening);
368
+ if (err.code === 'EADDRINUSE' && tries > 1 && port !== 0)
369
+ resolve(listen(server, port + 1, host, tries - 1));
370
+ else
371
+ reject(err);
372
+ };
373
+ const onListening = () => {
374
+ server.off('error', onError);
375
+ const a = server.address();
376
+ resolve(typeof a === 'object' && a ? a.port : port);
377
+ };
378
+ server.once('error', onError);
379
+ server.once('listening', onListening);
380
+ server.listen(port, host);
381
+ });
382
+ }
383
+ const LOCKED = '<!doctype html><meta name="viewport" content="width=device-width"><title>folderview</title><body style="font:16px system-ui;padding:2em;max-width:36em;margin:auto"><h1>🔒 This folderview needs its token</h1><p>Open the link you were given (it ends in <code>?token=…</code>). It is kept as a cookie, so you only need it once.</p>';
384
+ const bearer = (req) => /^Bearer (.+)$/i.exec(String(req.headers.authorization ?? ''))?.[1];
385
+ const cookie = (req, name) => {
386
+ for (const part of String(req.headers.cookie ?? '').split(';')) {
387
+ const [k, ...v] = part.trim().split('=');
388
+ if (k === name)
389
+ return decodeURIComponent(v.join('='));
390
+ }
391
+ return undefined;
392
+ };
393
+ function same(a, b) {
394
+ const x = crypto.createHash('sha256').update(a).digest();
395
+ const y = crypto.createHash('sha256').update(b).digest();
396
+ return crypto.timingSafeEqual(x, y);
397
+ }
398
+ const disposition = (name) => `attachment; filename="${name.replace(/[^\x20-\x7e]|["\\]/g, '_')}"; filename*=UTF-8''${encodeURIComponent(name)}`;
399
+ function allowedHosts(opts) {
400
+ const names = new Set(['localhost', os.hostname().toLowerCase(), ...opts.allowHosts.map((h) => h.toLowerCase())]);
401
+ if (opts.host !== '0.0.0.0' && opts.host !== '::')
402
+ names.add(opts.host.toLowerCase());
403
+ return (h) => names.has(h) ||
404
+ h.endsWith('.localhost') ||
405
+ /^\d{1,3}(\.\d{1,3}){3}$/.test(h) || // an IP address can't be rebound
406
+ h.includes(':') || // IPv6 literal
407
+ opts.allowHosts.includes('*');
408
+ }
409
+ function isRev(r) {
410
+ return /^[0-9a-f]{4,40}$/i.test(r) || /^HEAD(~\d{1,4}|\^)?$/.test(r);
411
+ }
412
+ function sendStatic(res, abs) {
413
+ fs.readFile(abs, (err, buf) => {
414
+ if (err)
415
+ return json(res, 404, { error: 'not found' });
416
+ res.writeHead(200, { 'content-type': STATIC[path.extname(abs)] ?? 'application/octet-stream', 'cache-control': 'no-store' }).end(buf);
417
+ });
418
+ }
419
+ function json(res, code, data) {
420
+ res.writeHead(code, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }).end(JSON.stringify(data));
421
+ }
422
+ function text(res, body, type = 'text/plain; charset=utf-8') {
423
+ res.writeHead(200, { 'content-type': type, 'cache-control': 'no-store' }).end(body);
424
+ }
425
+ /** an image comment's marks: the area, the strokes, and the marked-up picture (a PNG data URL) */
426
+ function imageMarks(b) {
427
+ const out = {};
428
+ const a = b['area'];
429
+ if (a && typeof a === 'object' && !Array.isArray(a)) {
430
+ const r = Object.fromEntries(Object.entries(a));
431
+ const n = (k) => (typeof r[k] === 'number' && Number.isFinite(r[k]) && Number(r[k]) >= 0 ? Math.round(Number(r[k])) : undefined);
432
+ const [x, y, w, h] = [n('x'), n('y'), n('w'), n('h')];
433
+ if (x !== undefined && y !== undefined && w && h)
434
+ out.area = { x, y, w, h };
435
+ }
436
+ const s = b['strokes'];
437
+ if (Array.isArray(s)) {
438
+ let budget = 20_000;
439
+ const strokes = s
440
+ .filter((st) => Array.isArray(st))
441
+ .map((st) => st.filter((v) => typeof v === 'number' && v >= 0 && v <= 1).map((v) => Math.round(v * 10000) / 10000))
442
+ .filter((st) => st.length >= 2 && st.length % 2 === 0 && (budget -= st.length) >= 0);
443
+ if (strokes.length)
444
+ out.strokes = strokes;
445
+ }
446
+ const snap = typeof b['snapshot'] === 'string' ? /^data:image\/png;base64,([A-Za-z0-9+/=]+)$/.exec(b['snapshot']) : null;
447
+ if (snap && out.strokes) {
448
+ const buf = Buffer.from(snap[1] ?? '', 'base64');
449
+ // a PNG, and nothing else
450
+ if (buf.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
451
+ out.snapshot = buf;
452
+ }
453
+ return out;
454
+ }
455
+ async function body(req, max = 1_000_000) {
456
+ if (!String(req.headers['content-type'] ?? '').includes('application/json'))
457
+ throw new Error('Send JSON');
458
+ const chunks = [];
459
+ let size = 0;
460
+ for await (const c of req) {
461
+ const b = Buffer.isBuffer(c) ? c : Buffer.from(String(c));
462
+ size += b.length;
463
+ if (size > max)
464
+ throw new Error('Too large');
465
+ chunks.push(b);
466
+ }
467
+ const data = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}');
468
+ if (!data || typeof data !== 'object' || Array.isArray(data))
469
+ throw new Error('Send a JSON object');
470
+ return Object.fromEntries(Object.entries(data));
471
+ }
472
+ const str = (v) => (typeof v === 'string' ? v : '');
473
+ const num = (v) => (typeof v === 'number' && Number.isInteger(v) && v > 0 ? v : undefined);
474
+ function readVersion() {
475
+ try {
476
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(here, '..', 'package.json'), 'utf8'));
477
+ return pkg && typeof pkg === 'object' && 'version' in pkg ? String(pkg.version) : '0.0.0';
478
+ }
479
+ catch {
480
+ return '0.0.0';
481
+ }
482
+ }
package/dist/watch.js ADDED
@@ -0,0 +1,69 @@
1
+ // Live reload: watches the folder and tells open pages which folders changed (and when git's state may have).
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { NOISE } from './files.js';
5
+ export function watchTree(root, gitDir, onBatch) {
6
+ let folders = new Set(), files = new Set(), git = false;
7
+ let timer;
8
+ const flush = () => {
9
+ timer = undefined;
10
+ const b = { folders: [...folders], files: [...files], git };
11
+ folders = new Set();
12
+ files = new Set();
13
+ git = false;
14
+ onBatch(b);
15
+ };
16
+ const bump = () => {
17
+ timer ??= setTimeout(flush, 250);
18
+ };
19
+ const closers = [];
20
+ try {
21
+ const w = fs.watch(root, { recursive: true }, (_event, name) => {
22
+ if (!name)
23
+ return bump();
24
+ const rel = String(name).split(path.sep).join('/');
25
+ const parts = rel.split('/');
26
+ if (parts[0] === '.git') {
27
+ // the index, HEAD and refs say git's view moved; objects and logs are noise
28
+ if (/^\.git\/(index|HEAD|refs\/|packed-refs|MERGE_HEAD)/.test(rel)) {
29
+ git = true;
30
+ bump();
31
+ }
32
+ return;
33
+ }
34
+ if (parts.some((p) => NOISE.has(p)))
35
+ return;
36
+ files.add(rel);
37
+ folders.add(parts.slice(0, -1).join('/'));
38
+ git = true; // a working-tree edit changes git status too
39
+ bump();
40
+ });
41
+ w.on('error', () => undefined);
42
+ closers.push(() => w.close());
43
+ }
44
+ catch {
45
+ // recursive watching isn't available here: no live reload, the page still works on refresh
46
+ }
47
+ // a served subfolder of a repo: .git is elsewhere, watch it on its own
48
+ if (gitDir && !gitDir.startsWith(root + path.sep)) {
49
+ try {
50
+ const w = fs.watch(gitDir, (_e, name) => {
51
+ if (name && /^(index|HEAD|packed-refs)$/.test(String(name))) {
52
+ git = true;
53
+ bump();
54
+ }
55
+ });
56
+ w.on('error', () => undefined);
57
+ closers.push(() => w.close());
58
+ }
59
+ catch {
60
+ // fine without it
61
+ }
62
+ }
63
+ return () => {
64
+ for (const c of closers)
65
+ c();
66
+ if (timer)
67
+ clearTimeout(timer);
68
+ };
69
+ }
package/dist/zip.js ADDED
@@ -0,0 +1,100 @@
1
+ // A folder as a .zip, streamed: each file deflated (or stored, when that's smaller), no dependencies.
2
+ // Plain ZIP (not ZIP64): up to 65,535 files and under 4 GB, which is checked before anything is sent.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import zlib from 'node:zlib';
6
+ const TABLE = (() => {
7
+ const t = new Uint32Array(256);
8
+ for (let n = 0; n < 256; n++) {
9
+ let c = n;
10
+ for (let k = 0; k < 8; k++)
11
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
12
+ t[n] = c >>> 0;
13
+ }
14
+ return t;
15
+ })();
16
+ export function crc32(buf) {
17
+ const native = zlib.crc32;
18
+ if (native)
19
+ return native(buf) >>> 0;
20
+ let c = 0xffffffff;
21
+ for (const b of buf)
22
+ c = (TABLE[(c ^ b) & 0xff] ?? 0) ^ (c >>> 8);
23
+ return (c ^ 0xffffffff) >>> 0;
24
+ }
25
+ export const ZIP_LIMITS = { files: 65_535, bytes: 2 * 1024 * 1024 * 1024 };
26
+ function dosTime(d) {
27
+ const time = (d.getHours() << 11) | (d.getMinutes() << 5) | Math.floor(d.getSeconds() / 2);
28
+ const date = (Math.max(0, d.getFullYear() - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate();
29
+ return [time, date];
30
+ }
31
+ /** Write a zip of the given files (absolute path → name inside the zip) to out. */
32
+ export async function writeZip(out, files) {
33
+ const central = [];
34
+ let offset = 0;
35
+ const write = (b) => new Promise((resolve, reject) => {
36
+ offset += b.length;
37
+ if (out.write(b))
38
+ resolve();
39
+ else {
40
+ out.once('drain', resolve);
41
+ out.once('error', reject);
42
+ }
43
+ });
44
+ for (const f of files) {
45
+ let data, st;
46
+ try {
47
+ st = await fs.promises.stat(f.abs);
48
+ data = await fs.promises.readFile(f.abs);
49
+ }
50
+ catch {
51
+ continue; // gone since it was listed
52
+ }
53
+ const crc = crc32(data);
54
+ const deflated = zlib.deflateRawSync(data, { level: 6 });
55
+ const store = deflated.length >= data.length;
56
+ const body = store ? data : deflated;
57
+ const name = Buffer.from(f.name, 'utf8');
58
+ const [time, date] = dosTime(st.mtime);
59
+ const local = Buffer.alloc(30);
60
+ local.writeUInt32LE(0x04034b50, 0);
61
+ local.writeUInt16LE(20, 4);
62
+ local.writeUInt16LE(0x0800, 6); // names are UTF-8
63
+ local.writeUInt16LE(store ? 0 : 8, 8);
64
+ local.writeUInt16LE(time, 10);
65
+ local.writeUInt16LE(date, 12);
66
+ local.writeUInt32LE(crc, 14);
67
+ local.writeUInt32LE(body.length, 18);
68
+ local.writeUInt32LE(data.length, 22);
69
+ local.writeUInt16LE(name.length, 26);
70
+ const at = offset;
71
+ await write(Buffer.concat([local, name]));
72
+ await write(body);
73
+ const cen = Buffer.alloc(46);
74
+ cen.writeUInt32LE(0x02014b50, 0);
75
+ cen.writeUInt16LE(20, 4);
76
+ cen.writeUInt16LE(20, 6);
77
+ cen.writeUInt16LE(0x0800, 8);
78
+ cen.writeUInt16LE(store ? 0 : 8, 10);
79
+ cen.writeUInt16LE(time, 12);
80
+ cen.writeUInt16LE(date, 14);
81
+ cen.writeUInt32LE(crc, 16);
82
+ cen.writeUInt32LE(body.length, 20);
83
+ cen.writeUInt32LE(data.length, 24);
84
+ cen.writeUInt16LE(name.length, 28);
85
+ cen.writeUInt32LE(at, 42);
86
+ central.push(Buffer.concat([cen, name]));
87
+ }
88
+ const start = offset;
89
+ const dir = Buffer.concat(central);
90
+ await write(dir);
91
+ const end = Buffer.alloc(22);
92
+ end.writeUInt32LE(0x06054b50, 0);
93
+ end.writeUInt16LE(central.length, 8);
94
+ end.writeUInt16LE(central.length, 10);
95
+ end.writeUInt32LE(dir.length, 12);
96
+ end.writeUInt32LE(start, 16);
97
+ await write(end);
98
+ }
99
+ /** A zip-safe name for a root-relative path inside a folder being zipped. */
100
+ export const entryName = (folderName, rel) => path.posix.join(folderName, rel);