mongoku 2.0.1 → 2.0.3

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 (292) hide show
  1. package/Dockerfile +27 -0
  2. package/README.md +8 -0
  3. package/build/client/_app/immutable/assets/0.BHsXFfsc.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/PrettyJson.CtngcfyW.css.gz +0 -0
  5. package/build/client/_app/immutable/chunks/{W-qL5_8w.js → BAM9w9EL.js} +1 -1
  6. package/build/client/_app/immutable/chunks/BAM9w9EL.js.br +0 -0
  7. package/build/client/_app/immutable/chunks/BAM9w9EL.js.gz +0 -0
  8. package/build/client/_app/immutable/chunks/BKCvWebR.js +23 -0
  9. package/build/client/_app/immutable/chunks/BKCvWebR.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/BKCvWebR.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/{Dbyirw23.js → BMa204Dm.js} +3 -3
  12. package/build/client/_app/immutable/chunks/BMa204Dm.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/BMa204Dm.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/BN_N9-2s.js.gz +0 -0
  15. package/build/client/_app/immutable/chunks/BY57TmdO.js +1 -0
  16. package/build/client/_app/immutable/chunks/BY57TmdO.js.br +0 -0
  17. package/build/client/_app/immutable/chunks/BY57TmdO.js.gz +0 -0
  18. package/build/client/_app/immutable/chunks/BaB-cr9N.js +2 -0
  19. package/build/client/_app/immutable/chunks/BaB-cr9N.js.br +0 -0
  20. package/build/client/_app/immutable/chunks/BaB-cr9N.js.gz +0 -0
  21. package/build/client/_app/immutable/chunks/BdR-m9Ad.js +1 -0
  22. package/build/client/_app/immutable/chunks/BdR-m9Ad.js.br +0 -0
  23. package/build/client/_app/immutable/chunks/BdR-m9Ad.js.gz +0 -0
  24. package/build/client/_app/immutable/chunks/ByjLCPv2.js +1 -0
  25. package/build/client/_app/immutable/chunks/ByjLCPv2.js.br +0 -0
  26. package/build/client/_app/immutable/chunks/ByjLCPv2.js.gz +0 -0
  27. package/build/client/_app/immutable/chunks/BzAcxkRZ.js +4 -0
  28. package/build/client/_app/immutable/chunks/BzAcxkRZ.js.br +0 -0
  29. package/build/client/_app/immutable/chunks/BzAcxkRZ.js.gz +0 -0
  30. package/build/client/_app/immutable/chunks/CyQLXPZI.js +2 -0
  31. package/build/client/_app/immutable/chunks/CyQLXPZI.js.br +0 -0
  32. package/build/client/_app/immutable/chunks/CyQLXPZI.js.gz +0 -0
  33. package/build/client/_app/immutable/chunks/D17Lj1H7.js +1 -0
  34. package/build/client/_app/immutable/chunks/D17Lj1H7.js.br +0 -0
  35. package/build/client/_app/immutable/chunks/D17Lj1H7.js.gz +0 -0
  36. package/build/client/_app/immutable/chunks/D4VhtiDg.js +1 -0
  37. package/build/client/_app/immutable/chunks/D4VhtiDg.js.br +0 -0
  38. package/build/client/_app/immutable/chunks/D4VhtiDg.js.gz +0 -0
  39. package/build/client/_app/immutable/chunks/D72Du7TF.js +1 -0
  40. package/build/client/_app/immutable/chunks/D72Du7TF.js.br +2 -0
  41. package/build/client/_app/immutable/chunks/D72Du7TF.js.gz +0 -0
  42. package/build/client/_app/immutable/chunks/{DZSJW-Zm.js → XYFbSe2V.js} +1 -1
  43. package/build/client/_app/immutable/chunks/XYFbSe2V.js.br +0 -0
  44. package/build/client/_app/immutable/chunks/XYFbSe2V.js.gz +0 -0
  45. package/build/client/_app/immutable/chunks/iHsChU5o.js +1 -0
  46. package/build/client/_app/immutable/chunks/iHsChU5o.js.br +0 -0
  47. package/build/client/_app/immutable/chunks/iHsChU5o.js.gz +0 -0
  48. package/build/client/_app/immutable/chunks/{DzSHMQR7.js → p23QjN60.js} +1 -1
  49. package/build/client/_app/immutable/chunks/p23QjN60.js.br +2 -0
  50. package/build/client/_app/immutable/chunks/p23QjN60.js.gz +0 -0
  51. package/build/client/_app/immutable/chunks/sBKW2MYH.js +1 -0
  52. package/build/client/_app/immutable/chunks/sBKW2MYH.js.br +0 -0
  53. package/build/client/_app/immutable/chunks/sBKW2MYH.js.gz +0 -0
  54. package/build/client/_app/immutable/chunks/{BEuO9qGi.js → uMNMODvc.js} +1 -1
  55. package/build/client/_app/immutable/chunks/uMNMODvc.js.br +0 -0
  56. package/build/client/_app/immutable/chunks/uMNMODvc.js.gz +0 -0
  57. package/build/client/_app/immutable/entry/app.9nC_873E.js +2 -0
  58. package/build/client/_app/immutable/entry/app.9nC_873E.js.br +0 -0
  59. package/build/client/_app/immutable/entry/app.9nC_873E.js.gz +0 -0
  60. package/build/client/_app/immutable/entry/start.Bn88Alw2.js +1 -0
  61. package/build/client/_app/immutable/entry/start.Bn88Alw2.js.br +2 -0
  62. package/build/client/_app/immutable/entry/start.Bn88Alw2.js.gz +0 -0
  63. package/build/client/_app/immutable/nodes/{0.B2lBDs71.js → 0.COxTCtn2.js} +1 -1
  64. package/build/client/_app/immutable/nodes/0.COxTCtn2.js.br +0 -0
  65. package/build/client/_app/immutable/nodes/0.COxTCtn2.js.gz +0 -0
  66. package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js +1 -0
  67. package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js.br +0 -0
  68. package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js.gz +0 -0
  69. package/build/client/_app/immutable/nodes/3.CI2GcqTf.js +1 -0
  70. package/build/client/_app/immutable/nodes/3.CI2GcqTf.js.br +0 -0
  71. package/build/client/_app/immutable/nodes/3.CI2GcqTf.js.gz +0 -0
  72. package/build/client/_app/immutable/nodes/{4.BqBphmVX.js → 4.ChSdW7ac.js} +1 -1
  73. package/build/client/_app/immutable/nodes/4.ChSdW7ac.js.br +0 -0
  74. package/build/client/_app/immutable/nodes/4.ChSdW7ac.js.gz +0 -0
  75. package/build/client/_app/immutable/nodes/{5.BkAWRgl4.js → 5.DaMML2go.js} +1 -1
  76. package/build/client/_app/immutable/nodes/5.DaMML2go.js.br +0 -0
  77. package/build/client/_app/immutable/nodes/5.DaMML2go.js.gz +0 -0
  78. package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js +1 -0
  79. package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js.br +0 -0
  80. package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js.gz +0 -0
  81. package/build/client/_app/immutable/nodes/7.CU-ncPes.js +1 -0
  82. package/build/client/_app/immutable/nodes/7.CU-ncPes.js.br +0 -0
  83. package/build/client/_app/immutable/nodes/7.CU-ncPes.js.gz +0 -0
  84. package/build/client/_app/version.json +1 -1
  85. package/build/client/_app/version.json.br +0 -0
  86. package/build/client/_app/version.json.gz +0 -0
  87. package/build/server/chunks/{0-BGoFMRn2.js → 0-C1NyHW8A.js} +3 -3
  88. package/build/server/chunks/{0-BGoFMRn2.js.map → 0-C1NyHW8A.js.map} +1 -1
  89. package/build/server/chunks/1-CThf4W5r.js +9 -0
  90. package/build/server/chunks/{1-Ch0AY58K.js.map → 1-CThf4W5r.js.map} +1 -1
  91. package/build/server/chunks/{3-Bmk9Qof9.js → 3-CJf0NbiV.js} +3 -3
  92. package/build/server/chunks/{3-Bmk9Qof9.js.map → 3-CJf0NbiV.js.map} +1 -1
  93. package/build/server/chunks/{4-DnpScx2e.js → 4-Dfbpsagm.js} +3 -3
  94. package/build/server/chunks/{4-DnpScx2e.js.map → 4-Dfbpsagm.js.map} +1 -1
  95. package/build/server/chunks/{5-Bw8bTTBj.js → 5-DLB6GOjf.js} +3 -3
  96. package/build/server/chunks/{5-Bw8bTTBj.js.map → 5-DLB6GOjf.js.map} +1 -1
  97. package/build/server/chunks/{6-D8zNoyW_.js → 6-DfCARDKO.js} +10 -5
  98. package/build/server/chunks/6-DfCARDKO.js.map +1 -0
  99. package/build/server/chunks/{7-Cim4qVL3.js → 7-B5o4OymX.js} +3 -3
  100. package/build/server/chunks/{7-Cim4qVL3.js.map → 7-B5o4OymX.js.map} +1 -1
  101. package/build/server/chunks/{Panel-DTdYk-gk.js → Panel-Box8ld78.js} +3 -3
  102. package/build/server/chunks/Panel-Box8ld78.js.map +1 -0
  103. package/build/server/chunks/{PrettyJson-wIuHvehp.js → PrettyJson-C2c9xGNo.js} +5 -5
  104. package/build/server/chunks/PrettyJson-C2c9xGNo.js.map +1 -0
  105. package/build/server/chunks/{TooltipTable-J278e1CA.js → TooltipTable-0z4HkHlH.js} +3 -1
  106. package/build/server/chunks/TooltipTable-0z4HkHlH.js.map +1 -0
  107. package/build/server/chunks/{_layout.svelte-D7RD9tLN.js → _layout.svelte-CU7AVRSZ.js} +14 -11
  108. package/build/server/chunks/_layout.svelte-CU7AVRSZ.js.map +1 -0
  109. package/build/server/chunks/{_page.svelte-4PYYtPii.js → _page.svelte-B_T8EFk3.js} +7 -6
  110. package/build/server/chunks/_page.svelte-B_T8EFk3.js.map +1 -0
  111. package/build/server/chunks/{_page.svelte-Be5VTn1e.js → _page.svelte-CflJkclc.js} +7 -6
  112. package/build/server/chunks/_page.svelte-CflJkclc.js.map +1 -0
  113. package/build/server/chunks/{_page.svelte-DZ59Sr3N.js → _page.svelte-D1QIZqLv.js} +65 -42
  114. package/build/server/chunks/_page.svelte-D1QIZqLv.js.map +1 -0
  115. package/build/server/chunks/{_page.svelte-DIPUjSjm.js → _page.svelte-PuIVQ52p.js} +20 -8
  116. package/build/server/chunks/_page.svelte-PuIVQ52p.js.map +1 -0
  117. package/build/server/chunks/_page.svelte-zlHeUnJK.js +101 -0
  118. package/build/server/chunks/_page.svelte-zlHeUnJK.js.map +1 -0
  119. package/build/server/chunks/{context-BU6gUbSI.js → async-BfR0vYHo.js} +8 -46
  120. package/build/server/chunks/async-BfR0vYHo.js.map +1 -0
  121. package/build/server/chunks/client-bj7sePQc.js +7 -0
  122. package/build/server/chunks/{client-BZtxOgbR.js.map → client-bj7sePQc.js.map} +1 -1
  123. package/build/server/chunks/{client2-LilSGYJA.js → client2-BX4-xbM4.js} +6 -5
  124. package/build/server/chunks/{client2-LilSGYJA.js.map → client2-BX4-xbM4.js.map} +1 -1
  125. package/build/server/chunks/error.svelte-D5sIJv3P.js +18 -0
  126. package/build/server/chunks/error.svelte-D5sIJv3P.js.map +1 -0
  127. package/build/server/chunks/event-DVH-6ISX.js +64 -0
  128. package/build/server/chunks/event-DVH-6ISX.js.map +1 -0
  129. package/build/server/chunks/{exports-DiyTWREe.js → exports-Cv9LZeD1.js} +2 -39
  130. package/build/server/chunks/exports-Cv9LZeD1.js.map +1 -0
  131. package/build/server/chunks/hooks.server-_TKfdzGA.js +26 -0
  132. package/build/server/chunks/hooks.server-_TKfdzGA.js.map +1 -0
  133. package/build/server/chunks/{index-XFKuhGxM.js → index-EKt9-9bV.js} +2 -2
  134. package/build/server/chunks/{index-XFKuhGxM.js.map → index-EKt9-9bV.js.map} +1 -1
  135. package/build/server/chunks/{index3-CUhdJ7wv.js → index3-DquTjlmp.js} +3 -3
  136. package/build/server/chunks/index3-DquTjlmp.js.map +1 -0
  137. package/build/server/chunks/{notifications.svelte-CrTIrfvL.js → notifications.svelte-CIqkoPWX.js} +6 -12
  138. package/build/server/chunks/notifications.svelte-CIqkoPWX.js.map +1 -0
  139. package/build/server/chunks/remote-xxtqbu-BFzaWXy0.js +234 -0
  140. package/build/server/chunks/remote-xxtqbu-BFzaWXy0.js.map +1 -0
  141. package/build/server/chunks/{routing-zIlQn_LN.js → routing-Ddj5b-2K.js} +25 -4
  142. package/build/server/chunks/routing-Ddj5b-2K.js.map +1 -0
  143. package/build/server/chunks/{server2-DfQrrcsk.js → server2-BOoUfKd7.js} +3 -3
  144. package/build/server/chunks/{server2-DfQrrcsk.js.map → server2-BOoUfKd7.js.map} +1 -1
  145. package/build/server/chunks/{state.svelte-s-CFCWQX.js → state.svelte-CRHA9yov.js} +3 -2
  146. package/build/server/chunks/{state.svelte-s-CFCWQX.js.map → state.svelte-CRHA9yov.js.map} +1 -1
  147. package/build/server/chunks/utils-DGYJFmnf.js +39 -0
  148. package/build/server/chunks/utils-DGYJFmnf.js.map +1 -0
  149. package/build/server/index.js +71 -24
  150. package/build/server/index.js.map +1 -1
  151. package/build/server/manifest.js +9 -30
  152. package/build/server/manifest.js.map +1 -1
  153. package/cli.ts +148 -0
  154. package/dist/cli.js +3 -4
  155. package/ecosystem.config.js +9 -0
  156. package/package.json +88 -81
  157. package/src/api/servers.remote.ts +98 -0
  158. package/src/app.css +228 -0
  159. package/src/app.d.ts +16 -0
  160. package/src/app.html +11 -0
  161. package/src/hooks.server.ts +34 -0
  162. package/src/lib/components/Breadcrumbs.svelte +133 -0
  163. package/src/lib/components/JsonValue.svelte +248 -0
  164. package/src/lib/components/Notifications.svelte +81 -0
  165. package/src/lib/components/Panel.svelte +37 -0
  166. package/src/lib/components/PrettyJson.svelte +187 -0
  167. package/src/lib/components/SearchBox.svelte +160 -0
  168. package/src/lib/components/TooltipTable.svelte +137 -0
  169. package/src/lib/server/HostsManager.ts +105 -0
  170. package/src/lib/server/JsonEncoder.ts +62 -0
  171. package/src/lib/server/mongo.ts +199 -0
  172. package/src/lib/stores/notifications.svelte.ts +45 -0
  173. package/src/lib/types.ts +56 -0
  174. package/src/lib/utils/filters.ts +25 -0
  175. package/src/lib/utils/jsonParser.ts +125 -0
  176. package/src/routes/+layout.server.ts +7 -0
  177. package/src/routes/+layout.svelte +27 -0
  178. package/src/routes/+page.server.ts +6 -0
  179. package/src/routes/servers/+page.server.ts +53 -0
  180. package/src/routes/servers/+page.svelte +196 -0
  181. package/src/routes/servers/[server]/databases/+page.server.ts +47 -0
  182. package/src/routes/servers/[server]/databases/+page.svelte +88 -0
  183. package/src/routes/servers/[server]/databases/[database]/collections/+page.server.ts +21 -0
  184. package/src/routes/servers/[server]/databases/[database]/collections/+page.svelte +110 -0
  185. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.server.ts +106 -0
  186. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.svelte +174 -0
  187. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.server.ts +25 -0
  188. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.svelte +90 -0
  189. package/src/tests/api/readonly.test.ts +89 -0
  190. package/src/tests/setup.ts +19 -0
  191. package/svelte.config.js +28 -0
  192. package/tsconfig.cli.json +15 -0
  193. package/tsconfig.json +19 -0
  194. package/vite.config.ts +7 -0
  195. package/build/client/_app/immutable/chunks/B-Z1NsR7.js +0 -1
  196. package/build/client/_app/immutable/chunks/B-Z1NsR7.js.br +0 -0
  197. package/build/client/_app/immutable/chunks/B-Z1NsR7.js.gz +0 -0
  198. package/build/client/_app/immutable/chunks/BEuO9qGi.js.br +0 -0
  199. package/build/client/_app/immutable/chunks/BEuO9qGi.js.gz +0 -0
  200. package/build/client/_app/immutable/chunks/BuIrCFA_.js +0 -1
  201. package/build/client/_app/immutable/chunks/BuIrCFA_.js.br +0 -0
  202. package/build/client/_app/immutable/chunks/BuIrCFA_.js.gz +0 -0
  203. package/build/client/_app/immutable/chunks/C9b4uNov.js +0 -1
  204. package/build/client/_app/immutable/chunks/C9b4uNov.js.br +0 -0
  205. package/build/client/_app/immutable/chunks/C9b4uNov.js.gz +0 -0
  206. package/build/client/_app/immutable/chunks/CIYBIyvN.js +0 -1
  207. package/build/client/_app/immutable/chunks/CIYBIyvN.js.br +0 -0
  208. package/build/client/_app/immutable/chunks/CIYBIyvN.js.gz +0 -0
  209. package/build/client/_app/immutable/chunks/CWUhQKKC.js +0 -1
  210. package/build/client/_app/immutable/chunks/CWUhQKKC.js.br +0 -0
  211. package/build/client/_app/immutable/chunks/CWUhQKKC.js.gz +0 -0
  212. package/build/client/_app/immutable/chunks/CrJSDly5.js +0 -2
  213. package/build/client/_app/immutable/chunks/CrJSDly5.js.br +0 -0
  214. package/build/client/_app/immutable/chunks/CrJSDly5.js.gz +0 -0
  215. package/build/client/_app/immutable/chunks/DRy_44ib.js +0 -1
  216. package/build/client/_app/immutable/chunks/DRy_44ib.js.br +0 -0
  217. package/build/client/_app/immutable/chunks/DRy_44ib.js.gz +0 -0
  218. package/build/client/_app/immutable/chunks/DS5yzhLt.js +0 -1
  219. package/build/client/_app/immutable/chunks/DS5yzhLt.js.br +0 -0
  220. package/build/client/_app/immutable/chunks/DS5yzhLt.js.gz +0 -0
  221. package/build/client/_app/immutable/chunks/DZSJW-Zm.js.br +0 -0
  222. package/build/client/_app/immutable/chunks/DZSJW-Zm.js.gz +0 -0
  223. package/build/client/_app/immutable/chunks/Dbyirw23.js.br +0 -0
  224. package/build/client/_app/immutable/chunks/Dbyirw23.js.gz +0 -0
  225. package/build/client/_app/immutable/chunks/DsnmJJEf.js +0 -1
  226. package/build/client/_app/immutable/chunks/DsnmJJEf.js.br +0 -2
  227. package/build/client/_app/immutable/chunks/DsnmJJEf.js.gz +0 -0
  228. package/build/client/_app/immutable/chunks/DzSHMQR7.js.br +0 -0
  229. package/build/client/_app/immutable/chunks/DzSHMQR7.js.gz +0 -0
  230. package/build/client/_app/immutable/chunks/W-qL5_8w.js.br +0 -0
  231. package/build/client/_app/immutable/chunks/W-qL5_8w.js.gz +0 -0
  232. package/build/client/_app/immutable/chunks/aB6hH3ax.js +0 -3
  233. package/build/client/_app/immutable/chunks/aB6hH3ax.js.br +0 -0
  234. package/build/client/_app/immutable/chunks/aB6hH3ax.js.gz +0 -0
  235. package/build/client/_app/immutable/chunks/nJ6DKbk8.js +0 -1
  236. package/build/client/_app/immutable/chunks/nJ6DKbk8.js.br +0 -0
  237. package/build/client/_app/immutable/chunks/nJ6DKbk8.js.gz +0 -0
  238. package/build/client/_app/immutable/chunks/zeTqHWB5.js +0 -2
  239. package/build/client/_app/immutable/chunks/zeTqHWB5.js.br +0 -0
  240. package/build/client/_app/immutable/chunks/zeTqHWB5.js.gz +0 -0
  241. package/build/client/_app/immutable/entry/app.CW91YXu8.js +0 -2
  242. package/build/client/_app/immutable/entry/app.CW91YXu8.js.br +0 -0
  243. package/build/client/_app/immutable/entry/app.CW91YXu8.js.gz +0 -0
  244. package/build/client/_app/immutable/entry/start.CidwPVgO.js +0 -1
  245. package/build/client/_app/immutable/entry/start.CidwPVgO.js.br +0 -2
  246. package/build/client/_app/immutable/entry/start.CidwPVgO.js.gz +0 -0
  247. package/build/client/_app/immutable/nodes/0.B2lBDs71.js.br +0 -0
  248. package/build/client/_app/immutable/nodes/0.B2lBDs71.js.gz +0 -0
  249. package/build/client/_app/immutable/nodes/1.CueZCvtl.js +0 -1
  250. package/build/client/_app/immutable/nodes/1.CueZCvtl.js.br +0 -0
  251. package/build/client/_app/immutable/nodes/1.CueZCvtl.js.gz +0 -0
  252. package/build/client/_app/immutable/nodes/3.alYAlI3j.js +0 -1
  253. package/build/client/_app/immutable/nodes/3.alYAlI3j.js.br +0 -0
  254. package/build/client/_app/immutable/nodes/3.alYAlI3j.js.gz +0 -0
  255. package/build/client/_app/immutable/nodes/4.BqBphmVX.js.br +0 -0
  256. package/build/client/_app/immutable/nodes/4.BqBphmVX.js.gz +0 -0
  257. package/build/client/_app/immutable/nodes/5.BkAWRgl4.js.br +0 -0
  258. package/build/client/_app/immutable/nodes/5.BkAWRgl4.js.gz +0 -0
  259. package/build/client/_app/immutable/nodes/6.KI4mq5ln.js +0 -1
  260. package/build/client/_app/immutable/nodes/6.KI4mq5ln.js.br +0 -0
  261. package/build/client/_app/immutable/nodes/6.KI4mq5ln.js.gz +0 -0
  262. package/build/client/_app/immutable/nodes/7.Brjrw0K8.js +0 -1
  263. package/build/client/_app/immutable/nodes/7.Brjrw0K8.js.br +0 -0
  264. package/build/client/_app/immutable/nodes/7.Brjrw0K8.js.gz +0 -0
  265. package/build/server/chunks/1-Ch0AY58K.js +0 -9
  266. package/build/server/chunks/6-D8zNoyW_.js.map +0 -1
  267. package/build/server/chunks/Panel-DTdYk-gk.js.map +0 -1
  268. package/build/server/chunks/PrettyJson-wIuHvehp.js.map +0 -1
  269. package/build/server/chunks/TooltipTable-J278e1CA.js.map +0 -1
  270. package/build/server/chunks/_layout.svelte-D7RD9tLN.js.map +0 -1
  271. package/build/server/chunks/_page.svelte-4PYYtPii.js.map +0 -1
  272. package/build/server/chunks/_page.svelte-Be5VTn1e.js.map +0 -1
  273. package/build/server/chunks/_page.svelte-DIPUjSjm.js.map +0 -1
  274. package/build/server/chunks/_page.svelte-DZ59Sr3N.js.map +0 -1
  275. package/build/server/chunks/_page.svelte-sdKGQrEF.js +0 -91
  276. package/build/server/chunks/_page.svelte-sdKGQrEF.js.map +0 -1
  277. package/build/server/chunks/_server.ts-BIRebnb3.js +0 -20
  278. package/build/server/chunks/_server.ts-BIRebnb3.js.map +0 -1
  279. package/build/server/chunks/_server.ts-MnKJDybo.js +0 -19
  280. package/build/server/chunks/_server.ts-MnKJDybo.js.map +0 -1
  281. package/build/server/chunks/_server.ts-Tog8k3Mh.js +0 -52
  282. package/build/server/chunks/_server.ts-Tog8k3Mh.js.map +0 -1
  283. package/build/server/chunks/client-BZtxOgbR.js +0 -7
  284. package/build/server/chunks/context-BU6gUbSI.js.map +0 -1
  285. package/build/server/chunks/error.svelte-BfneeC-d.js +0 -16
  286. package/build/server/chunks/error.svelte-BfneeC-d.js.map +0 -1
  287. package/build/server/chunks/exports-DiyTWREe.js.map +0 -1
  288. package/build/server/chunks/hooks.server-DNhdbQNi.js +0 -7
  289. package/build/server/chunks/hooks.server-DNhdbQNi.js.map +0 -1
  290. package/build/server/chunks/index3-CUhdJ7wv.js.map +0 -1
  291. package/build/server/chunks/notifications.svelte-CrTIrfvL.js.map +0 -1
  292. package/build/server/chunks/routing-zIlQn_LN.js.map +0 -1
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import { notificationStore } from "$lib/stores/notifications.svelte";
3
+ </script>
4
+
5
+ <div class="notifications">
6
+ {#each notificationStore.items as notification (notification.id)}
7
+ <div class="notification notification-{notification.type}">
8
+ <span>{notification.message}</span>
9
+ <button onclick={() => notificationStore.remove(notification.id)}>&times;</button>
10
+ </div>
11
+ {/each}
12
+ </div>
13
+
14
+ <style lang="postcss">
15
+ .notifications {
16
+ position: fixed;
17
+ top: 80px;
18
+ right: 20px;
19
+ z-index: 1000;
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 10px;
23
+ }
24
+
25
+ .notification {
26
+ padding: 15px 20px;
27
+ border-radius: 4px;
28
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 15px;
32
+ min-width: 300px;
33
+ max-width: 500px;
34
+ animation: slideIn 0.3s ease-out;
35
+
36
+ button {
37
+ background: none;
38
+ border: none;
39
+ font-size: 24px;
40
+ cursor: pointer;
41
+ opacity: 0.7;
42
+ margin-left: auto;
43
+ padding: 0;
44
+ width: 24px;
45
+ height: 24px;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+
50
+ &:hover {
51
+ opacity: 1;
52
+ }
53
+ }
54
+ }
55
+
56
+ .notification-error {
57
+ background-color: #dc3545;
58
+ color: white;
59
+ }
60
+
61
+ .notification-success {
62
+ background-color: #28a745;
63
+ color: white;
64
+ }
65
+
66
+ .notification-info {
67
+ background-color: #17a2b8;
68
+ color: white;
69
+ }
70
+
71
+ @keyframes slideIn {
72
+ from {
73
+ transform: translateX(400px);
74
+ opacity: 0;
75
+ }
76
+ to {
77
+ transform: translateX(0);
78
+ opacity: 1;
79
+ }
80
+ }
81
+ </style>
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ interface Props {
5
+ children?: Snippet;
6
+ title?: string | Snippet;
7
+ actions?: Snippet;
8
+ class?: string;
9
+ }
10
+
11
+ let { children, title, actions, class: className }: Props = $props();
12
+ </script>
13
+
14
+ <div class="panel rounded-md {className}">
15
+ {#if title}
16
+ <div class="px-4 py-2 flex justify-between items-center text-lg uppercase font-medium">
17
+ {#if typeof title === "string"}
18
+ <span>{title}</span>
19
+ {:else}
20
+ {@render title()}
21
+ {/if}
22
+ {#if actions}
23
+ <div class="flex gap-2 items-center">
24
+ {@render actions()}
25
+ </div>
26
+ {/if}
27
+ </div>
28
+ {/if}
29
+ {@render children?.()}
30
+ </div>
31
+
32
+ <style lang="postcss">
33
+ .panel {
34
+ background-color: var(--color-2);
35
+ border: var(--border);
36
+ }
37
+ </style>
@@ -0,0 +1,187 @@
1
+ <script lang="ts">
2
+ import { resolve } from "$app/paths";
3
+ import { notificationStore } from "$lib/stores/notifications.svelte";
4
+ import type { MongoDocument } from "$lib/types";
5
+ import { parseJSON } from "$lib/utils/jsonParser";
6
+ import JsonValue from "./JsonValue.svelte";
7
+ import Panel from "./Panel.svelte";
8
+
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ interface Props {
12
+ json: MongoDocument;
13
+ autoCollapse?: boolean;
14
+ readOnly?: boolean;
15
+ onedit?: (json: any) => void;
16
+ onremove?: () => void;
17
+ server: string;
18
+ database: string;
19
+ collection: string;
20
+ }
21
+
22
+ let {
23
+ json,
24
+ autoCollapse = false,
25
+ readOnly = false,
26
+ onedit,
27
+ onremove,
28
+ server,
29
+ database,
30
+ collection,
31
+ }: Props = $props();
32
+
33
+ let editorVisible = $state(false);
34
+ let editJson = $state("");
35
+ let removing = $state(false);
36
+ let editorRef = $state<HTMLTextAreaElement>();
37
+
38
+ // Extract timestamp from ObjectId
39
+ function getTimestampFromObjectId(objectId: string): Date | null {
40
+ try {
41
+ // ObjectId is 24 hex characters, first 8 represent timestamp
42
+ const timestamp = parseInt(objectId.substring(0, 8), 16);
43
+ return new Date(timestamp * 1000);
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ // Custom serializer that converts ObjectId objects back to new ObjectId() format
50
+ function serializeForEditing(obj: any, depth = 0): string {
51
+ const indent = " ".repeat(depth);
52
+ const nextIndent = " ".repeat(depth + 1);
53
+
54
+ if (obj === null) return "null";
55
+ if (obj === undefined) return "undefined";
56
+ if (typeof obj === "string") return JSON.stringify(obj);
57
+ if (typeof obj === "number") return obj.toString();
58
+ if (typeof obj === "boolean") return obj.toString();
59
+
60
+ if (Array.isArray(obj)) {
61
+ if (obj.length === 0) return "[]";
62
+ const items = obj.map((item) => `${nextIndent}${serializeForEditing(item, depth + 1)}`).join(",\n");
63
+ return `[\n${items}\n${indent}]`;
64
+ }
65
+
66
+ if (typeof obj === "object") {
67
+ // Handle special MongoDB types
68
+ if (obj.$type === "ObjectId") {
69
+ return `new ObjectId("${obj.$value}")`;
70
+ }
71
+ if (obj.$type === "Date") {
72
+ return `new Date("${obj.$value}")`;
73
+ }
74
+ if (obj.$type === "RegExp") {
75
+ return `new RegExp("${obj.$value.$pattern}", "${obj.$value.$flags}")`;
76
+ }
77
+
78
+ // Handle regular objects
79
+ const keys = Object.keys(obj);
80
+ if (keys.length === 0) return "{}";
81
+
82
+ const pairs = keys
83
+ .map((key) => {
84
+ const value = serializeForEditing(obj[key], depth + 1);
85
+ return `${nextIndent}${JSON.stringify(key)}: ${value}`;
86
+ })
87
+ .join(",\n");
88
+
89
+ return `{\n${pairs}\n${indent}}`;
90
+ }
91
+
92
+ return JSON.stringify(obj);
93
+ }
94
+
95
+ function enableEditor() {
96
+ editJson = serializeForEditing(json);
97
+ editorVisible = true;
98
+ }
99
+
100
+ function disableEditor() {
101
+ editorVisible = false;
102
+ editJson = "";
103
+ }
104
+
105
+ function save() {
106
+ try {
107
+ const updatedJson = parseJSON(editJson);
108
+ disableEditor();
109
+ onedit?.(updatedJson);
110
+ } catch (err) {
111
+ notificationStore.notifyError(err, "Invalid JSON");
112
+ }
113
+ }
114
+
115
+ function showRemove() {
116
+ removing = true;
117
+ }
118
+
119
+ function cancelRemove() {
120
+ removing = false;
121
+ }
122
+
123
+ function confirmRemove() {
124
+ onremove?.();
125
+ }
126
+ </script>
127
+
128
+ <Panel class="group">
129
+ {#snippet title()}
130
+ {#if json._id}
131
+ <div class="">
132
+ <a
133
+ type="button"
134
+ class="bg-transparent border-none text-[var(--text)] no-underline cursor-pointer text-xl font-inherit p-0 hover:underline"
135
+ href={resolve(
136
+ `/servers/${encodeURIComponent(server)}/databases/${encodeURIComponent(database)}/collections/${encodeURIComponent(collection)}/documents/${json._id?.$value}`,
137
+ )}
138
+ >
139
+ {json._id?.$value}
140
+ </a>
141
+ {#if json._id?.$value}
142
+ {@const timestamp = getTimestampFromObjectId(json._id.$value)}
143
+ {#if timestamp}
144
+ <span class="ml-2 text-md text-[var(--text-secondary,#888)]">{timestamp.toLocaleString()}</span>
145
+ {/if}
146
+ {/if}
147
+ </div>
148
+ {/if}
149
+ {/snippet}
150
+
151
+ {#snippet actions()}
152
+ {#if !readOnly}
153
+ <div class="hidden group-hover:block">
154
+ <button class="btn btn-outline-light btn-sm ml-2 -my-2" onclick={enableEditor}>Edit</button>
155
+ <button class="btn btn-outline-danger btn-sm ml-2 -my-2" onclick={showRemove}>Remove</button>
156
+ </div>
157
+ {/if}
158
+ {/snippet}
159
+
160
+ <div class="p-4 relative border-t border-[var(--border-color)]">
161
+ <div class="font-mono text-sm leading-tight whitespace-pre-wrap break-words relative">
162
+ <JsonValue value={json} {autoCollapse} collapsed={false} />
163
+ </div>
164
+
165
+ <div class="absolute h-full z-[100] w-full top-0 left-0" class:hidden={!editorVisible} class:block={editorVisible}>
166
+ <div class="absolute z-10 right-5 top-4">
167
+ <button class="btn btn-success ml-2" onclick={save}>Save</button>
168
+ <button class="btn btn-default ml-2" onclick={disableEditor}>Cancel</button>
169
+ </div>
170
+ <textarea
171
+ bind:this={editorRef}
172
+ bind:value={editJson}
173
+ class="w-full min-h-[300px] font-mono text-sm leading-relaxed p-2.5 bg-[var(--color-1)] text-[var(--text)] border border-[var(--border-color)] rounded resize-y"
174
+ ></textarea>
175
+ </div>
176
+
177
+ {#if removing}
178
+ <div class="absolute z-10 top-0 left-0 w-full h-full rounded bg-[var(--text-inverse)] opacity-70">
179
+ <p class="text-[var(--text)] text-center mt-5 text-2xl">Are you sure?</p>
180
+ <div class="absolute w-full h-full top-0 flex justify-center items-center">
181
+ <button class="btn btn-danger m-5" onclick={confirmRemove}>Yes - Remove</button>
182
+ <button class="btn btn-success m-5" onclick={cancelRemove}>No - Cancel</button>
183
+ </div>
184
+ </div>
185
+ {/if}
186
+ </div>
187
+ </Panel>
@@ -0,0 +1,160 @@
1
+ <script lang="ts">
2
+ import { goto } from "$app/navigation";
3
+ import { resolve } from "$app/paths";
4
+ import { page } from "$app/state";
5
+ import type { SearchParams } from "$lib/types";
6
+
7
+ interface Props {
8
+ params: SearchParams;
9
+ }
10
+
11
+ let { params = $bindable() }: Props = $props();
12
+
13
+ // Show optional fields - start with all hidden
14
+ let showOptionalFields = $state(
15
+ params.sort !== "{}" || params.project !== "{}" || params.skip !== 0 || params.limit !== 20,
16
+ );
17
+
18
+ let counter = $state(Math.random());
19
+
20
+ let queryInput: HTMLInputElement | undefined;
21
+
22
+ $effect(() => {
23
+ if (queryInput) {
24
+ queryInput.setSelectionRange(1, 1 /* queryInput.value.length - 1 */);
25
+ queryInput.focus();
26
+ }
27
+ });
28
+
29
+ async function submit(event: SubmitEvent) {
30
+ event.preventDefault();
31
+ counter++;
32
+ const formData = new FormData(form);
33
+ await goto(
34
+ resolve(
35
+ (page.url.pathname +
36
+ "?" +
37
+ [...formData.entries()]
38
+ .map((e) => encodeURIComponent(e[0]) + "=" + encodeURIComponent(e[1] as string))
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ .join("&")) as any,
41
+ ),
42
+ {
43
+ keepFocus: true,
44
+ },
45
+ );
46
+ }
47
+
48
+ let form: HTMLFormElement | undefined;
49
+ </script>
50
+
51
+ <form class="flex items-stretch w-full" method="GET" action="?" onsubmit={submit} bind:this={form}>
52
+ <!-- Parameters group -->
53
+ <div class="flex-grow">
54
+ <!-- Query input (always shown) -->
55
+ <div class="flex items-stretch w-full h-10">
56
+ <div
57
+ class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] {!showOptionalFields
58
+ ? 'border-b rounded-bl-md'
59
+ : 'border-b-0'} bg-[var(--color-1)] rounded-tl-md"
60
+ >
61
+ Query:
62
+ </div>
63
+ <input
64
+ type="text"
65
+ bind:this={queryInput}
66
+ bind:value={params.query}
67
+ placeholder={"{}"}
68
+ name="query"
69
+ class="flex-grow border-0 bg-[var(--color-3)] pl-2.5 font-mono"
70
+ />
71
+ </div>
72
+
73
+ <input type="hidden" value={counter} name="v" />
74
+ <!-- Sort input -->
75
+ {#if showOptionalFields}
76
+ <div class="flex items-stretch w-full h-10">
77
+ <div
78
+ class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
79
+ >
80
+ Sort:
81
+ </div>
82
+ <input
83
+ type="text"
84
+ bind:value={params.sort}
85
+ name="sort"
86
+ placeholder={"{}"}
87
+ class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
88
+ />
89
+ </div>
90
+
91
+ <!-- Skip input -->
92
+ <div class="flex items-stretch w-full h-10">
93
+ <div
94
+ class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
95
+ >
96
+ Skip:
97
+ </div>
98
+ <input
99
+ type="number"
100
+ bind:value={params.skip}
101
+ name="skip"
102
+ min="0"
103
+ class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
104
+ />
105
+ </div>
106
+
107
+ <!-- Limit input -->
108
+ <div class="flex items-stretch w-full h-10">
109
+ <div
110
+ class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
111
+ >
112
+ Limit:
113
+ </div>
114
+ <input
115
+ type="number"
116
+ bind:value={params.limit}
117
+ name="limit"
118
+ min="1"
119
+ class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
120
+ />
121
+ </div>
122
+
123
+ <!-- Project input -->
124
+ <div class="flex items-stretch w-full h-10">
125
+ <div
126
+ class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b bg-[var(--color-1)] rounded-bl-md"
127
+ >
128
+ Project:
129
+ </div>
130
+ <input
131
+ type="text"
132
+ bind:value={params.project}
133
+ name="project"
134
+ placeholder={"{}"}
135
+ class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
136
+ />
137
+ </div>
138
+ {/if}
139
+ </div>
140
+
141
+ <!-- Toggle optional fields button -->
142
+ <button
143
+ class="btn btn-default !w-12 !rounded-none !border-r-0 text-2xl leading-none font-bold !py-1.5"
144
+ type="button"
145
+ onclick={() => {
146
+ showOptionalFields = !showOptionalFields;
147
+ }}
148
+ >
149
+ {showOptionalFields ? "−" : "+"}
150
+ </button>
151
+
152
+ <!-- Search button -->
153
+ <button class="btn btn-success !rounded-l-none !rounded-r-md font-bold !py-1.5" type="submit"> GO! </button>
154
+ </form>
155
+
156
+ <style lang="postcss">
157
+ input {
158
+ border-radius: 0;
159
+ }
160
+ </style>
@@ -0,0 +1,137 @@
1
+ <script lang="ts">
2
+ import { tick, type Snippet } from "svelte";
3
+
4
+ interface TableColumn {
5
+ header: string;
6
+ key: string;
7
+ align?: "left" | "right" | "center";
8
+ }
9
+
10
+ interface TableRow {
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ let {
15
+ columns,
16
+ rows,
17
+ children,
18
+ hideHeader,
19
+ }: {
20
+ columns: TableColumn[];
21
+ rows: TableRow[];
22
+ children: Snippet;
23
+ hideHeader?: boolean;
24
+ } = $props();
25
+
26
+ let showTooltip = $state(false);
27
+ let tooltipElement = $state<HTMLDivElement>();
28
+ let containerElement = $state<HTMLDivElement>();
29
+
30
+ let tooltipPosition = $state({
31
+ left: "",
32
+ right: "",
33
+ top: "",
34
+ bottom: "",
35
+ marginTop: "",
36
+ marginBottom: "",
37
+ });
38
+
39
+ function handleMouseEnter() {
40
+ showTooltip = true;
41
+ }
42
+
43
+ function handleMouseLeave() {
44
+ showTooltip = false;
45
+ }
46
+
47
+ $effect(() => {
48
+ if (showTooltip && tooltipElement && containerElement) {
49
+ tick().then(() => {
50
+ if (!containerElement || !tooltipElement) return;
51
+
52
+ const tooltipRect = tooltipElement.getBoundingClientRect();
53
+ const viewportWidth = window.innerWidth;
54
+ const viewportHeight = window.innerHeight;
55
+
56
+ // Reset positioning
57
+ tooltipPosition = {
58
+ left: "",
59
+ right: "",
60
+ top: "",
61
+ bottom: "",
62
+ marginTop: "",
63
+ marginBottom: "",
64
+ };
65
+
66
+ // Position horizontally
67
+ if (tooltipRect.right > viewportWidth) {
68
+ tooltipPosition.right = "0";
69
+ } else {
70
+ tooltipPosition.left = "0";
71
+ }
72
+
73
+ // Position vertically
74
+ if (tooltipRect.bottom > viewportHeight) {
75
+ tooltipPosition.bottom = "100%";
76
+ tooltipPosition.marginBottom = "5px";
77
+ } else {
78
+ tooltipPosition.top = "100%";
79
+ tooltipPosition.marginTop = "5px";
80
+ }
81
+ });
82
+ }
83
+ });
84
+ </script>
85
+
86
+ <div class="relative inline-block" bind:this={containerElement}>
87
+ <button class="dotted text-center" onmouseenter={handleMouseEnter} onmouseleave={handleMouseLeave}>
88
+ {@render children?.()}
89
+ </button>
90
+ {#if showTooltip}
91
+ <div
92
+ class="absolute bg-[var(--color-2)] border border-[var(--color-3)] rounded p-0 z-[1000] whitespace-nowrap shadow-lg"
93
+ bind:this={tooltipElement}
94
+ style:left={tooltipPosition.left}
95
+ style:right={tooltipPosition.right}
96
+ style:top={tooltipPosition.top}
97
+ style:bottom={tooltipPosition.bottom}
98
+ style:margin-top={tooltipPosition.marginTop}
99
+ style:margin-bottom={tooltipPosition.marginBottom}
100
+ >
101
+ <table class="w-full border-collapse text-base font-medium">
102
+ {#if !hideHeader}
103
+ <thead>
104
+ <tr>
105
+ {#each columns as column, index (index)}
106
+ <th
107
+ class="px-2 py-1 border-b border-[var(--color-3)] bg-[var(--color-4)] font-bold"
108
+ class:text-left={column.align === "left" || !column.align}
109
+ class:text-right={column.align === "right"}
110
+ class:text-center={column.align === "center"}
111
+ >
112
+ {column.header}
113
+ </th>
114
+ {/each}
115
+ </tr>
116
+ </thead>
117
+ {/if}
118
+ <tbody>
119
+ {#each rows as row, index (index)}
120
+ <tr>
121
+ {#each columns as column, index (index)}
122
+ <td
123
+ class="px-2 py-1 border-b border-[var(--color-3)]"
124
+ class:text-left={column.align === "left" || !column.align}
125
+ class:text-right={column.align === "right"}
126
+ class:text-center={column.align === "center"}
127
+ >
128
+ {row[column.key]}
129
+ </td>
130
+ {/each}
131
+ </tr>
132
+ {/each}
133
+ </tbody>
134
+ </table>
135
+ </div>
136
+ {/if}
137
+ </div>
@@ -0,0 +1,105 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+
5
+ export interface Host {
6
+ path: string;
7
+ _id: string;
8
+ }
9
+
10
+ const DEFAULT_HOSTS = process.env.MONGOKU_DEFAULT_HOST
11
+ ? process.env.MONGOKU_DEFAULT_HOST.split(";")
12
+ : ["localhost:27017"];
13
+ const DATABASE_FILE = process.env.MONGOKU_DATABASE_FILE || path.join(os.homedir(), ".mongoku.db");
14
+
15
+ export class HostsManager {
16
+ private _hosts: Map<string, string> = new Map(); // path -> _id
17
+
18
+ async load() {
19
+ let first = false;
20
+ try {
21
+ await fs.promises.stat(DATABASE_FILE);
22
+ } catch {
23
+ first = true;
24
+ }
25
+
26
+ if (!first) {
27
+ await this._loadFromFile();
28
+ }
29
+
30
+ if (first || this._hosts.size === 0) {
31
+ // Initialize with default hosts
32
+ for (const hostname of DEFAULT_HOSTS) {
33
+ this._hosts.set(hostname, this._generateId());
34
+ }
35
+ await this._saveToFile();
36
+ }
37
+ }
38
+
39
+ private async _loadFromFile(): Promise<void> {
40
+ const content = await fs.promises.readFile(DATABASE_FILE, "utf8");
41
+ const lines = content
42
+ .trim()
43
+ .split("\n")
44
+ .filter((line) => line.trim());
45
+
46
+ const newHosts = new Map<string, string>();
47
+ for (const line of lines) {
48
+ const host = JSON.parse(line);
49
+ if (host && typeof host.path === "string") {
50
+ // Use existing _id if available, generate new one if not
51
+ const id = host._id || this._generateId();
52
+ newHosts.set(host.path, id);
53
+ }
54
+ }
55
+ this._hosts = newHosts;
56
+ }
57
+
58
+ private async _saveToFile(): Promise<void> {
59
+ const lines = Array.from(this._hosts).map(([hostPath, id]) => JSON.stringify({ path: hostPath, _id: id }));
60
+ await fs.promises.writeFile(DATABASE_FILE, lines.join("\n") + "\n", "utf8");
61
+ }
62
+
63
+ private _generateId(): string {
64
+ // Generate a NeDB-compatible ID (16 characters, alphanumeric)
65
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
66
+ let result = "";
67
+ for (let i = 0; i < 16; i++) {
68
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
69
+ }
70
+ return result;
71
+ }
72
+
73
+ async getHosts(): Promise<Host[]> {
74
+ return Array.from(this._hosts).map(([hostPath, id]) => ({ path: hostPath, _id: id }));
75
+ }
76
+
77
+ async add(hostPath: string): Promise<string> {
78
+ // Use existing ID if host already exists, generate new one if not
79
+ let id = this._hosts.get(hostPath);
80
+ if (!id) {
81
+ id = this._generateId();
82
+ this._hosts.set(hostPath, id);
83
+ }
84
+ await this._saveToFile();
85
+ return id;
86
+ }
87
+
88
+ async remove(hostPath: string): Promise<void> {
89
+ // Remove exact matches and regex pattern matches
90
+ const toRemove = Array.from(this._hosts.keys()).filter((existingPath) => {
91
+ try {
92
+ const regex = new RegExp(hostPath);
93
+ return existingPath === hostPath || regex.test(existingPath);
94
+ } catch {
95
+ // If hostPath is not a valid regex, just do exact match
96
+ return existingPath === hostPath;
97
+ }
98
+ });
99
+
100
+ for (const host of toRemove) {
101
+ this._hosts.delete(host);
102
+ }
103
+ await this._saveToFile();
104
+ }
105
+ }