mongoku 2.0.2 → 2.0.4

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 (239) hide show
  1. package/Dockerfile +29 -0
  2. package/README.md +9 -7
  3. package/build/client/_app/immutable/assets/0.CEsPYTp2.css +1 -0
  4. package/build/client/_app/immutable/assets/0.CEsPYTp2.css.br +0 -0
  5. package/build/client/_app/immutable/assets/0.CEsPYTp2.css.gz +0 -0
  6. package/build/client/_app/immutable/chunks/B2-iEFN3.js +2 -0
  7. package/build/client/_app/immutable/chunks/B2-iEFN3.js.br +0 -0
  8. package/build/client/_app/immutable/chunks/B2-iEFN3.js.gz +0 -0
  9. package/build/client/_app/immutable/chunks/B5y2vsNr.js +1 -0
  10. package/build/client/_app/immutable/chunks/B5y2vsNr.js.br +0 -0
  11. package/build/client/_app/immutable/chunks/B5y2vsNr.js.gz +0 -0
  12. package/build/client/_app/immutable/chunks/C1z4TjN7.js +1 -0
  13. package/build/client/_app/immutable/chunks/C1z4TjN7.js.br +0 -0
  14. package/build/client/_app/immutable/chunks/C1z4TjN7.js.gz +0 -0
  15. package/build/client/_app/immutable/chunks/CQBaNlA9.js +1 -0
  16. package/build/client/_app/immutable/chunks/CQBaNlA9.js.br +0 -0
  17. package/build/client/_app/immutable/chunks/CQBaNlA9.js.gz +0 -0
  18. package/build/client/_app/immutable/chunks/CVoOGazw.js +4 -0
  19. package/build/client/_app/immutable/chunks/CVoOGazw.js.br +0 -0
  20. package/build/client/_app/immutable/chunks/CVoOGazw.js.gz +0 -0
  21. package/build/client/_app/immutable/chunks/CapOg2jD.js +1 -0
  22. package/build/client/_app/immutable/chunks/CapOg2jD.js.br +0 -0
  23. package/build/client/_app/immutable/chunks/CapOg2jD.js.gz +0 -0
  24. package/build/client/_app/immutable/chunks/{CGIdus8b.js → ChWB6xnu.js} +1 -1
  25. package/build/client/_app/immutable/chunks/ChWB6xnu.js.br +0 -0
  26. package/build/client/_app/immutable/chunks/ChWB6xnu.js.gz +0 -0
  27. package/build/client/_app/immutable/chunks/{MHUppGzk.js → CqnBT2zJ.js} +1 -1
  28. package/build/client/_app/immutable/chunks/CqnBT2zJ.js.br +0 -0
  29. package/build/client/_app/immutable/chunks/CqnBT2zJ.js.gz +0 -0
  30. package/build/client/_app/immutable/chunks/{BdWVCPGW.js → D069mncP.js} +1 -1
  31. package/build/client/_app/immutable/chunks/D069mncP.js.br +0 -0
  32. package/build/client/_app/immutable/chunks/D069mncP.js.gz +0 -0
  33. package/build/client/_app/immutable/chunks/{CN-ecO3-.js → DACrOfV_.js} +9 -9
  34. package/build/client/_app/immutable/chunks/DACrOfV_.js.br +0 -0
  35. package/build/client/_app/immutable/chunks/DACrOfV_.js.gz +0 -0
  36. package/build/client/_app/immutable/chunks/{BKCvWebR.js → DQkpHJGc.js} +1 -1
  37. package/build/client/_app/immutable/chunks/DQkpHJGc.js.br +0 -0
  38. package/build/client/_app/immutable/chunks/{BKCvWebR.js.gz → DQkpHJGc.js.gz} +0 -0
  39. package/build/client/_app/immutable/chunks/DSDD_hFT.js +1 -0
  40. package/build/client/_app/immutable/chunks/DSDD_hFT.js.br +0 -0
  41. package/build/client/_app/immutable/chunks/DSDD_hFT.js.gz +0 -0
  42. package/build/client/_app/immutable/chunks/D_aTX_ol.js +1 -0
  43. package/build/client/_app/immutable/chunks/D_aTX_ol.js.br +0 -0
  44. package/build/client/_app/immutable/chunks/D_aTX_ol.js.gz +0 -0
  45. package/build/client/_app/immutable/chunks/Dg3qHExF.js +1 -0
  46. package/build/client/_app/immutable/chunks/Dg3qHExF.js.br +0 -0
  47. package/build/client/_app/immutable/chunks/Dg3qHExF.js.gz +0 -0
  48. package/build/client/_app/immutable/chunks/{BaB-cr9N.js → Dt6-JRym.js} +1 -1
  49. package/build/client/_app/immutable/chunks/Dt6-JRym.js.br +0 -0
  50. package/build/client/_app/immutable/chunks/{BaB-cr9N.js.gz → Dt6-JRym.js.gz} +0 -0
  51. package/build/client/_app/immutable/chunks/QoKxaG0e.js +1 -0
  52. package/build/client/_app/immutable/chunks/QoKxaG0e.js.br +2 -0
  53. package/build/client/_app/immutable/chunks/QoKxaG0e.js.gz +0 -0
  54. package/build/client/_app/immutable/chunks/rtveiEyS.js +1 -0
  55. package/build/client/_app/immutable/chunks/rtveiEyS.js.br +0 -0
  56. package/build/client/_app/immutable/chunks/rtveiEyS.js.gz +0 -0
  57. package/build/client/_app/immutable/entry/app.Cc1FNLNH.js +2 -0
  58. package/build/client/_app/immutable/entry/app.Cc1FNLNH.js.br +0 -0
  59. package/build/client/_app/immutable/entry/app.Cc1FNLNH.js.gz +0 -0
  60. package/build/client/_app/immutable/entry/start.CwqlbFyy.js +1 -0
  61. package/build/client/_app/immutable/entry/start.CwqlbFyy.js.br +2 -0
  62. package/build/client/_app/immutable/entry/start.CwqlbFyy.js.gz +0 -0
  63. package/build/client/_app/immutable/nodes/0.wBQEpdjZ.js +1 -0
  64. package/build/client/_app/immutable/nodes/0.wBQEpdjZ.js.br +0 -0
  65. package/build/client/_app/immutable/nodes/0.wBQEpdjZ.js.gz +0 -0
  66. package/build/client/_app/immutable/nodes/1.K6-n7D0b.js +1 -0
  67. package/build/client/_app/immutable/nodes/1.K6-n7D0b.js.br +0 -0
  68. package/build/client/_app/immutable/nodes/1.K6-n7D0b.js.gz +0 -0
  69. package/build/client/_app/immutable/nodes/3.DkGcIF98.js +1 -0
  70. package/build/client/_app/immutable/nodes/3.DkGcIF98.js.br +0 -0
  71. package/build/client/_app/immutable/nodes/3.DkGcIF98.js.gz +0 -0
  72. package/build/client/_app/immutable/nodes/{4.DQfaAvJi.js → 4.C_NxbT98.js} +1 -1
  73. package/build/client/_app/immutable/nodes/4.C_NxbT98.js.br +0 -0
  74. package/build/client/_app/immutable/nodes/4.C_NxbT98.js.gz +0 -0
  75. package/build/client/_app/immutable/nodes/{5.B1E6iW2R.js → 5.CFvfYaLM.js} +1 -1
  76. package/build/client/_app/immutable/nodes/5.CFvfYaLM.js.br +0 -0
  77. package/build/client/_app/immutable/nodes/5.CFvfYaLM.js.gz +0 -0
  78. package/build/client/_app/immutable/nodes/6.h34Zyorq.js +1 -0
  79. package/build/client/_app/immutable/nodes/6.h34Zyorq.js.br +0 -0
  80. package/build/client/_app/immutable/nodes/6.h34Zyorq.js.gz +0 -0
  81. package/build/client/_app/immutable/nodes/{7.qpcLWZb7.js → 7.BVJmNbU5.js} +1 -1
  82. package/build/client/_app/immutable/nodes/7.BVJmNbU5.js.br +0 -0
  83. package/build/client/_app/immutable/nodes/7.BVJmNbU5.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-m42kIUxj.js → 0-Cb5ytlMI.js} +4 -4
  88. package/build/server/chunks/{0-m42kIUxj.js.map → 0-Cb5ytlMI.js.map} +1 -1
  89. package/build/server/chunks/1-Cpb7zRtJ.js +9 -0
  90. package/build/server/chunks/{1-uc74UVG3.js.map → 1-Cpb7zRtJ.js.map} +1 -1
  91. package/build/server/chunks/{3-Bi8teWON.js → 3-B_DkgZkc.js} +3 -3
  92. package/build/server/chunks/{3-Bi8teWON.js.map → 3-B_DkgZkc.js.map} +1 -1
  93. package/build/server/chunks/{4-u1WGAtFU.js → 4-DzHoLo__.js} +3 -3
  94. package/build/server/chunks/{4-u1WGAtFU.js.map → 4-DzHoLo__.js.map} +1 -1
  95. package/build/server/chunks/{5-BlGdcdjs.js → 5-CCWbCDpk.js} +3 -3
  96. package/build/server/chunks/{5-BlGdcdjs.js.map → 5-CCWbCDpk.js.map} +1 -1
  97. package/build/server/chunks/{6-YCp6xyCU.js → 6-Dok8svjB.js} +3 -3
  98. package/build/server/chunks/{6-YCp6xyCU.js.map → 6-Dok8svjB.js.map} +1 -1
  99. package/build/server/chunks/{7-ieA4k9K_.js → 7-CmgJSG2j.js} +3 -3
  100. package/build/server/chunks/{7-ieA4k9K_.js.map → 7-CmgJSG2j.js.map} +1 -1
  101. package/build/server/chunks/{Panel-Box8ld78.js → Panel-Bz0b85EW.js} +2 -2
  102. package/build/server/chunks/{Panel-Box8ld78.js.map → Panel-Bz0b85EW.js.map} +1 -1
  103. package/build/server/chunks/{PrettyJson-C2c9xGNo.js → PrettyJson-CX6-dNsW.js} +6 -5
  104. package/build/server/chunks/PrettyJson-CX6-dNsW.js.map +1 -0
  105. package/build/server/chunks/{_layout.svelte-CU7AVRSZ.js → _layout.svelte-UiDiCQEz.js} +3 -3
  106. package/build/server/chunks/{_layout.svelte-CU7AVRSZ.js.map → _layout.svelte-UiDiCQEz.js.map} +1 -1
  107. package/build/server/chunks/{_page.svelte-B_T8EFk3.js → _page.svelte-FIp-IsOo.js} +4 -4
  108. package/build/server/chunks/{_page.svelte-B_T8EFk3.js.map → _page.svelte-FIp-IsOo.js.map} +1 -1
  109. package/build/server/chunks/{_page.svelte-zlHeUnJK.js → _page.svelte-FLzHOuyK.js} +5 -5
  110. package/build/server/chunks/{_page.svelte-zlHeUnJK.js.map → _page.svelte-FLzHOuyK.js.map} +1 -1
  111. package/build/server/chunks/{_page.svelte-CflJkclc.js → _page.svelte-jxFKbZdN.js} +4 -4
  112. package/build/server/chunks/{_page.svelte-CflJkclc.js.map → _page.svelte-jxFKbZdN.js.map} +1 -1
  113. package/build/server/chunks/{_page.svelte-D1QIZqLv.js → _page.svelte-k8avSQW1.js} +5 -5
  114. package/build/server/chunks/{_page.svelte-D1QIZqLv.js.map → _page.svelte-k8avSQW1.js.map} +1 -1
  115. package/build/server/chunks/{_page.svelte-PuIVQ52p.js → _page.svelte-w9cxGvUf.js} +4 -4
  116. package/build/server/chunks/{_page.svelte-PuIVQ52p.js.map → _page.svelte-w9cxGvUf.js.map} +1 -1
  117. package/build/server/chunks/{routing-Ddj5b-2K.js → routing-DcR4fCSD.js} +6 -2
  118. package/build/server/chunks/routing-DcR4fCSD.js.map +1 -0
  119. package/build/server/chunks/{server2-BOoUfKd7.js → server2-BiqGvkCy.js} +2 -2
  120. package/build/server/chunks/{server2-BOoUfKd7.js.map → server2-BiqGvkCy.js.map} +1 -1
  121. package/build/server/index.js +2 -2
  122. package/build/server/index.js.map +1 -1
  123. package/build/server/manifest.js +8 -8
  124. package/build/server/manifest.js.map +1 -1
  125. package/cli.ts +154 -0
  126. package/dist/cli.js +8 -3
  127. package/ecosystem.config.js +6 -0
  128. package/package.json +10 -2
  129. package/src/api/servers.remote.ts +98 -0
  130. package/src/app.css +228 -0
  131. package/src/app.d.ts +16 -0
  132. package/src/app.html +11 -0
  133. package/src/hooks.server.ts +34 -0
  134. package/src/lib/components/Breadcrumbs.svelte +133 -0
  135. package/src/lib/components/JsonValue.svelte +248 -0
  136. package/src/lib/components/Notifications.svelte +81 -0
  137. package/src/lib/components/Panel.svelte +37 -0
  138. package/src/lib/components/PrettyJson.svelte +197 -0
  139. package/src/lib/components/SearchBox.svelte +160 -0
  140. package/src/lib/components/TooltipTable.svelte +137 -0
  141. package/src/lib/server/HostsManager.ts +105 -0
  142. package/src/lib/server/JsonEncoder.ts +62 -0
  143. package/src/lib/server/mongo.ts +199 -0
  144. package/src/lib/stores/notifications.svelte.ts +45 -0
  145. package/src/lib/types.ts +56 -0
  146. package/src/lib/utils/filters.ts +25 -0
  147. package/src/lib/utils/jsonParser.ts +125 -0
  148. package/src/routes/+layout.server.ts +7 -0
  149. package/src/routes/+layout.svelte +27 -0
  150. package/src/routes/+page.server.ts +6 -0
  151. package/src/routes/servers/+page.server.ts +53 -0
  152. package/src/routes/servers/+page.svelte +196 -0
  153. package/src/routes/servers/[server]/databases/+page.server.ts +47 -0
  154. package/src/routes/servers/[server]/databases/+page.svelte +88 -0
  155. package/src/routes/servers/[server]/databases/[database]/collections/+page.server.ts +21 -0
  156. package/src/routes/servers/[server]/databases/[database]/collections/+page.svelte +110 -0
  157. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.server.ts +106 -0
  158. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.svelte +174 -0
  159. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.server.ts +25 -0
  160. package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.svelte +90 -0
  161. package/src/tests/api/readonly.test.ts +89 -0
  162. package/src/tests/setup.ts +19 -0
  163. package/svelte.config.js +28 -0
  164. package/tsconfig.cli.json +15 -0
  165. package/tsconfig.json +19 -0
  166. package/vite.config.ts +7 -0
  167. package/build/client/_app/immutable/assets/0.BHsXFfsc.css +0 -1
  168. package/build/client/_app/immutable/assets/0.BHsXFfsc.css.br +0 -0
  169. package/build/client/_app/immutable/assets/0.BHsXFfsc.css.gz +0 -0
  170. package/build/client/_app/immutable/chunks/BFhvhM4X.js +0 -2
  171. package/build/client/_app/immutable/chunks/BFhvhM4X.js.br +0 -0
  172. package/build/client/_app/immutable/chunks/BFhvhM4X.js.gz +0 -0
  173. package/build/client/_app/immutable/chunks/BKCvWebR.js.br +0 -0
  174. package/build/client/_app/immutable/chunks/BY57TmdO.js +0 -1
  175. package/build/client/_app/immutable/chunks/BY57TmdO.js.br +0 -0
  176. package/build/client/_app/immutable/chunks/BY57TmdO.js.gz +0 -0
  177. package/build/client/_app/immutable/chunks/BaB-cr9N.js.br +0 -0
  178. package/build/client/_app/immutable/chunks/BdWVCPGW.js.br +0 -0
  179. package/build/client/_app/immutable/chunks/BdWVCPGW.js.gz +0 -0
  180. package/build/client/_app/immutable/chunks/ByjLCPv2.js +0 -1
  181. package/build/client/_app/immutable/chunks/ByjLCPv2.js.br +0 -0
  182. package/build/client/_app/immutable/chunks/ByjLCPv2.js.gz +0 -0
  183. package/build/client/_app/immutable/chunks/CGIdus8b.js.br +0 -0
  184. package/build/client/_app/immutable/chunks/CGIdus8b.js.gz +0 -0
  185. package/build/client/_app/immutable/chunks/CN-ecO3-.js.br +0 -0
  186. package/build/client/_app/immutable/chunks/CN-ecO3-.js.gz +0 -0
  187. package/build/client/_app/immutable/chunks/D17Lj1H7.js +0 -1
  188. package/build/client/_app/immutable/chunks/D17Lj1H7.js.br +0 -0
  189. package/build/client/_app/immutable/chunks/D17Lj1H7.js.gz +0 -0
  190. package/build/client/_app/immutable/chunks/D72Du7TF.js +0 -1
  191. package/build/client/_app/immutable/chunks/D72Du7TF.js.br +0 -2
  192. package/build/client/_app/immutable/chunks/D72Du7TF.js.gz +0 -0
  193. package/build/client/_app/immutable/chunks/DB3PPjLu.js +0 -4
  194. package/build/client/_app/immutable/chunks/DB3PPjLu.js.br +0 -0
  195. package/build/client/_app/immutable/chunks/DB3PPjLu.js.gz +0 -0
  196. package/build/client/_app/immutable/chunks/MHUppGzk.js.br +0 -0
  197. package/build/client/_app/immutable/chunks/MHUppGzk.js.gz +0 -0
  198. package/build/client/_app/immutable/chunks/_2kcttvK.js +0 -1
  199. package/build/client/_app/immutable/chunks/_2kcttvK.js.br +0 -0
  200. package/build/client/_app/immutable/chunks/_2kcttvK.js.gz +0 -0
  201. package/build/client/_app/immutable/chunks/iHsChU5o.js +0 -1
  202. package/build/client/_app/immutable/chunks/iHsChU5o.js.br +0 -0
  203. package/build/client/_app/immutable/chunks/iHsChU5o.js.gz +0 -0
  204. package/build/client/_app/immutable/chunks/p23QjN60.js +0 -1
  205. package/build/client/_app/immutable/chunks/p23QjN60.js.br +0 -2
  206. package/build/client/_app/immutable/chunks/p23QjN60.js.gz +0 -0
  207. package/build/client/_app/immutable/chunks/sBKW2MYH.js +0 -1
  208. package/build/client/_app/immutable/chunks/sBKW2MYH.js.br +0 -0
  209. package/build/client/_app/immutable/chunks/sBKW2MYH.js.gz +0 -0
  210. package/build/client/_app/immutable/chunks/zXvB9_Mi.js +0 -1
  211. package/build/client/_app/immutable/chunks/zXvB9_Mi.js.br +0 -0
  212. package/build/client/_app/immutable/chunks/zXvB9_Mi.js.gz +0 -0
  213. package/build/client/_app/immutable/entry/app.hGE78f-O.js +0 -2
  214. package/build/client/_app/immutable/entry/app.hGE78f-O.js.br +0 -0
  215. package/build/client/_app/immutable/entry/app.hGE78f-O.js.gz +0 -0
  216. package/build/client/_app/immutable/entry/start._GE1Zd3d.js +0 -1
  217. package/build/client/_app/immutable/entry/start._GE1Zd3d.js.br +0 -2
  218. package/build/client/_app/immutable/entry/start._GE1Zd3d.js.gz +0 -0
  219. package/build/client/_app/immutable/nodes/0.DyBXVtnT.js +0 -1
  220. package/build/client/_app/immutable/nodes/0.DyBXVtnT.js.br +0 -0
  221. package/build/client/_app/immutable/nodes/0.DyBXVtnT.js.gz +0 -0
  222. package/build/client/_app/immutable/nodes/1.FqB0jq88.js +0 -1
  223. package/build/client/_app/immutable/nodes/1.FqB0jq88.js.br +0 -2
  224. package/build/client/_app/immutable/nodes/1.FqB0jq88.js.gz +0 -0
  225. package/build/client/_app/immutable/nodes/3.Bekn_8hM.js +0 -1
  226. package/build/client/_app/immutable/nodes/3.Bekn_8hM.js.br +0 -0
  227. package/build/client/_app/immutable/nodes/3.Bekn_8hM.js.gz +0 -0
  228. package/build/client/_app/immutable/nodes/4.DQfaAvJi.js.br +0 -0
  229. package/build/client/_app/immutable/nodes/4.DQfaAvJi.js.gz +0 -0
  230. package/build/client/_app/immutable/nodes/5.B1E6iW2R.js.br +0 -0
  231. package/build/client/_app/immutable/nodes/5.B1E6iW2R.js.gz +0 -0
  232. package/build/client/_app/immutable/nodes/6.28eZQkvz.js +0 -1
  233. package/build/client/_app/immutable/nodes/6.28eZQkvz.js.br +0 -0
  234. package/build/client/_app/immutable/nodes/6.28eZQkvz.js.gz +0 -0
  235. package/build/client/_app/immutable/nodes/7.qpcLWZb7.js.br +0 -0
  236. package/build/client/_app/immutable/nodes/7.qpcLWZb7.js.gz +0 -0
  237. package/build/server/chunks/1-uc74UVG3.js +0 -9
  238. package/build/server/chunks/PrettyJson-C2c9xGNo.js.map +0 -1
  239. package/build/server/chunks/routing-Ddj5b-2K.js.map +0 -1
@@ -0,0 +1,133 @@
1
+ <script lang="ts">
2
+ import { resolve } from "$app/paths";
3
+ import { page } from "$app/state";
4
+ import { serverName } from "$lib/utils/filters";
5
+
6
+ interface BreadcrumbItem {
7
+ label: string;
8
+ href?: string;
9
+ }
10
+
11
+ function getBreadcrumbs(pathname: string, params: Record<string, string>): BreadcrumbItem[] {
12
+ const breadcrumbs: BreadcrumbItem[] = [];
13
+
14
+ // Parse the path segments
15
+ const segments = pathname.split("/").filter(Boolean);
16
+
17
+ if (segments.length === 0) {
18
+ // We're at home, no breadcrumbs needed
19
+ return [];
20
+ }
21
+
22
+ if (segments[0] === "servers" && segments.length >= 3) {
23
+ const server = decodeURIComponent(params.server || "");
24
+ const serverDisplayName = serverName(server);
25
+
26
+ if (segments[2] === "databases") {
27
+ // /servers/{server}/databases
28
+ breadcrumbs.push({
29
+ label: serverDisplayName,
30
+ href: `/servers/${encodeURIComponent(server)}/databases`,
31
+ });
32
+
33
+ if (segments.length >= 5 && segments[3] && segments[4] === "collections") {
34
+ const database = decodeURIComponent(params.database || "");
35
+ // /servers/{server}/databases/{database}/collections
36
+ breadcrumbs.push({
37
+ label: database,
38
+ href: `/servers/${encodeURIComponent(server)}/databases/${encodeURIComponent(database)}/collections`,
39
+ });
40
+
41
+ if (segments.length >= 6 && segments[5]) {
42
+ const collection = decodeURIComponent(params.collection || "");
43
+
44
+ if (segments.length === 6) {
45
+ // /servers/{server}/databases/{database}/collections/{collection}
46
+ breadcrumbs.push({
47
+ label: collection,
48
+ });
49
+ } else if (segments.length >= 7 && segments[6] === "documents" && segments[7]) {
50
+ // /servers/{server}/databases/{database}/collections/{collection}/documents/{document}
51
+ breadcrumbs.push({
52
+ label: collection,
53
+ href: `/servers/${encodeURIComponent(server)}/databases/${encodeURIComponent(database)}/collections/${encodeURIComponent(collection)}`,
54
+ });
55
+
56
+ breadcrumbs.push({
57
+ label: decodeURIComponent(params.document || "[Document]"),
58
+ });
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ return breadcrumbs;
66
+ }
67
+
68
+ const breadcrumbs = $derived(getBreadcrumbs(page.url.pathname, page.params));
69
+ </script>
70
+
71
+ {#if breadcrumbs.length > 0}
72
+ <nav class="breadcrumbs" aria-label="Breadcrumb">
73
+ <ol class="breadcrumb-list">
74
+ {#each breadcrumbs as crumb, index (index)}
75
+ <li class="breadcrumb-item">
76
+ {#if crumb.href && index < breadcrumbs.length - 1}
77
+ <!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
78
+ <a href={resolve(crumb.href as any)} class="breadcrumb-link">{crumb.label}</a>
79
+ {:else}
80
+ <span class="breadcrumb-current">{crumb.label}</span>
81
+ {/if}
82
+ {#if index < breadcrumbs.length - 1}
83
+ <span class="breadcrumb-separator">/</span>
84
+ {/if}
85
+ </li>
86
+ {/each}
87
+ </ol>
88
+ </nav>
89
+ {/if}
90
+
91
+ <style lang="postcss">
92
+ .breadcrumbs {
93
+ margin-left: 20px;
94
+ font-size: 16px;
95
+ }
96
+
97
+ .breadcrumb-list {
98
+ display: flex;
99
+ align-items: center;
100
+ list-style: none;
101
+ margin: 0;
102
+ padding: 0;
103
+ gap: 8px;
104
+ }
105
+
106
+ .breadcrumb-item {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 8px;
110
+ }
111
+
112
+ .breadcrumb-link {
113
+ color: var(--text);
114
+ text-decoration: none;
115
+ transition: color 0.2s;
116
+
117
+ &:hover {
118
+ color: var(--text);
119
+ text-decoration: underline;
120
+ }
121
+ }
122
+
123
+ .breadcrumb-current {
124
+ color: var(--text-darker);
125
+ font-weight: 500;
126
+ }
127
+
128
+ .breadcrumb-separator {
129
+ color: var(--text-darker);
130
+ font-weight: 300;
131
+ user-select: none;
132
+ }
133
+ </style>
@@ -0,0 +1,248 @@
1
+ <script lang="ts">
2
+ import JsonValue from "./JsonValue.svelte";
3
+
4
+ const INDENT = " ";
5
+
6
+ interface Props {
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ value: any;
9
+ key?: string;
10
+ /** are sub items collapsed by default*/
11
+ autoCollapse?: boolean;
12
+ /** is it collapsed at top level */
13
+ collapsed?: boolean;
14
+ depth?: number;
15
+ localTopLevel?: boolean;
16
+ }
17
+
18
+ let { value, key, autoCollapse = false, collapsed = false, localTopLevel = true, depth = 0 }: Props = $props();
19
+
20
+ function toggleCollapse() {
21
+ collapsed = !collapsed;
22
+ }
23
+
24
+ function getIndent(level: number): string {
25
+ return INDENT.repeat(level);
26
+ }
27
+
28
+ function isUrl(str: string): boolean {
29
+ return /^https?:\/\/[^\s]+$/.test(str);
30
+ }
31
+
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ function getValueType(val: any): string {
34
+ if (val === null) return "null";
35
+ if (Array.isArray(val)) return "array";
36
+
37
+ switch (typeof val) {
38
+ case "boolean":
39
+ return "boolean";
40
+ case "number":
41
+ return "number";
42
+ case "string":
43
+ return "string";
44
+ case "object":
45
+ switch (val.$type) {
46
+ case "ObjectId":
47
+ return "objectid";
48
+ case "Date":
49
+ return "date";
50
+ case "RegExp":
51
+ return "regexp";
52
+ default:
53
+ return "object";
54
+ }
55
+ default:
56
+ return "unknown";
57
+ }
58
+ }
59
+
60
+ let valueType = $derived(getValueType(value));
61
+ let isCollapsible = $derived(valueType === "array" || valueType === "object");
62
+ let isEmpty = $derived(
63
+ (valueType === "array" && value.length === 0) || (valueType === "object" && Object.keys(value).length === 0),
64
+ );
65
+ </script>
66
+
67
+ {#if key !== undefined}
68
+ <span class="prop" class:collapsible={isCollapsible && !isEmpty} class:collapsed>
69
+ {getIndent(depth + 1)}<var>{key}</var>: <JsonValue
70
+ {value}
71
+ {autoCollapse}
72
+ collapsed={autoCollapse}
73
+ depth={depth + 1}
74
+ localTopLevel={false}
75
+ />
76
+ </span>
77
+ {:else}
78
+ <!-- Root value rendering -->
79
+ {#if valueType === "string"}
80
+ <span class="value quoted">
81
+ "<span class="string" class:url={isUrl(value)}>
82
+ {#if isUrl(value)}
83
+ <!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
84
+ <a href={value} target="_blank">{value}</a>
85
+ {:else}
86
+ {value}
87
+ {/if}
88
+ </span>"
89
+ </span>
90
+ {:else if valueType === "number"}
91
+ <span class="value number">{value}</span>
92
+ {:else if valueType === "boolean"}
93
+ <span class="value boolean">{value}</span>
94
+ {:else if valueType === "null"}
95
+ <span class="value null">null</span>
96
+ {:else if valueType === "objectid"}
97
+ <span class="call function">ObjectId("<span class="string">{value.$value}</span>")</span>
98
+ {:else if valueType === "date"}
99
+ <span class="call function">Date("<span class="string">{value.$value}</span>")</span>
100
+ {:else if valueType === "regexp"}
101
+ <span class="value regexp">/{value.$value.$pattern}/{value.$value.$flags}</span>
102
+ {:else if valueType === "array"}
103
+ {#if isEmpty}
104
+ <span class="value array">[]</span>
105
+ {:else}
106
+ <span class="collapsible-wrapper" class:collapsed class:expanded={!collapsed}>
107
+ <button class="collapse-toggle" onclick={toggleCollapse}>
108
+ {collapsed ? "▶" : "▼"}
109
+ </button><span class="value array">
110
+ [<span class="collapsible-content" class:hidden={collapsed}>
111
+ {#each value as item, i (i)}
112
+ <br />{getIndent(depth + 1)}<JsonValue
113
+ value={item}
114
+ {autoCollapse}
115
+ collapsed={false}
116
+ depth={depth + 1}
117
+ />{/each}
118
+ <br />{getIndent(depth)}
119
+ </span>{#if collapsed}
120
+ <span class="collapsed-summary">... {value.length} item{value.length !== 1 ? "s" : ""}</span>
121
+ {/if}]
122
+ </span>
123
+ </span>
124
+ {/if}
125
+ {:else if valueType === "object"}
126
+ {#if isEmpty}
127
+ <span class="value object">{"{}"}</span>
128
+ {:else}
129
+ <span class="collapsible-wrapper" class:collapsed class:expanded={!collapsed}>
130
+ {#if depth !== 0}<button class="collapse-toggle" class:local-top-level={localTopLevel} onclick={toggleCollapse}>
131
+ {collapsed ? "▶" : "▼"}
132
+ </button>{/if}<span class="value object">
133
+ {"{"}<span class="collapsible-content" class:hidden={collapsed}
134
+ >{#each Object.keys(value) as objKey (objKey)}
135
+ <JsonValue value={value[objKey]} key={objKey} {autoCollapse} collapsed={autoCollapse} {depth} />{/each}
136
+ </span>{#if collapsed}
137
+ <span class="collapsed-summary"
138
+ >... {Object.keys(value).length} key{Object.keys(value).length !== 1 ? "s" : ""}</span
139
+ >
140
+ {:else}
141
+ {getIndent(depth)}
142
+ {/if}}
143
+ </span>
144
+ </span>
145
+ {/if}
146
+ {:else}
147
+ <span>{String(value)}</span>
148
+ {/if}{#if depth !== 0},{/if}
149
+ {/if}
150
+
151
+ <style lang="postcss">
152
+ .prop {
153
+ position: relative;
154
+ display: block;
155
+ }
156
+
157
+ .collapsible-wrapper {
158
+ display: inline;
159
+ position: relative;
160
+
161
+ .collapse-toggle {
162
+ display: inline-block;
163
+ width: 16px;
164
+ cursor: pointer;
165
+ user-select: none;
166
+ color: var(--text-secondary, #888);
167
+ font-size: 12px;
168
+ margin-right: 4px;
169
+ transition: color 0.2s;
170
+ background: none;
171
+ border: none;
172
+ padding: 0;
173
+ font-family: inherit;
174
+
175
+ &.local-top-level {
176
+ position: absolute;
177
+ left: -18px;
178
+ margin-top: 4px;
179
+ }
180
+
181
+ &:hover {
182
+ color: var(--text, #fff);
183
+ }
184
+ }
185
+
186
+ &.collapsed .collapsible-content {
187
+ display: none;
188
+ }
189
+ }
190
+
191
+ .collapsible-content {
192
+ white-space: pre;
193
+
194
+ &.hidden {
195
+ display: none;
196
+ }
197
+ }
198
+
199
+ .collapsed-summary {
200
+ color: var(--text-secondary, #888);
201
+ font-style: italic;
202
+ margin: 0 4px;
203
+ }
204
+
205
+ .value {
206
+ &.number {
207
+ color: var(--code-numbers);
208
+ }
209
+
210
+ &.boolean {
211
+ color: var(--code-boolean);
212
+ }
213
+
214
+ &.null {
215
+ color: var(--code-null);
216
+ }
217
+
218
+ &.quoted {
219
+ .string {
220
+ color: var(--code-string);
221
+
222
+ &.url a {
223
+ color: var(--code-string);
224
+ text-decoration: underline;
225
+
226
+ &:hover {
227
+ color: var(--code-links);
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ &.regexp {
234
+ color: var(--code-regexp);
235
+ }
236
+ }
237
+
238
+ .call {
239
+ &.function {
240
+ color: var(--code-function);
241
+ }
242
+ }
243
+
244
+ var {
245
+ color: var(--code-namespace);
246
+ font-style: normal;
247
+ }
248
+ </style>
@@ -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,197 @@
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
+ let contentContainerRef = $state<HTMLDivElement>();
38
+ let editorHeight = $state<string>("300px");
39
+
40
+ // Extract timestamp from ObjectId
41
+ function getTimestampFromObjectId(objectId: string): Date | null {
42
+ try {
43
+ // ObjectId is 24 hex characters, first 8 represent timestamp
44
+ const timestamp = parseInt(objectId.substring(0, 8), 16);
45
+ return new Date(timestamp * 1000);
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ // Custom serializer that converts ObjectId objects back to new ObjectId() format
52
+ function serializeForEditing(obj: any, depth = 0): string {
53
+ const indent = " ".repeat(depth);
54
+ const nextIndent = " ".repeat(depth + 1);
55
+
56
+ if (obj === null) return "null";
57
+ if (obj === undefined) return "undefined";
58
+ if (typeof obj === "string") return JSON.stringify(obj);
59
+ if (typeof obj === "number") return obj.toString();
60
+ if (typeof obj === "boolean") return obj.toString();
61
+
62
+ if (Array.isArray(obj)) {
63
+ if (obj.length === 0) return "[]";
64
+ const items = obj.map((item) => `${nextIndent}${serializeForEditing(item, depth + 1)}`).join(",\n");
65
+ return `[\n${items}\n${indent}]`;
66
+ }
67
+
68
+ if (typeof obj === "object") {
69
+ // Handle special MongoDB types
70
+ if (obj.$type === "ObjectId") {
71
+ return `new ObjectId("${obj.$value}")`;
72
+ }
73
+ if (obj.$type === "Date") {
74
+ return `new Date("${obj.$value}")`;
75
+ }
76
+ if (obj.$type === "RegExp") {
77
+ return `new RegExp("${obj.$value.$pattern}", "${obj.$value.$flags}")`;
78
+ }
79
+
80
+ // Handle regular objects
81
+ const keys = Object.keys(obj);
82
+ if (keys.length === 0) return "{}";
83
+
84
+ const pairs = keys
85
+ .map((key) => {
86
+ const value = serializeForEditing(obj[key], depth + 1);
87
+ return `${nextIndent}${JSON.stringify(key)}: ${value}`;
88
+ })
89
+ .join(",\n");
90
+
91
+ return `{\n${pairs}\n${indent}}`;
92
+ }
93
+
94
+ return JSON.stringify(obj);
95
+ }
96
+
97
+ function enableEditor() {
98
+ editJson = serializeForEditing(json);
99
+
100
+ // Set editor height to match the full content container (including padding)
101
+ if (contentContainerRef) {
102
+ const height = contentContainerRef.offsetHeight;
103
+ editorHeight = `${Math.max(height, 300)}px`;
104
+ }
105
+
106
+ editorVisible = true;
107
+ }
108
+
109
+ function disableEditor() {
110
+ editorVisible = false;
111
+ editJson = "";
112
+ }
113
+
114
+ function save() {
115
+ try {
116
+ const updatedJson = parseJSON(editJson);
117
+ disableEditor();
118
+ onedit?.(updatedJson);
119
+ } catch (err) {
120
+ notificationStore.notifyError(err, "Invalid JSON");
121
+ }
122
+ }
123
+
124
+ function showRemove() {
125
+ removing = true;
126
+ }
127
+
128
+ function cancelRemove() {
129
+ removing = false;
130
+ }
131
+
132
+ function confirmRemove() {
133
+ onremove?.();
134
+ }
135
+ </script>
136
+
137
+ <Panel class="group">
138
+ {#snippet title()}
139
+ {#if json._id}
140
+ <div class="">
141
+ <a
142
+ type="button"
143
+ class="bg-transparent border-none text-[var(--text)] no-underline cursor-pointer text-xl font-inherit p-0 hover:underline"
144
+ href={resolve(
145
+ `/servers/${encodeURIComponent(server)}/databases/${encodeURIComponent(database)}/collections/${encodeURIComponent(collection)}/documents/${json._id?.$value}`,
146
+ )}
147
+ >
148
+ {json._id?.$value}
149
+ </a>
150
+ {#if json._id?.$value}
151
+ {@const timestamp = getTimestampFromObjectId(json._id.$value)}
152
+ {#if timestamp}
153
+ <span class="ml-2 text-md text-[var(--text-secondary,#888)]">{timestamp.toLocaleString()}</span>
154
+ {/if}
155
+ {/if}
156
+ </div>
157
+ {/if}
158
+ {/snippet}
159
+
160
+ {#snippet actions()}
161
+ {#if !readOnly}
162
+ <div class="hidden group-hover:block">
163
+ <button class="btn btn-outline-light btn-sm ml-2 -my-2" onclick={enableEditor}>Edit</button>
164
+ <button class="btn btn-outline-danger btn-sm ml-2 -my-2" onclick={showRemove}>Remove</button>
165
+ </div>
166
+ {/if}
167
+ {/snippet}
168
+
169
+ <div bind:this={contentContainerRef} class="p-4 relative border-t border-[var(--border-color)]">
170
+ <div class="font-mono text-sm leading-tight whitespace-pre-wrap break-words relative">
171
+ <JsonValue value={json} {autoCollapse} collapsed={false} />
172
+ </div>
173
+
174
+ <div class="absolute h-full z-[100] w-full top-0 left-0" class:hidden={!editorVisible} class:block={editorVisible}>
175
+ <div class="absolute z-10 right-5 top-4">
176
+ <button class="btn btn-success ml-2" onclick={save}>Save</button>
177
+ <button class="btn btn-default ml-2" onclick={disableEditor}>Cancel</button>
178
+ </div>
179
+ <textarea
180
+ bind:this={editorRef}
181
+ bind:value={editJson}
182
+ style="height: {editorHeight};"
183
+ class="w-full font-mono text-sm leading-relaxed p-2.5 bg-[var(--color-1)] text-[var(--text)] border border-[var(--border-color)] rounded resize-y"
184
+ ></textarea>
185
+ </div>
186
+
187
+ {#if removing}
188
+ <div class="absolute z-10 top-0 left-0 w-full h-full rounded bg-[var(--text-inverse)] opacity-70">
189
+ <p class="text-[var(--text)] text-center mt-5 text-2xl">Are you sure?</p>
190
+ <div class="absolute w-full h-full top-0 flex justify-center items-center">
191
+ <button class="btn btn-danger m-5" onclick={confirmRemove}>Yes - Remove</button>
192
+ <button class="btn btn-success m-5" onclick={cancelRemove}>No - Cancel</button>
193
+ </div>
194
+ </div>
195
+ {/if}
196
+ </div>
197
+ </Panel>