@vandenberghinc/volt 1.1.2

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 (451) hide show
  1. package/.vrepo +28 -0
  2. package/.vscode/tasks.json +87 -0
  3. package/README.md +67 -0
  4. package/backend/dist/cjs/blacklist.d.ts +10 -0
  5. package/backend/dist/cjs/blacklist.js +53 -0
  6. package/backend/dist/cjs/cli.d.ts +2 -0
  7. package/backend/dist/cjs/cli.js +263 -0
  8. package/backend/dist/cjs/database.d.ts +364 -0
  9. package/backend/dist/cjs/database.js +1962 -0
  10. package/backend/dist/cjs/endpoint.d.ts +57 -0
  11. package/backend/dist/cjs/endpoint.js +425 -0
  12. package/backend/dist/cjs/file_watcher.d.ts +44 -0
  13. package/backend/dist/cjs/file_watcher.js +348 -0
  14. package/backend/dist/cjs/frontend.d.ts +13 -0
  15. package/backend/dist/cjs/frontend.js +30 -0
  16. package/backend/dist/cjs/image_endpoint.d.ts +24 -0
  17. package/backend/dist/cjs/image_endpoint.js +210 -0
  18. package/backend/dist/cjs/logger.d.ts +5 -0
  19. package/backend/dist/cjs/logger.js +16 -0
  20. package/backend/dist/cjs/meta.d.ts +50 -0
  21. package/backend/dist/cjs/meta.js +153 -0
  22. package/backend/dist/cjs/mutex.d.ts +24 -0
  23. package/backend/dist/cjs/mutex.js +52 -0
  24. package/backend/dist/cjs/package.json +1 -0
  25. package/backend/dist/cjs/payments/paddle.d.ts +161 -0
  26. package/backend/dist/cjs/payments/paddle.js +2301 -0
  27. package/backend/dist/cjs/plugins/browser.d.ts +36 -0
  28. package/backend/dist/cjs/plugins/browser.js +183 -0
  29. package/backend/dist/cjs/plugins/communication.d.ts +70 -0
  30. package/backend/dist/cjs/plugins/communication.js +177 -0
  31. package/backend/dist/cjs/plugins/css.d.ts +10 -0
  32. package/backend/dist/cjs/plugins/css.js +71 -0
  33. package/backend/dist/cjs/plugins/mail.d.ts +277 -0
  34. package/backend/dist/cjs/plugins/mail.js +1419 -0
  35. package/backend/dist/cjs/plugins/pdf.d.ts +757 -0
  36. package/backend/dist/cjs/plugins/pdf.js +1694 -0
  37. package/backend/dist/cjs/plugins/thread_monitor.d.ts +18 -0
  38. package/backend/dist/cjs/plugins/thread_monitor.js +127 -0
  39. package/backend/dist/cjs/plugins/ts/compiler.d.ts +132 -0
  40. package/backend/dist/cjs/plugins/ts/compiler.js +944 -0
  41. package/backend/dist/cjs/plugins/ts/preprocessing.d.ts +14 -0
  42. package/backend/dist/cjs/plugins/ts/preprocessing.js +762 -0
  43. package/backend/dist/cjs/rate_limit.d.ts +65 -0
  44. package/backend/dist/cjs/rate_limit.js +463 -0
  45. package/backend/dist/cjs/request.deprc.d.ts +48 -0
  46. package/backend/dist/cjs/request.deprc.js +572 -0
  47. package/backend/dist/cjs/response.deprc.d.ts +55 -0
  48. package/backend/dist/cjs/response.deprc.js +275 -0
  49. package/backend/dist/cjs/server.d.ts +311 -0
  50. package/backend/dist/cjs/server.js +3475 -0
  51. package/backend/dist/cjs/splash_screen.d.ts +35 -0
  52. package/backend/dist/cjs/splash_screen.js +152 -0
  53. package/backend/dist/cjs/status.d.ts +60 -0
  54. package/backend/dist/cjs/status.js +199 -0
  55. package/backend/dist/cjs/stream.d.ts +75 -0
  56. package/backend/dist/cjs/stream.js +954 -0
  57. package/backend/dist/cjs/users.d.ts +111 -0
  58. package/backend/dist/cjs/users.js +1945 -0
  59. package/backend/dist/cjs/utils.d.ts +27 -0
  60. package/backend/dist/cjs/utils.js +329 -0
  61. package/backend/dist/cjs/view.d.ts +52 -0
  62. package/backend/dist/cjs/view.js +568 -0
  63. package/backend/dist/cjs/vinc.d.ts +2 -0
  64. package/backend/dist/cjs/vinc.dev.d.ts +2 -0
  65. package/backend/dist/cjs/vinc.dev.js +42 -0
  66. package/backend/dist/cjs/vinc.js +42 -0
  67. package/backend/dist/cjs/volt.d.ts +15 -0
  68. package/backend/dist/cjs/volt.js +64 -0
  69. package/backend/dist/css/adyen.css +92 -0
  70. package/backend/dist/css/volt.css +65 -0
  71. package/backend/dist/esm/blacklist.d.ts +10 -0
  72. package/backend/dist/esm/blacklist.js +49 -0
  73. package/backend/dist/esm/cli.d.ts +2 -0
  74. package/backend/dist/esm/cli.js +228 -0
  75. package/backend/dist/esm/database.d.ts +364 -0
  76. package/backend/dist/esm/database.js +1957 -0
  77. package/backend/dist/esm/endpoint.d.ts +57 -0
  78. package/backend/dist/esm/endpoint.js +421 -0
  79. package/backend/dist/esm/file_watcher.d.ts +44 -0
  80. package/backend/dist/esm/file_watcher.js +313 -0
  81. package/backend/dist/esm/frontend.d.ts +13 -0
  82. package/backend/dist/esm/frontend.js +27 -0
  83. package/backend/dist/esm/image_endpoint.d.ts +24 -0
  84. package/backend/dist/esm/image_endpoint.js +206 -0
  85. package/backend/dist/esm/logger.d.ts +5 -0
  86. package/backend/dist/esm/logger.js +13 -0
  87. package/backend/dist/esm/meta.d.ts +50 -0
  88. package/backend/dist/esm/meta.js +149 -0
  89. package/backend/dist/esm/mutex.d.ts +24 -0
  90. package/backend/dist/esm/mutex.js +48 -0
  91. package/backend/dist/esm/payments/paddle.d.ts +161 -0
  92. package/backend/dist/esm/payments/paddle.js +2261 -0
  93. package/backend/dist/esm/plugins/browser.d.ts +36 -0
  94. package/backend/dist/esm/plugins/browser.js +176 -0
  95. package/backend/dist/esm/plugins/communication.d.ts +70 -0
  96. package/backend/dist/esm/plugins/communication.js +169 -0
  97. package/backend/dist/esm/plugins/css.d.ts +10 -0
  98. package/backend/dist/esm/plugins/css.js +64 -0
  99. package/backend/dist/esm/plugins/mail.d.ts +277 -0
  100. package/backend/dist/esm/plugins/mail.js +1403 -0
  101. package/backend/dist/esm/plugins/pdf.d.ts +757 -0
  102. package/backend/dist/esm/plugins/pdf.js +1694 -0
  103. package/backend/dist/esm/plugins/thread_monitor.d.ts +18 -0
  104. package/backend/dist/esm/plugins/thread_monitor.js +120 -0
  105. package/backend/dist/esm/plugins/ts/compiler.d.ts +132 -0
  106. package/backend/dist/esm/plugins/ts/compiler.js +907 -0
  107. package/backend/dist/esm/plugins/ts/preprocessing.d.ts +14 -0
  108. package/backend/dist/esm/plugins/ts/preprocessing.js +724 -0
  109. package/backend/dist/esm/rate_limit.d.ts +65 -0
  110. package/backend/dist/esm/rate_limit.js +425 -0
  111. package/backend/dist/esm/request.deprc.d.ts +48 -0
  112. package/backend/dist/esm/request.deprc.js +572 -0
  113. package/backend/dist/esm/response.deprc.d.ts +55 -0
  114. package/backend/dist/esm/response.deprc.js +275 -0
  115. package/backend/dist/esm/server.d.ts +311 -0
  116. package/backend/dist/esm/server.js +3435 -0
  117. package/backend/dist/esm/splash_screen.d.ts +35 -0
  118. package/backend/dist/esm/splash_screen.js +148 -0
  119. package/backend/dist/esm/status.d.ts +60 -0
  120. package/backend/dist/esm/status.js +196 -0
  121. package/backend/dist/esm/stream.d.ts +75 -0
  122. package/backend/dist/esm/stream.js +947 -0
  123. package/backend/dist/esm/users.d.ts +111 -0
  124. package/backend/dist/esm/users.js +1908 -0
  125. package/backend/dist/esm/utils.d.ts +27 -0
  126. package/backend/dist/esm/utils.js +324 -0
  127. package/backend/dist/esm/view.d.ts +52 -0
  128. package/backend/dist/esm/view.js +561 -0
  129. package/backend/dist/esm/vinc.d.ts +2 -0
  130. package/backend/dist/esm/vinc.dev.d.ts +2 -0
  131. package/backend/dist/esm/vinc.dev.js +6 -0
  132. package/backend/dist/esm/vinc.js +6 -0
  133. package/backend/dist/esm/volt.d.ts +15 -0
  134. package/backend/dist/esm/volt.js +23 -0
  135. package/backend/dist/esm-dev/blacklist.d.ts +10 -0
  136. package/backend/dist/esm-dev/blacklist.js +49 -0
  137. package/backend/dist/esm-dev/cli.d.ts +2 -0
  138. package/backend/dist/esm-dev/cli.js +228 -0
  139. package/backend/dist/esm-dev/database.d.ts +364 -0
  140. package/backend/dist/esm-dev/database.js +1957 -0
  141. package/backend/dist/esm-dev/endpoint.d.ts +57 -0
  142. package/backend/dist/esm-dev/endpoint.js +421 -0
  143. package/backend/dist/esm-dev/file_watcher.d.ts +44 -0
  144. package/backend/dist/esm-dev/file_watcher.js +313 -0
  145. package/backend/dist/esm-dev/frontend.d.ts +13 -0
  146. package/backend/dist/esm-dev/frontend.js +27 -0
  147. package/backend/dist/esm-dev/image_endpoint.d.ts +24 -0
  148. package/backend/dist/esm-dev/image_endpoint.js +206 -0
  149. package/backend/dist/esm-dev/logger.d.ts +5 -0
  150. package/backend/dist/esm-dev/logger.js +13 -0
  151. package/backend/dist/esm-dev/meta.d.ts +50 -0
  152. package/backend/dist/esm-dev/meta.js +149 -0
  153. package/backend/dist/esm-dev/mutex.d.ts +24 -0
  154. package/backend/dist/esm-dev/mutex.js +48 -0
  155. package/backend/dist/esm-dev/payments/paddle.d.ts +161 -0
  156. package/backend/dist/esm-dev/payments/paddle.js +2261 -0
  157. package/backend/dist/esm-dev/plugins/browser.d.ts +36 -0
  158. package/backend/dist/esm-dev/plugins/browser.js +176 -0
  159. package/backend/dist/esm-dev/plugins/communication.d.ts +70 -0
  160. package/backend/dist/esm-dev/plugins/communication.js +169 -0
  161. package/backend/dist/esm-dev/plugins/css.d.ts +10 -0
  162. package/backend/dist/esm-dev/plugins/css.js +64 -0
  163. package/backend/dist/esm-dev/plugins/mail.d.ts +277 -0
  164. package/backend/dist/esm-dev/plugins/mail.js +1403 -0
  165. package/backend/dist/esm-dev/plugins/pdf.d.ts +757 -0
  166. package/backend/dist/esm-dev/plugins/pdf.js +1694 -0
  167. package/backend/dist/esm-dev/plugins/thread_monitor.d.ts +18 -0
  168. package/backend/dist/esm-dev/plugins/thread_monitor.js +120 -0
  169. package/backend/dist/esm-dev/plugins/ts/compiler.d.ts +132 -0
  170. package/backend/dist/esm-dev/plugins/ts/compiler.js +907 -0
  171. package/backend/dist/esm-dev/plugins/ts/preprocessing.d.ts +14 -0
  172. package/backend/dist/esm-dev/plugins/ts/preprocessing.js +724 -0
  173. package/backend/dist/esm-dev/rate_limit.d.ts +65 -0
  174. package/backend/dist/esm-dev/rate_limit.js +425 -0
  175. package/backend/dist/esm-dev/request.deprc.d.ts +48 -0
  176. package/backend/dist/esm-dev/request.deprc.js +572 -0
  177. package/backend/dist/esm-dev/response.deprc.d.ts +55 -0
  178. package/backend/dist/esm-dev/response.deprc.js +275 -0
  179. package/backend/dist/esm-dev/server.d.ts +311 -0
  180. package/backend/dist/esm-dev/server.js +3435 -0
  181. package/backend/dist/esm-dev/splash_screen.d.ts +35 -0
  182. package/backend/dist/esm-dev/splash_screen.js +148 -0
  183. package/backend/dist/esm-dev/status.d.ts +60 -0
  184. package/backend/dist/esm-dev/status.js +196 -0
  185. package/backend/dist/esm-dev/stream.d.ts +75 -0
  186. package/backend/dist/esm-dev/stream.js +947 -0
  187. package/backend/dist/esm-dev/users.d.ts +111 -0
  188. package/backend/dist/esm-dev/users.js +1908 -0
  189. package/backend/dist/esm-dev/utils.d.ts +27 -0
  190. package/backend/dist/esm-dev/utils.js +324 -0
  191. package/backend/dist/esm-dev/view.d.ts +52 -0
  192. package/backend/dist/esm-dev/view.js +561 -0
  193. package/backend/dist/esm-dev/vinc.d.ts +2 -0
  194. package/backend/dist/esm-dev/vinc.dev.d.ts +2 -0
  195. package/backend/dist/esm-dev/vinc.dev.js +6 -0
  196. package/backend/dist/esm-dev/vinc.js +6 -0
  197. package/backend/dist/esm-dev/volt.d.ts +15 -0
  198. package/backend/dist/esm-dev/volt.js +23 -0
  199. package/backend/src/blacklist.ts +69 -0
  200. package/backend/src/cli.js +245 -0
  201. package/backend/src/database.ts +2241 -0
  202. package/backend/src/endpoint.ts +494 -0
  203. package/backend/src/file_watcher.ts +359 -0
  204. package/backend/src/frontend.ts +35 -0
  205. package/backend/src/globals.d.ts +8 -0
  206. package/backend/src/image_endpoint.ts +258 -0
  207. package/backend/src/logger.ts +18 -0
  208. package/backend/src/meta.ts +202 -0
  209. package/backend/src/mutex.ts +51 -0
  210. package/backend/src/payments/paddle.ts +2659 -0
  211. package/backend/src/plugins/browser.ts +188 -0
  212. package/backend/src/plugins/communication.ts +204 -0
  213. package/backend/src/plugins/css.ts +84 -0
  214. package/backend/src/plugins/fonts/Menlo-Bold.ttf +0 -0
  215. package/backend/src/plugins/fonts/Menlo-Regular.ttf +0 -0
  216. package/backend/src/plugins/mail.ts +1720 -0
  217. package/backend/src/plugins/pdf.js +1932 -0
  218. package/backend/src/plugins/thread_monitor.ts +164 -0
  219. package/backend/src/plugins/ts/compiler.ts +1242 -0
  220. package/backend/src/plugins/ts/preprocessing.ts +812 -0
  221. package/backend/src/rate_limit.ts +503 -0
  222. package/backend/src/request.deprc.js +626 -0
  223. package/backend/src/response.deprc.js +354 -0
  224. package/backend/src/server.ts +4149 -0
  225. package/backend/src/splash_screen.ts +192 -0
  226. package/backend/src/status.ts +199 -0
  227. package/backend/src/stream.ts +1070 -0
  228. package/backend/src/users.ts +2077 -0
  229. package/backend/src/utils.ts +359 -0
  230. package/backend/src/view.ts +655 -0
  231. package/backend/src/vinc.dev.js +6 -0
  232. package/backend/src/vinc.ts +6 -0
  233. package/backend/src/volt.js +25 -0
  234. package/backend/tsconfig.cjs.json +29 -0
  235. package/backend/tsconfig.esm.dev.json +34 -0
  236. package/backend/tsconfig.esm.json +30 -0
  237. package/backend/tsconfig.json +2 -0
  238. package/frontend/compile.js +436 -0
  239. package/frontend/dist/elements/base.d.ts +9891 -0
  240. package/frontend/dist/elements/base.js +8818 -0
  241. package/frontend/dist/elements/module.d.ts +16 -0
  242. package/frontend/dist/elements/module.js +178 -0
  243. package/frontend/dist/modules/array.d.ts +37 -0
  244. package/frontend/dist/modules/array.js +284 -0
  245. package/frontend/dist/modules/auth.d.ts +45 -0
  246. package/frontend/dist/modules/auth.js +138 -0
  247. package/frontend/dist/modules/colors.d.ts +26 -0
  248. package/frontend/dist/modules/colors.js +340 -0
  249. package/frontend/dist/modules/compression.d.ts +6 -0
  250. package/frontend/dist/modules/compression.js +999 -0
  251. package/frontend/dist/modules/cookies.d.ts +17 -0
  252. package/frontend/dist/modules/cookies.js +166 -0
  253. package/frontend/dist/modules/date.d.ts +142 -0
  254. package/frontend/dist/modules/date.js +493 -0
  255. package/frontend/dist/modules/events.d.ts +7 -0
  256. package/frontend/dist/modules/events.js +90 -0
  257. package/frontend/dist/modules/google.d.ts +10 -0
  258. package/frontend/dist/modules/google.js +53 -0
  259. package/frontend/dist/modules/meta.d.ts +9 -0
  260. package/frontend/dist/modules/meta.js +45 -0
  261. package/frontend/dist/modules/mutex.d.ts +8 -0
  262. package/frontend/dist/modules/mutex.js +52 -0
  263. package/frontend/dist/modules/number.d.ts +12 -0
  264. package/frontend/dist/modules/number.js +8 -0
  265. package/frontend/dist/modules/object.d.ts +50 -0
  266. package/frontend/dist/modules/object.js +147 -0
  267. package/frontend/dist/modules/paddle.d.ts +1403 -0
  268. package/frontend/dist/modules/paddle.js +2641 -0
  269. package/frontend/dist/modules/scheme.d.ts +207 -0
  270. package/frontend/dist/modules/scheme.js +649 -0
  271. package/frontend/dist/modules/settings.d.ts +3 -0
  272. package/frontend/dist/modules/settings.js +4 -0
  273. package/frontend/dist/modules/statics.d.ts +4 -0
  274. package/frontend/dist/modules/statics.js +45 -0
  275. package/frontend/dist/modules/string.d.ts +163 -0
  276. package/frontend/dist/modules/string.js +291 -0
  277. package/frontend/dist/modules/support.d.ts +18 -0
  278. package/frontend/dist/modules/support.js +102 -0
  279. package/frontend/dist/modules/themes.d.ts +8 -0
  280. package/frontend/dist/modules/themes.js +17 -0
  281. package/frontend/dist/modules/user.d.ts +58 -0
  282. package/frontend/dist/modules/user.js +279 -0
  283. package/frontend/dist/modules/utils.d.ts +58 -0
  284. package/frontend/dist/modules/utils.js +1159 -0
  285. package/frontend/dist/types/gradient.d.ts +12 -0
  286. package/frontend/dist/types/gradient.js +79 -0
  287. package/frontend/dist/ui/border_button.d.ts +177 -0
  288. package/frontend/dist/ui/border_button.js +235 -0
  289. package/frontend/dist/ui/button.d.ts +42 -0
  290. package/frontend/dist/ui/button.js +114 -0
  291. package/frontend/dist/ui/canvas.d.ts +56 -0
  292. package/frontend/dist/ui/canvas.js +411 -0
  293. package/frontend/dist/ui/checkbox.d.ts +72 -0
  294. package/frontend/dist/ui/checkbox.js +277 -0
  295. package/frontend/dist/ui/code.d.ts +232 -0
  296. package/frontend/dist/ui/code.js +977 -0
  297. package/frontend/dist/ui/color.d.ts +1 -0
  298. package/frontend/dist/ui/color.js +110 -0
  299. package/frontend/dist/ui/context_menu.d.ts +30 -0
  300. package/frontend/dist/ui/context_menu.js +211 -0
  301. package/frontend/dist/ui/css.d.ts +10 -0
  302. package/frontend/dist/ui/css.js +44 -0
  303. package/frontend/dist/ui/divider.d.ts +18 -0
  304. package/frontend/dist/ui/divider.js +82 -0
  305. package/frontend/dist/ui/dropdown.d.ts +115 -0
  306. package/frontend/dist/ui/dropdown.js +446 -0
  307. package/frontend/dist/ui/for_each.d.ts +38 -0
  308. package/frontend/dist/ui/for_each.js +97 -0
  309. package/frontend/dist/ui/form.d.ts +25 -0
  310. package/frontend/dist/ui/form.js +227 -0
  311. package/frontend/dist/ui/frame_modes.d.ts +28 -0
  312. package/frontend/dist/ui/frame_modes.js +116 -0
  313. package/frontend/dist/ui/google_map.d.ts +31 -0
  314. package/frontend/dist/ui/google_map.js +111 -0
  315. package/frontend/dist/ui/gradient.d.ts +24 -0
  316. package/frontend/dist/ui/gradient.js +115 -0
  317. package/frontend/dist/ui/image.d.ts +138 -0
  318. package/frontend/dist/ui/image.js +570 -0
  319. package/frontend/dist/ui/input.d.ts +316 -0
  320. package/frontend/dist/ui/input.js +1187 -0
  321. package/frontend/dist/ui/link.d.ts +39 -0
  322. package/frontend/dist/ui/link.js +146 -0
  323. package/frontend/dist/ui/list.d.ts +33 -0
  324. package/frontend/dist/ui/list.js +161 -0
  325. package/frontend/dist/ui/loader_button.d.ts +108 -0
  326. package/frontend/dist/ui/loader_button.js +207 -0
  327. package/frontend/dist/ui/loaders.d.ts +60 -0
  328. package/frontend/dist/ui/loaders.js +150 -0
  329. package/frontend/dist/ui/popup.d.ts +84 -0
  330. package/frontend/dist/ui/popup.js +331 -0
  331. package/frontend/dist/ui/pseudo.d.ts +16 -0
  332. package/frontend/dist/ui/pseudo.js +81 -0
  333. package/frontend/dist/ui/scroller.d.ts +131 -0
  334. package/frontend/dist/ui/scroller.js +1251 -0
  335. package/frontend/dist/ui/slider.d.ts +35 -0
  336. package/frontend/dist/ui/slider.js +203 -0
  337. package/frontend/dist/ui/spacer.d.ts +20 -0
  338. package/frontend/dist/ui/spacer.js +83 -0
  339. package/frontend/dist/ui/span.d.ts +11 -0
  340. package/frontend/dist/ui/span.js +75 -0
  341. package/frontend/dist/ui/stack.d.ts +123 -0
  342. package/frontend/dist/ui/stack.js +344 -0
  343. package/frontend/dist/ui/steps.d.ts +72 -0
  344. package/frontend/dist/ui/steps.js +306 -0
  345. package/frontend/dist/ui/style.d.ts +12 -0
  346. package/frontend/dist/ui/style.js +78 -0
  347. package/frontend/dist/ui/switch.d.ts +44 -0
  348. package/frontend/dist/ui/switch.js +280 -0
  349. package/frontend/dist/ui/table.d.ts +118 -0
  350. package/frontend/dist/ui/table.js +411 -0
  351. package/frontend/dist/ui/tabs.d.ts +85 -0
  352. package/frontend/dist/ui/tabs.js +392 -0
  353. package/frontend/dist/ui/text.d.ts +19 -0
  354. package/frontend/dist/ui/text.js +88 -0
  355. package/frontend/dist/ui/theme.d.ts +25 -0
  356. package/frontend/dist/ui/theme.js +237 -0
  357. package/frontend/dist/ui/title.d.ts +36 -0
  358. package/frontend/dist/ui/title.js +127 -0
  359. package/frontend/dist/ui/ui.d.ts +38 -0
  360. package/frontend/dist/ui/ui.js +41 -0
  361. package/frontend/dist/ui/view.d.ts +25 -0
  362. package/frontend/dist/ui/view.js +93 -0
  363. package/frontend/dist/volt.d.ts +22 -0
  364. package/frontend/dist/volt.js +27 -0
  365. package/frontend/exports.json +1340 -0
  366. package/frontend/src/css/adyen.css +92 -0
  367. package/frontend/src/css/volt.css +65 -0
  368. package/frontend/src/elements/base.ts +16790 -0
  369. package/frontend/src/elements/module.ts +184 -0
  370. package/frontend/src/elements/types.d.ts +155 -0
  371. package/frontend/src/modules/array.ts +366 -0
  372. package/frontend/src/modules/auth.ts +188 -0
  373. package/frontend/src/modules/colors.ts +449 -0
  374. package/frontend/src/modules/compression.ts +67 -0
  375. package/frontend/src/modules/cookies.ts +182 -0
  376. package/frontend/src/modules/date.js +535 -0
  377. package/frontend/src/modules/date.ts +583 -0
  378. package/frontend/src/modules/events.ts +96 -0
  379. package/frontend/src/modules/google.ts +60 -0
  380. package/frontend/src/modules/meta.ts +59 -0
  381. package/frontend/src/modules/mutex.ts +59 -0
  382. package/frontend/src/modules/number.ts +20 -0
  383. package/frontend/src/modules/object.ts +212 -0
  384. package/frontend/src/modules/paddle.ts +2990 -0
  385. package/frontend/src/modules/scheme.ts +740 -0
  386. package/frontend/src/modules/settings.ts +5 -0
  387. package/frontend/src/modules/statics.ts +47 -0
  388. package/frontend/src/modules/string.ts +500 -0
  389. package/frontend/src/modules/support.ts +118 -0
  390. package/frontend/src/modules/themes.ts +24 -0
  391. package/frontend/src/modules/user.ts +321 -0
  392. package/frontend/src/modules/utils.ts +1260 -0
  393. package/frontend/src/static/admin/admin.png +0 -0
  394. package/frontend/src/static/admin/password.webp +0 -0
  395. package/frontend/src/static/icons/copy.webp +0 -0
  396. package/frontend/src/static/payments/arrow.long.webp +0 -0
  397. package/frontend/src/static/payments/arrow.long2.webp +0 -0
  398. package/frontend/src/static/payments/cancelled.webp +0 -0
  399. package/frontend/src/static/payments/check.sign.webp +0 -0
  400. package/frontend/src/static/payments/check.webp +0 -0
  401. package/frontend/src/static/payments/close.webp +0 -0
  402. package/frontend/src/static/payments/error.webp +0 -0
  403. package/frontend/src/static/payments/exclamation.webp +0 -0
  404. package/frontend/src/static/payments/minus.webp +0 -0
  405. package/frontend/src/static/payments/party.webp +0 -0
  406. package/frontend/src/static/payments/plus.webp +0 -0
  407. package/frontend/src/static/payments/shopping_cart.webp +0 -0
  408. package/frontend/src/static/payments/trash.webp +0 -0
  409. package/frontend/src/types/global.d.ts +4 -0
  410. package/frontend/src/types/gradient.ts +87 -0
  411. package/frontend/src/ui/any_element.d.ts +5 -0
  412. package/frontend/src/ui/border_button.ts +320 -0
  413. package/frontend/src/ui/button.ts +62 -0
  414. package/frontend/src/ui/canvas.ts +431 -0
  415. package/frontend/src/ui/checkbox.ts +284 -0
  416. package/frontend/src/ui/code.ts +1049 -0
  417. package/frontend/src/ui/color.ts +117 -0
  418. package/frontend/src/ui/context_menu.ts +194 -0
  419. package/frontend/src/ui/css.ts +57 -0
  420. package/frontend/src/ui/divider.ts +28 -0
  421. package/frontend/src/ui/dropdown.ts +503 -0
  422. package/frontend/src/ui/for_each.ts +71 -0
  423. package/frontend/src/ui/form.ts +208 -0
  424. package/frontend/src/ui/frame_modes.ts +140 -0
  425. package/frontend/src/ui/google_map.ts +70 -0
  426. package/frontend/src/ui/gradient.ts +73 -0
  427. package/frontend/src/ui/image.ts +587 -0
  428. package/frontend/src/ui/input.ts +1284 -0
  429. package/frontend/src/ui/link.ts +77 -0
  430. package/frontend/src/ui/list.ts +88 -0
  431. package/frontend/src/ui/loader_button.ts +192 -0
  432. package/frontend/src/ui/loaders.ts +126 -0
  433. package/frontend/src/ui/popup.ts +370 -0
  434. package/frontend/src/ui/pseudo.ts +33 -0
  435. package/frontend/src/ui/scroller.ts +1324 -0
  436. package/frontend/src/ui/slider.ts +215 -0
  437. package/frontend/src/ui/spacer.ts +29 -0
  438. package/frontend/src/ui/span.ts +23 -0
  439. package/frontend/src/ui/stack.ts +238 -0
  440. package/frontend/src/ui/steps.ts +334 -0
  441. package/frontend/src/ui/style.ts +26 -0
  442. package/frontend/src/ui/switch.ts +286 -0
  443. package/frontend/src/ui/table.ts +323 -0
  444. package/frontend/src/ui/tabs.ts +441 -0
  445. package/frontend/src/ui/text.ts +38 -0
  446. package/frontend/src/ui/theme.ts +279 -0
  447. package/frontend/src/ui/title.ts +64 -0
  448. package/frontend/src/ui/ui.ts +47 -0
  449. package/frontend/src/ui/view.ts +44 -0
  450. package/frontend/src/volt.ts +31 -0
  451. package/package.json +58 -0
@@ -0,0 +1,2241 @@
1
+ /*
2
+ * Author: Daan van den Bergh
3
+ * Copyright: © 2022 - 2024 Daan van den Bergh.
4
+ */
5
+
6
+ // ---------------------------------------------------------
7
+ // Libraries.
8
+
9
+ import { ChildProcess, spawn } from "child_process";
10
+ import { deserialize, serialize } from "bson";
11
+ import { Transform } from 'stream';
12
+ import { MongoClient, Collection as MongoCollection, ObjectId } from 'mongodb';
13
+ import { logger, LogSource } from "./logger.js";
14
+ import { Status } from "./status.js";
15
+ import { vlib } from "@vinc";
16
+
17
+ const log_source = new LogSource("Database")
18
+
19
+ // ---------------------------------------------------------
20
+ // Collection.
21
+ // Path based collection, so "myfile", "mydir/myfile".
22
+
23
+ // @warning: The "path" param must always be allowed to be an object or string, also for the UIDCollection class.
24
+ // @warning: THE DATABASE COLLECTION SHOULD ALSO ACCEPT OBJECTS FOR PATHS.
25
+ /* @docs:
26
+ @nav: Backend
27
+ @chapter: Database
28
+ @title: Collection
29
+ @desc: The database collection class.
30
+ @note: The document attribute `_path` is a reserved index attribute for the path of the document.
31
+ @attribute:
32
+ @name: col
33
+ @desc: The native mongodb collection.
34
+ */
35
+ class Collection {
36
+
37
+ // Static attributes.
38
+ static chunk_size: number = 1024 * 1024 * 4; // 4MB chunks, lower is better for frequent updates.
39
+ static constructor_scheme = {
40
+ name: "string",
41
+ uid_based: "boolean",
42
+ ttl: {type: "number", default: null},
43
+ indexes: {
44
+ type: "array",
45
+ default: [],
46
+ value_scheme: {
47
+ type: ["string", "object"],
48
+ scheme: {
49
+ key: {type: "string", required: (data) => data.key == null && data.keys == null },
50
+ keys: {
51
+ type: ["string", "array"],
52
+ required: (data) => data.key == null && data.keys == null, value_scheme: "string",
53
+ postprocess: (keys) => typeof keys === "string" ? [keys] : keys,
54
+ },
55
+ options: {type: "object", required: false},
56
+ commit_quorom: {type: "object", required: false},
57
+ forced: {type: "boolean", default: false},
58
+ },
59
+ postprocess: (info) => {
60
+ if (typeof info === "string") return {keys: [info]}
61
+ if (typeof info === "object" && info.key) {
62
+ info.keys = info.key;
63
+ delete info.key;
64
+ return info;
65
+ }
66
+ return info;
67
+ },
68
+ },
69
+ },
70
+ }
71
+
72
+
73
+ // Instance attributes
74
+ col: MongoCollection;
75
+ name: string;
76
+ uid_based: boolean;
77
+ ttl: number | null;
78
+ ttl_enabled: boolean;
79
+
80
+ constructor(
81
+ name: string,
82
+ collection: MongoCollection,
83
+ ttl: number | null = null,
84
+ indexes: Array<{
85
+ keys: string[] | string | Record<string, any>,
86
+ options?: Record<string, any>,
87
+ commit_quorum?: any,
88
+ forced?: boolean
89
+ }> = [],
90
+ uid_based: boolean = false,
91
+ ) {
92
+
93
+ // Verify scheme.
94
+ ({indexes, ttl} = vlib.Scheme.verify({
95
+ object: {
96
+ name,
97
+ indexes,
98
+ ttl,
99
+ uid_based,
100
+ },
101
+ check_unknown: true,
102
+ scheme: Collection.constructor_scheme,
103
+ }));
104
+
105
+ // Attributes.
106
+ this.name = name;
107
+ this.col = collection;
108
+ this.uid_based = uid_based;
109
+ this.ttl = ttl;
110
+ this.ttl_enabled = typeof ttl === "number";
111
+
112
+ // Create default indexes.
113
+ if (uid_based) {
114
+ this.col.createIndex({ _path: 1, _uid: 1 });
115
+ } else {
116
+ this.col.createIndex({_path: 1});
117
+ }
118
+
119
+ // Create ttl index.
120
+ if (this.ttl_enabled) {
121
+ this.col.dropIndex("_ttl_timestamp_1")
122
+ .catch(err => {
123
+ if (err.codeName !== 'IndexNotFound') {
124
+ throw err;
125
+ }
126
+ })
127
+ .then(() => {
128
+ this.col.createIndex(
129
+ {_ttl_timestamp: 1},
130
+ {expireAfterSeconds: parseInt(String(this.ttl! / 1000))}
131
+ );
132
+ })
133
+ }
134
+
135
+ // Create indexes.
136
+ if (Array.isArray(indexes) && indexes.length > 0) {
137
+ for (const index of indexes) {
138
+ this.create_index({
139
+ keys: index.keys,
140
+ options: index.options,
141
+ commit_quorum: index.commit_quorum,
142
+ forced: index.forced,
143
+ })
144
+ }
145
+ }
146
+ }
147
+
148
+ // Handle file response.
149
+ private _process_doc<T>(doc: T | null): T | null {
150
+ if (doc == null) { return null; }
151
+ else if ((doc as any)._content != null) {
152
+ return (doc as any)._content;
153
+ }
154
+ return doc;
155
+ }
156
+
157
+ // Chunked methods.
158
+ private async _load_chunked(
159
+ path: string | Record<string, any>,
160
+ find_opts?: Record<string, any>
161
+ ): Promise<any> {
162
+ let query = typeof path === "string" ?
163
+ {_path: path, chunk: {$gte: 0}} :
164
+ {...path, chunk: {$gte: 0}};
165
+ const chunks_cursor = this.col.find(query, find_opts).sort({chunk: 1});
166
+ const chunks = await chunks_cursor.toArray();
167
+ if (chunks.length === 0) {
168
+ return null;
169
+ }
170
+ const buffer = Buffer.concat(chunks.map(chunk => chunk.data.buffer));
171
+ return deserialize(buffer);
172
+ }
173
+
174
+ private async _save_chunked(
175
+ path: string | Record<string, any>,
176
+ content: any
177
+ ): Promise<void> {
178
+
179
+ // Serialize.
180
+ const buffer = serialize(content);
181
+ const new_chunk_count = Math.ceil(buffer.length / Collection.chunk_size);
182
+
183
+ // Retrieve the old chunk count
184
+ const ref_query: any = typeof path === "string" ?
185
+ {_path: path, chunk: -1} :
186
+ {...path, chunk: -1};
187
+ const object_ref = await this.col.findOne(ref_query);
188
+ const old_chunk_count = object_ref ? object_ref.chunks : 0;
189
+
190
+ // Update chunks.
191
+ const bulk_ops: any[] = [];
192
+ for (let i = 0; i < buffer.length; i += Collection.chunk_size) {
193
+ let query: Record<string, any>, update: Record<string, any>;
194
+ if (typeof path === "string") {
195
+ query = {
196
+ _path: path,
197
+ chunk: i / Collection.chunk_size,
198
+ }
199
+ update = {
200
+ chunk: i / Collection.chunk_size,
201
+ data: buffer.slice(i, i + Collection.chunk_size)
202
+ }
203
+ } else {
204
+ query = {
205
+ ...path,
206
+ chunk: i / Collection.chunk_size,
207
+ }
208
+ update = {
209
+ chunk: i / Collection.chunk_size,
210
+ data: buffer.slice(i, i + Collection.chunk_size)
211
+ }
212
+ }
213
+ const full_update: Record<string, any> = {
214
+ $set: update,
215
+ };
216
+ if (this.ttl_enabled) {
217
+ full_update["$setOnInsert"] = { _ttl_timestamp: new Date() };
218
+ }
219
+ bulk_ops.push({
220
+ updateOne: {
221
+ filter: query,
222
+ update: full_update,
223
+ upsert: true
224
+ }
225
+ });
226
+ }
227
+
228
+ // Update reference.
229
+ const full_update: Record<string, any> = {
230
+ $set: {
231
+ chunk: -1,
232
+ chunks: new_chunk_count,
233
+ },
234
+ };
235
+ if (this.ttl_enabled) {
236
+ full_update["$setOnInsert"] = { _ttl_timestamp: new Date() };
237
+ }
238
+ bulk_ops.push({
239
+ updateOne: {
240
+ filter: ref_query,
241
+ update: full_update,
242
+ upsert: true
243
+ }
244
+ });
245
+
246
+ // Write.
247
+ await this.col.bulkWrite(bulk_ops, {ordered: true});
248
+
249
+ // Delete any excess chunks if the new chunk count is less than the old chunk count
250
+ if (new_chunk_count < old_chunk_count) {
251
+ ref_query.chunk = {$gte: new_chunk_count};
252
+ await this.col.deleteMany(ref_query);
253
+ }
254
+ }
255
+
256
+ /* @docs:
257
+ @title: Create index
258
+ @description: Creates indexes on collections.
259
+ @return:
260
+ Returns the document that was found or `null` when no document is found.
261
+ @parameter:
262
+ @name: keys
263
+ @desc: The `keys` argument for the orignal mongodb `createIndex()` function.
264
+ @parameter:
265
+ @name: options
266
+ @desc: The `options` argument for the orignal mongodb `createIndex()` function.
267
+ @parameter:
268
+ @name: commitQuorum
269
+ @desc: The `commitQuorum` argument for the orignal mongodb `createIndex()` function.
270
+ */
271
+ async create_index({
272
+ keys,
273
+ options = null,
274
+ commit_quorum = null,
275
+ forced = false
276
+ }: {
277
+ keys: string[] | string | Record<string, any>,
278
+ options?: Record<string, any> | null,
279
+ commit_quorum?: any,
280
+ forced?: boolean
281
+ }): Promise<string> {
282
+ let keys_obj: Record<string, number> = {};
283
+ if (typeof keys === "string") {
284
+ keys_obj = {};
285
+ keys_obj[keys] = 1;
286
+ }
287
+ else if (Array.isArray(keys)) {
288
+ keys_obj = {};
289
+ for (const key of keys) {
290
+ keys_obj[key] = 1;
291
+ }
292
+ }
293
+ else if (keys == null || typeof keys !== "object") {
294
+ vlib.Scheme.throw_invalid_type("keys", keys, ["string", "string[]", "object"], true);
295
+ } else {
296
+ keys_obj = keys;
297
+ }
298
+
299
+ // Drop index.
300
+ if (forced) {
301
+ try {
302
+ await this.col.dropIndex(
303
+ options?.name ??
304
+ Object.entries(keys_obj)
305
+ .map(([key, value]) => `${key}_${value}`)
306
+ .join('_')
307
+ )
308
+ } catch (err: any) {
309
+ if (err.codeName !== 'IndexNotFound') {
310
+ throw err;
311
+ }
312
+ }
313
+ }
314
+
315
+ // Create index.
316
+ // @ts-ignore
317
+ return await this.col.createIndex(keys_obj, options || {}, commit_quorum)
318
+ }
319
+
320
+ /* @docs:
321
+ * @title: Find
322
+ * @description: Find a document by a query.
323
+ * @return:
324
+ * Returns the document that was found or `null` when no document is found.
325
+ * @parameter:
326
+ * @name: query
327
+ * @desc: The query options.
328
+ * @type: object
329
+ */
330
+ async find(query: Record<string, any>): Promise<any> {
331
+ try {
332
+ return this._process_doc(await this.col.findOne(query));
333
+ } catch (error) {
334
+ console.error(error);
335
+ throw new Error('Encountered an error while finding the document.');
336
+ }
337
+ }
338
+
339
+ /* @docs:
340
+ * @title: Exists
341
+ * @description: Check if a document exists.
342
+ * @parameter:
343
+ * @name: path
344
+ * @description: The database path to the document.
345
+ * @type: string
346
+ */
347
+ async exists(path: string | Record<string, any>): Promise<boolean> {
348
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
349
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
350
+ }
351
+ try {
352
+ const doc = await this.col.findOne(
353
+ typeof path === "object" ? path : {_path: path},
354
+ {projection: { _id: 1 }}
355
+ );
356
+ return doc != null;
357
+ } catch (error) {
358
+ console.error(error);
359
+ throw new Error('Encountered an error while checking if the document exists.');
360
+ }
361
+ }
362
+
363
+ /* @docs:
364
+ * @title: Load
365
+ * @description: Load data by path.
366
+ * @return:
367
+ * Returns the loaded document.
368
+ *
369
+ * Returns the `def` parameter when the data does not exist, keep in mind that when parameter `def` is an object it could be a reference to a defined variable.
370
+ * @parameter:
371
+ * @name: path
372
+ * @description: The database path to the document.
373
+ * @type: string
374
+ * @parameter:
375
+ * @name: opts
376
+ * @desc: Additional options.
377
+ * @type: null, object
378
+ * @attribute:
379
+ * @name: default
380
+ * @description:
381
+ * The default data to be returned when the data does not exist.
382
+ *
383
+ * When the type of attribute `default` is `object` then the keys that do not exist in the loaded object, but do exist in the default object will be inserted into the loaded object.
384
+ * @type: null, object
385
+ * @attribute:
386
+ * @name: chunked
387
+ * @description: Load a chunked document.
388
+ * @type: null, object
389
+ * @attribute:
390
+ * @name: attributes
391
+ * @description: The attributes to load.
392
+ * @type: null, string[]
393
+ */
394
+ async load(
395
+ path: string | Record<string, any>,
396
+ opts: {
397
+ default?: any,
398
+ chunked?: boolean,
399
+ attributes?: string[],
400
+ projection?: Record<string, any>
401
+ } | null = null
402
+ ): Promise<any> {
403
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
404
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
405
+ }
406
+ try {
407
+
408
+ // Get attributes.
409
+ let find_opts: {projection?: Record<string, any>} | undefined;
410
+ if (opts) {
411
+ if (opts.projection) {
412
+ find_opts = {projection: opts.projection};
413
+ }
414
+ else if (opts.attributes) {
415
+ find_opts = {projection: {
416
+ _id: 1,
417
+ _path: 1,
418
+ _uid: 1,
419
+ }};
420
+ opts.attributes.forEach((i) => {
421
+ if (find_opts?.projection) {
422
+ find_opts.projection[i] = 1;
423
+ }
424
+ })
425
+ }
426
+ }
427
+
428
+ // Load doc.
429
+ let doc;
430
+ if (opts != null && opts.chunked === true) {
431
+ doc = await this._load_chunked(path, find_opts);
432
+ } else {
433
+
434
+ // Load.
435
+ doc = await this.col.findOne(
436
+ typeof path === "object" ? path : {_path: path},
437
+ find_opts,
438
+ );
439
+ this.clean(doc);
440
+ }
441
+
442
+ // Process doc.
443
+ doc = this._process_doc(doc);
444
+
445
+ // Handle default.
446
+ if (doc == null) {
447
+ if (opts != null && opts.default !== undefined) { return opts.default; }
448
+ return null;
449
+ }
450
+
451
+ // Insert default keys.
452
+ else if (opts != null && typeof opts.default === "object" && opts.default != null && Array.isArray(opts.default) === false) {
453
+ const set_defaults = (obj: Record<string, any>, defaults: Record<string, any>) => {
454
+ Object.keys(defaults).forEach((key) => {
455
+ if (obj[key] === undefined) {
456
+ obj[key] = defaults[key];
457
+ } else if (
458
+ typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] != null &&
459
+ typeof defaults[key] === "object" && !Array.isArray(defaults[key]) && defaults[key] != null
460
+ ) {
461
+ set_defaults(obj[key], defaults[key])
462
+ }
463
+ })
464
+ }
465
+ set_defaults(doc, opts.default);
466
+ }
467
+
468
+ // Response.
469
+ return doc;
470
+
471
+ } catch (error) {
472
+ console.error(error);
473
+ throw new Error('Encountered an error while loading the document.');
474
+ }
475
+ }
476
+
477
+ /* @docs:
478
+ * @title: Save
479
+ * @description: Save data by path. When the document already exists this function only updates the specified content attributes.
480
+ * @return:
481
+ * Returns the updated document.
482
+ * @parameter:
483
+ * @name: path
484
+ * @description: The database path to the document.
485
+ * @type: string
486
+ * @parameter:
487
+ * @name: data
488
+ * @description: The data to save.
489
+ * @type: null, boolean, number, string, array, object
490
+ * @parameter:
491
+ * @name: opts
492
+ * @desc: Additional options.
493
+ * @type: null, object
494
+ * @attribute:
495
+ * @name: chunked
496
+ * @description: Chunk the document into multiple documents, therefore documents larger than 16MB are supported.
497
+ * @warning: Currently this option is only supported for types `object` and `array`.
498
+ * @default: false
499
+ * @type: boolean
500
+ * @attribute:
501
+ * @name: bulk
502
+ * @description: Get a bulk operation object, so several operations can be executed in bulk.
503
+ * @default: false
504
+ * @type: boolean
505
+ * @attribute:
506
+ * @name: set
507
+ * @description: By default the $set attribute is used for the content, with `opts.set` disabled you can create your own instructions. The `content` attribute must reflect this.
508
+ * @warning: This does not work in combination with `opts.chunked`.
509
+ * @default: true
510
+ * @type: boolean
511
+ */
512
+ async save(
513
+ path: string | Record<string, any>,
514
+ content: any,
515
+ opts: {
516
+ chunked?: boolean,
517
+ bulk?: boolean,
518
+ set?: boolean
519
+ } | null = null
520
+ ): Promise<any> {
521
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
522
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
523
+ }
524
+ try {
525
+ // Vars.
526
+ let doc, set;
527
+
528
+ // Create set.
529
+ if (typeof content === "object" && Array.isArray(content) == false && content != null) {
530
+ delete content._id;
531
+ delete content._path;
532
+ delete content._uid;
533
+ delete content._ttl_timestamp;
534
+ set = content;
535
+ } else {
536
+ set = {_content: content};
537
+ }
538
+
539
+ // Save chunked.
540
+ if (opts != null && opts.chunked === true) {
541
+ await this._save_chunked(path, set);
542
+ }
543
+
544
+ // Save as single doc.
545
+ else {
546
+
547
+ // Apply $set rules.
548
+ if (opts == null || (opts.set !== false)) {
549
+ set = {$set: set};
550
+ }
551
+
552
+ // Apply TTL.
553
+ if (this.ttl_enabled) {
554
+ if (set["$setOnInsert"] === undefined) {
555
+ set["$setOnInsert"] = {}
556
+ set["$setOnInsert"]._ttl_timestamp = new Date();
557
+ }
558
+ else if (set["$setOnInsert"] != null && typeof set["$setOnInsert"] === "object") {
559
+ set["$setOnInsert"]._ttl_timestamp = new Date();
560
+ } else {
561
+ throw new Error(`Undefined behaviour: Unable to assign the $setOnInsert data for ttl control.`);
562
+ }
563
+ }
564
+
565
+ // Bulk operation.
566
+ if (opts != null && opts.bulk) {
567
+ return {
568
+ updateOne: {
569
+ filter: typeof path === "object" ? path : {_path: path},
570
+ update: set,
571
+ upsert: true,
572
+ }
573
+ };
574
+ }
575
+
576
+ // Normal operation.
577
+ else {
578
+ await this.col.updateOne(
579
+ typeof path === "object" ? path : {_path: path},
580
+ set,
581
+ {upsert: true},
582
+ );
583
+ }
584
+ }
585
+
586
+ // Response.
587
+ return content;
588
+
589
+ } catch (error) {
590
+ console.error(error);
591
+ throw new Error('Encountered an error while updating the document.');
592
+ }
593
+ }
594
+
595
+ // List.
596
+ /* @docs:
597
+ * @title: List
598
+ * @description: List all child documents of directory path.
599
+ * @parameter:
600
+ * @name: path
601
+ * @description: The database directory path.
602
+ * @type: string
603
+ * @parameter:
604
+ * @name: options
605
+ * @description: List options.
606
+ * @type: object
607
+ * @attribute:
608
+ * @name: process
609
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
610
+ * @type: boolean
611
+ * @default: true
612
+ * @attribute:
613
+ * @name: projection
614
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
615
+ * @type: object
616
+ * @default: undefined
617
+ */
618
+ async list(
619
+ path: string | Record<string, any>,
620
+ options: {
621
+ process?: boolean,
622
+ projection?: Record<string, any>
623
+ } = {}
624
+ ): Promise<any[]> {
625
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
626
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
627
+ }
628
+ if (typeof path === "string") {
629
+ while (path.length > 0 && path.charAt(path.length - 1) === "/") {
630
+ path = path.substr(0, path.length - 1);
631
+ }
632
+ if (path.length == 0) {
633
+ throw Error("Invalid path.");
634
+ }
635
+ path = {_path: {$regex: `^${path}/`}};
636
+ } else if ((path as Record<string, any>)._path) {
637
+ let _path = (path as Record<string, any>)._path;
638
+ while (_path.length > 0 && _path.charAt(_path.length - 1) === "/") {
639
+ _path = _path.substr(0, _path.length - 1);
640
+ }
641
+ if (_path.length == 0) {
642
+ throw Error("Invalid path.");
643
+ }
644
+ (path as Record<string, any>)._path = {$regex: `^${_path}/`};
645
+ }
646
+ try {
647
+ const docs = await this.col.find(path, {projection: options.projection}).toArray();
648
+ if (options.process === false) {
649
+ return docs;
650
+ }
651
+ return docs.map((doc) => this._process_doc(doc));
652
+ } catch (error) {
653
+ console.error(error);
654
+ throw new Error('Encountered an error while listing all documents.');
655
+ }
656
+ }
657
+
658
+ /* @docs:
659
+ * @title: List Query
660
+ * @description: List all documents of the collection based on a query.
661
+ * @parameter:
662
+ * @name: query
663
+ * @desc: The query options.
664
+ * @type: object
665
+ * @parameter:
666
+ * @name: options
667
+ * @description: List options.
668
+ * @type: object
669
+ * @attribute:
670
+ * @name: process
671
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
672
+ * @type: boolean
673
+ * @default: true
674
+ * @attribute:
675
+ * @name: projection
676
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
677
+ * @type: object
678
+ * @default: undefined
679
+ */
680
+ async list_query(
681
+ query: Record<string, any> = {},
682
+ options: {
683
+ process?: boolean,
684
+ projection?: Record<string, any>
685
+ } = {}
686
+ ): Promise<any[]> {
687
+ try {
688
+ const docs = await this.col.find(query, {projection: options.projection}).toArray();
689
+ if (options.process === false) { return docs; }
690
+ return docs.map((doc) => this._process_doc(doc)) // list as array since the user might have used a path object width different attributes so dict is not reliable.
691
+ } catch (error) {
692
+ console.error(error);
693
+ throw new Error('Encountered an error while listing all documents.');
694
+ }
695
+ }
696
+
697
+ /* @docs:
698
+ * @title: List All
699
+ * @description: List all documents of the collection, optionally per uid.
700
+ * @parameter:
701
+ * @name: query
702
+ * @ignore: true
703
+ * @parameter:
704
+ * @name: options
705
+ * @description: List options.
706
+ * @type: object
707
+ * @attribute:
708
+ * @name: process
709
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
710
+ * @type: boolean
711
+ * @default: true
712
+ * @attribute:
713
+ * @name: projection
714
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
715
+ * @type: object
716
+ * @default: undefined
717
+ */
718
+ async list_all(
719
+ query: Record<string, any> = {},
720
+ options: {
721
+ process?: boolean,
722
+ projection?: Record<string, any>
723
+ } = {}
724
+ ): Promise<any[]> {
725
+ let docs;
726
+ if (this.uid_based) {
727
+ docs = await this.col.find(query, {projection: options.projection}).toArray();
728
+ } else {
729
+ docs = await this.col.find(query, {projection: options.projection}).toArray();
730
+ }
731
+ if (options.process === false) {
732
+ return docs;
733
+ }
734
+ return docs.map((doc) => this._process_doc(doc)) // list as array since the user might have used a path object width different attributes so dict is not reliable.
735
+ }
736
+
737
+ /* @docs:
738
+ * @title: Delete
739
+ * @description: Delete a document of the collection by path.
740
+ * @parameter:
741
+ * @name: path
742
+ * @description: The database path to the document.
743
+ * @type: string
744
+ * @parameter:
745
+ * @name: opts
746
+ * @desc: Additional options.
747
+ * @type: null, object
748
+ * @attribute:
749
+ * @name: chunked
750
+ * @description: Delete a chunked document.
751
+ * @default: false
752
+ * @type: boolean
753
+ * @attribute:
754
+ * @name: bulk
755
+ * @description: Get a bulk operation object, so several operations can be executed in bulk.
756
+ * @default: false
757
+ * @type: boolean
758
+ */
759
+ async delete(
760
+ path: string | Record<string, any>,
761
+ opts: {
762
+ chunked?: boolean,
763
+ bulk?: boolean
764
+ } | null = null
765
+ ): Promise<any> {
766
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
767
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
768
+ }
769
+ try {
770
+ if (opts != null && opts.chunked === true) {
771
+ if (opts.bulk) {
772
+ return {deleteMany: {filter: typeof path === "object" ? path : {_path: path}}};
773
+ } else {
774
+ await this.col.deleteMany(typeof path === "object" ? path : {_path: path});
775
+ }
776
+ } else {
777
+ if (opts != null && opts.bulk) {
778
+ return {deleteOne: {filter: typeof path === "object" ? path : {_path: path}}};
779
+ } else {
780
+ await this.col.deleteOne(typeof path === "object" ? path : {_path: path});
781
+ }
782
+ }
783
+ } catch (error) {
784
+ console.error(error);
785
+ throw new Error('Encountered an error while deleting.');
786
+ }
787
+ }
788
+
789
+ /* @docs:
790
+ * @title: Delete Query
791
+ * @description: Delete a document of the collection by query.
792
+ * @parameter:
793
+ * @name: query
794
+ * @description: The query object.
795
+ * @type: object
796
+ */
797
+ async delete_query(query: Record<string, any> = {}): Promise<any> {
798
+ if (typeof query !== "object" || query == null || Object.keys(query).length === 0) {
799
+ throw Error(`Parameter "query" has an invalid type "${typeof query}", the valid type is "object".`);
800
+ }
801
+ if (Object.keys(query).length === 0) {
802
+ throw Error(`Parameter "query" is an empty object.`);
803
+ }
804
+ return await this.col.deleteMany(query)
805
+ }
806
+
807
+ // Delete all.
808
+ async delete_all(path: string | Record<string, any>): Promise<void> {
809
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
810
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
811
+ }
812
+ try {
813
+ await this.col.deleteMany(typeof path === "object" ? path : {_path: path});
814
+ } catch (error) {
815
+ console.error(error);
816
+ throw new Error('Encountered an error while deleting.');
817
+ }
818
+ }
819
+
820
+ /* @docs:
821
+ * @title: Delete Collection
822
+ * @description: Delete all documents of from the collection.
823
+ */
824
+ async delete_collection(): Promise<void> {
825
+ await this.col.deleteMany()
826
+ await this.col.drop();
827
+ }
828
+
829
+ /* @docs:
830
+ * @title: Clean document
831
+ * @description: Clean a document from all default system attributes.
832
+ */
833
+ clean<T>(doc: T): T | null {
834
+ if (doc == null) { return doc; }
835
+ if (typeof doc === "object") {
836
+ delete (doc as any)._id;
837
+ delete (doc as any)._path;
838
+ if (this.uid_based) {
839
+ delete (doc as any)._uid;
840
+ }
841
+ if (this.ttl_enabled) {
842
+ delete (doc as any)._ttl_timestamp;
843
+ }
844
+ }
845
+ return doc;
846
+ }
847
+
848
+ /** Write bulk operations. */
849
+ async bulk_operations(operations: any[] = []): Promise<any> {
850
+ return await this.col.bulkWrite(operations, {ordered: true});
851
+ }
852
+
853
+ }
854
+
855
+ // ---------------------------------------------------------
856
+ // UID based collection.
857
+
858
+ // @warning: The "path" param must always be allowed to be an object or string, also for the UIDCollection class.
859
+ // @warning: THE DATABASE COLLECTION SHOULD ALSO ACCEPT OBJECTS FOR PATHS.
860
+ /* @docs:
861
+ @nav: Backend
862
+ @chapter: Database
863
+ @title: UID Collection
864
+ @desc: The UID based database collection class.
865
+ @note: The document attribute `_uid` is a reserved index attribute for the user id of the document.
866
+ @note: The document attribute `_path` is a reserved index attribute for the path of the document.
867
+ @attribute:
868
+ @name: col
869
+ @desc: The native mongodb collection.
870
+ */
871
+ class UIDCollection {
872
+ private _col: Collection;
873
+ col: MongoCollection;
874
+
875
+ constructor(
876
+ name: string,
877
+ collection: MongoCollection,
878
+ indexes: Array<{
879
+ keys: string[] | string | Record<string, any>,
880
+ options?: Record<string, any>,
881
+ commit_quorum?: any
882
+ }> = [],
883
+ ttl: number | null = null,
884
+ ) {
885
+ this._col = new Collection(name, collection, ttl, indexes, true);
886
+ this.col = this._col.col;
887
+ }
888
+
889
+ /* @docs:
890
+ @title: Create index
891
+ @description: Creates indexes on collections.
892
+ @return:
893
+ Returns the document that was found or `null` when no document is found.
894
+ @parameter:
895
+ @name: keys
896
+ @desc: The `keys` argument for the orignal mongodb `createIndex()` function.
897
+ @parameter:
898
+ @name: options
899
+ @desc: The `options` argument for the orignal mongodb `createIndex()` function.
900
+ @parameter:
901
+ @name: commitQuorum
902
+ @desc: The `commitQuorum` argument for the orignal mongodb `createIndex()` function.
903
+ */
904
+ async create_index({
905
+ keys,
906
+ options = null,
907
+ commit_quorum = null
908
+ }: {
909
+ keys: string[] | string | Record<string, any>,
910
+ options?: Record<string, any> | null,
911
+ commit_quorum?: any
912
+ }): Promise<string> {
913
+ return this._col.create_index({keys, options, commit_quorum});
914
+ }
915
+
916
+ /* @docs:
917
+ * @title: Find
918
+ * @description: Find a document by a query.
919
+ * @return:
920
+ * Returns the document that was found or `null` when no document is found.
921
+ * @parameter:
922
+ * @name: uid
923
+ * @cached: Users:uid:param
924
+ * @required: false
925
+ * @parameter:
926
+ * @name: query
927
+ * @desc: The query options.
928
+ * @type: object
929
+ */
930
+ async find(uid: string | null = null, query: Record<string, any> = {}): Promise<any> {
931
+ if (uid != null) {
932
+ query._uid = uid;
933
+ }
934
+ return await this._col.find(query);
935
+ }
936
+
937
+ /* @docs:
938
+ * @title: Exists
939
+ * @description: Check if a document exists.
940
+ * @parameter:
941
+ * @name: uid
942
+ * @cached: Users:uid:param
943
+ * @parameter:
944
+ * @name: path
945
+ * @description: The database path to the document.
946
+ * @type: string, object
947
+ */
948
+ async exists(uid: string, path: string | Record<string, any>): Promise<boolean> {
949
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
950
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
951
+ }
952
+ if (typeof uid !== "string") {
953
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
954
+ }
955
+ if (typeof path === "object") {
956
+ return await this._col.exists({...path, _uid: uid});
957
+ } else {
958
+ return await this._col.exists({_path: path, _uid: uid});
959
+ }
960
+ }
961
+
962
+ /* @docs:
963
+ * @title: Load
964
+ * @description: Load data by user id and path.
965
+ * @return:
966
+ * Returns the loaded document.
967
+ *
968
+ * Returns the `def` parameter when the data does not exist, keep in mind that when parameter `def` is an object it could be a reference to a defined variable.
969
+ * @parameter:
970
+ * @name: uid
971
+ * @cached: Users:uid:param
972
+ * @parameter:
973
+ * @name: path
974
+ * @description: The database path to the document.
975
+ * @type: string, object
976
+ * @parameter:
977
+ * @name: opts
978
+ * @desc: Additional options.
979
+ * @type: null, object
980
+ * @attribute:
981
+ * @name: default
982
+ * @description:
983
+ * The default data to be returned when the data does not exist.
984
+ *
985
+ * When the type of attribute `default` is `object` then the keys that do not exist in the loaded object, but do exist in the default object will be inserted into the loaded object.
986
+ * @type: null, object
987
+ */
988
+ async load(
989
+ uid: string,
990
+ path: string | Record<string, any>,
991
+ opts: {
992
+ default?: any,
993
+ chunked?: boolean,
994
+ attributes?: string[],
995
+ projection?: Record<string, any>
996
+ } | null = null
997
+ ): Promise<any> {
998
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
999
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
1000
+ }
1001
+ if (typeof uid !== "string") {
1002
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
1003
+ }
1004
+ if (typeof path === "object") {
1005
+ return await this._col.load({...path, _uid: uid}, opts);
1006
+ } else {
1007
+ return await this._col.load({_path: path, _uid: uid}, opts);
1008
+ }
1009
+ }
1010
+
1011
+ /* @docs:
1012
+ * @title: Save
1013
+ * @description: Save data by user id and path. When the document already exists this function only updates the specified content attributes.
1014
+ * @return:
1015
+ * Returns the updated document.
1016
+ * @parameter:
1017
+ * @name: uid
1018
+ * @cached: Users:uid:param
1019
+ * @parameter:
1020
+ * @name: path
1021
+ * @description: The database path to the document.
1022
+ * @type: string, object
1023
+ * @parameter:
1024
+ * @name: data
1025
+ * @description: The data to save.
1026
+ * @type: null, boolean, number, string, array, object
1027
+ * @parameter:
1028
+ * @name: opts
1029
+ * @desc: Additional options.
1030
+ * @type: null, object
1031
+ * @attribute:
1032
+ * @name: chunked
1033
+ * @description: Chunk the document into multiple documents, therefore documents larger than 16MB are supported.
1034
+ * @warning: Currently this option is only supported for types `object` and `array`.
1035
+ * @default: false
1036
+ * @type: boolean
1037
+ * @attribute:
1038
+ * @name: bulk
1039
+ * @description: Get a bulk operation object, so several operations can be executed in bulk.
1040
+ * @default: false
1041
+ * @type: boolean
1042
+ * @attribute:
1043
+ * @name: set
1044
+ * @description: By default the $set attribute is used for the content, with `opts.set` disabled you can create your own instructions. The `content` attribute must reflect this.
1045
+ * @warning: This does not work in combination with `opts.chunked`.
1046
+ * @default: true
1047
+ * @type: boolean
1048
+ */
1049
+ async save(
1050
+ uid: string,
1051
+ path: string | Record<string, any>,
1052
+ content: any,
1053
+ opts: {
1054
+ chunked?: boolean,
1055
+ bulk?: boolean,
1056
+ set?: boolean
1057
+ } | null = null
1058
+ ): Promise<any> {
1059
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
1060
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
1061
+ }
1062
+ if (typeof uid !== "string") {
1063
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
1064
+ }
1065
+ if (typeof path === "object") {
1066
+ return await this._col.save({...path, _uid: uid}, content, opts);
1067
+ } else {
1068
+ return await this._col.save({_path: path, _uid: uid}, content, opts);
1069
+ }
1070
+ }
1071
+
1072
+ /* @docs:
1073
+ * @title: List
1074
+ * @description: List all child documents of directory path.
1075
+ * @parameter:
1076
+ * @name: uid
1077
+ * @cached: Users:uid:param
1078
+ * @parameter:
1079
+ * @name: path
1080
+ * @description: The database directory path.
1081
+ * @type: string, object
1082
+ * @parameter:
1083
+ * @name: options
1084
+ * @description: List options.
1085
+ * @type: object
1086
+ * @attribute:
1087
+ * @name: process
1088
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
1089
+ * @type: boolean
1090
+ * @default: true
1091
+ * @attribute:
1092
+ * @name: projection
1093
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
1094
+ * @type: object
1095
+ * @default: undefined
1096
+ */
1097
+ async list(
1098
+ uid: string,
1099
+ path: string | Record<string, any>,
1100
+ options: {
1101
+ process?: boolean,
1102
+ projection?: Record<string, any>
1103
+ } = {}
1104
+ ): Promise<any[]> {
1105
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
1106
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
1107
+ }
1108
+ if (typeof uid !== "string") {
1109
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
1110
+ }
1111
+ if (typeof path === "object") {
1112
+ return await this._col.list({...path, _uid: uid}, options)
1113
+ } else {
1114
+ return await this._col.list({_path: path, _uid: uid}, options)
1115
+ }
1116
+ }
1117
+
1118
+ /* @docs:
1119
+ * @title: List Query
1120
+ * @description: List all documents of the collection based on a query.
1121
+ * @parameter:
1122
+ * @name: query
1123
+ * @desc: The query options.
1124
+ * @type: object
1125
+ * @parameter:
1126
+ * @name: options
1127
+ * @description: List options.
1128
+ * @type: object
1129
+ * @attribute:
1130
+ * @name: process
1131
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
1132
+ * @type: boolean
1133
+ * @default: true
1134
+ * @attribute:
1135
+ * @name: projection
1136
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
1137
+ * @type: object
1138
+ * @default: undefined
1139
+ */
1140
+ async list_query(
1141
+ query: Record<string, any> = {},
1142
+ options: {
1143
+ process?: boolean,
1144
+ projection?: Record<string, any>
1145
+ } = {}
1146
+ ): Promise<any[]> {
1147
+ return await this._col.list_query(query, options);
1148
+ }
1149
+
1150
+ /* @docs:
1151
+ * @title: List All
1152
+ * @description: List all documents of the collection, optionally per uid.
1153
+ * @parameter:
1154
+ * @name: uid
1155
+ * @cached: Users:uid:param
1156
+ * @parameter:
1157
+ * @name: options
1158
+ * @description: List options.
1159
+ * @type: object
1160
+ * @attribute:
1161
+ * @name: process
1162
+ * @description: Process the document. By default saved non object data will be stored under `_content`. Processing checks this attribute and uses that content instead when it is detected.
1163
+ * @type: boolean
1164
+ * @default: true
1165
+ * @attribute:
1166
+ * @name: projection
1167
+ * @description: The data attributes to retrieve, when left undefined all attributes are retrieved.
1168
+ * @type: object
1169
+ * @default: undefined
1170
+ */
1171
+ async list_all(
1172
+ uid: string | null = null,
1173
+ options: {
1174
+ process?: boolean,
1175
+ projection?: Record<string, any>
1176
+ } = {}
1177
+ ): Promise<any[]> {
1178
+ if (uid == null) {
1179
+ return await this._col.list_all({}, options)
1180
+ } else {
1181
+ return await this._col.list_all({_uid: uid}, options);
1182
+ }
1183
+ }
1184
+
1185
+ /* @docs:
1186
+ * @title: Delete
1187
+ * @description: Delete a document of the collection by uid and path.
1188
+ * @parameter:
1189
+ * @name: uid
1190
+ * @cached: Users:uid:param
1191
+ * @parameter:
1192
+ * @name: path
1193
+ * @description: The database path to the document.
1194
+ * @type: string, object
1195
+ * @parameter:
1196
+ * @name: opts
1197
+ * @desc: Additional options.
1198
+ * @type: null, object
1199
+ * @attribute:
1200
+ * @name: chunked
1201
+ * @description: Delete a chunked document.
1202
+ * @default: false
1203
+ * @type: boolean
1204
+ * @attribute:
1205
+ * @name: bulk
1206
+ * @description: Get a bulk operation object, so several operations can be executed in bulk.
1207
+ * @default: false
1208
+ * @type: boolean
1209
+ */
1210
+ async delete(
1211
+ uid: string,
1212
+ path: string | Record<string, any>,
1213
+ opts: {
1214
+ chunked?: boolean,
1215
+ bulk?: boolean
1216
+ } | null = null
1217
+ ): Promise<any> {
1218
+ if (typeof path !== "string" && (typeof path !== "object" || path == null)) {
1219
+ throw Error(`Parameter "path" has an invalid type "${typeof path}", the valid type is "string".`);
1220
+ }
1221
+ if (typeof uid !== "string") {
1222
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
1223
+ }
1224
+ if (typeof path === "object") {
1225
+ return await this._col.delete({...path, _uid: uid}, opts)
1226
+ } else {
1227
+ return await this._col.delete({_path: path, _uid: uid}, opts)
1228
+ }
1229
+ }
1230
+
1231
+ /* @docs:
1232
+ * @title: Delete Query
1233
+ * @description: Delete a document of the collection by query.
1234
+ * @parameter:
1235
+ * @name: query
1236
+ * @description: The query object.
1237
+ * @type: object
1238
+ */
1239
+ async delete_query(query: Record<string, any>): Promise<any> {
1240
+ if (typeof query !== "object" || query == null || Object.keys(query).length === 0) {
1241
+ throw Error(`Parameter "query" has an invalid type "${typeof query}", the valid type is "object".`);
1242
+ }
1243
+ if (Object.keys(query).length === 0) {
1244
+ throw Error(`Parameter "query" is an empty object.`);
1245
+ }
1246
+ return await this._col.delete_query(query)
1247
+ }
1248
+
1249
+ // Delete all.
1250
+ async delete_all(
1251
+ uid: string,
1252
+ path: string | Record<string, any> | null = null
1253
+ ): Promise<void> {
1254
+ if (typeof uid !== "string") {
1255
+ throw Error(`Parameter "uid" has an invalid type "${typeof uid}", the valid type is "string".`);
1256
+ }
1257
+ if (path == null) {
1258
+ return await this._col.delete_all({_uid: uid})
1259
+ }
1260
+ else if (typeof path === "object") {
1261
+ return await this._col.delete_all({...path, _uid: uid})
1262
+ } else {
1263
+ return await this._col.delete_all({_path: path, _uid: uid})
1264
+ }
1265
+ }
1266
+
1267
+ /* @docs:
1268
+ * @title: Delete Collection
1269
+ * @description: Delete all documents of from the collection.
1270
+ */
1271
+ async delete_collection(): Promise<void> {
1272
+ await this._col.delete_collection()
1273
+ }
1274
+
1275
+ /* @docs:
1276
+ * @title: Clean document
1277
+ * @description: Clean a document from all default system attributes.
1278
+ */
1279
+ clean<T>(doc: T): T | null {
1280
+ return this._col.clean(doc);
1281
+ }
1282
+
1283
+ /** Write bulk operations. */
1284
+ async bulk_operations(operations: any[] = []): Promise<any> {
1285
+ return await this.col.bulkWrite(operations, {ordered: true});
1286
+ }
1287
+ }
1288
+
1289
+ // ---------------------------------------------------------
1290
+ // Database.
1291
+
1292
+ /* @docs:
1293
+ @nav: Backend
1294
+ @chapter: Database
1295
+ @title: Database
1296
+ @desc:
1297
+ The MongoDB database class, accessable under `Server.db`.
1298
+
1299
+ The database class can be utilized in two ways.
1300
+
1301
+ 1. You only provide the `uri` parameter to access an already running mongodb database.
1302
+
1303
+ 2. You provide parameters `config` and `start_args` to start and optionally create the database.
1304
+
1305
+ @warning:
1306
+ Do not forget to enable TLS when using the `config` parameter.
1307
+ @param:
1308
+ @name: uri
1309
+ @desc: The mongodb server uri.
1310
+ @type: string
1311
+ @param:
1312
+ @name: source
1313
+ @desc: The source path of the database directory, by default path `$server_source/.db` will be used.
1314
+ @type: null, string
1315
+ @param:
1316
+ @name: config
1317
+ @desc: The json data for the mongodb config file.
1318
+ @type: null, string
1319
+ @param:
1320
+ @name: start_args
1321
+ @desc: The mongod database start command arguments.
1322
+ @type: null, array[string]
1323
+ @param:
1324
+ @name: client
1325
+ @desc: The MongoClient options.
1326
+ @type: null, object
1327
+ */
1328
+ class Database {
1329
+ static constructor_scheme = {
1330
+ uri: {type: "string", default: null},
1331
+ source: {type: "string", default: null},
1332
+ config: {type: "object", default: {}},
1333
+ start_args: {type: "array", default: []},
1334
+ client: {type: "object", default: {}},
1335
+ collections: {type: "array", default: [], value_scheme: {
1336
+ type: ["string", "object"],
1337
+ preprocess: (info: string | Record<string, any>) => typeof info === "string" ? {name: info} : info,
1338
+ scheme: {
1339
+ name: Collection.constructor_scheme.name,
1340
+ ttl: Collection.constructor_scheme.ttl,
1341
+ indexes: Collection.constructor_scheme.indexes,
1342
+ },
1343
+ }},
1344
+ uid_collections: {type: "array", default: [], value_scheme: {
1345
+ type: ["string", "object"],
1346
+ preprocess: (info: string | Record<string, any>) => typeof info === "string" ? {name: info} : info,
1347
+ scheme: {
1348
+ name: Collection.constructor_scheme.name,
1349
+ ttl: Collection.constructor_scheme.ttl,
1350
+ indexes: Collection.constructor_scheme.indexes,
1351
+ },
1352
+ }},
1353
+ preview: {type: "boolean", default: true},
1354
+ preview_ip_whitelist: {type: "array", default: []},
1355
+ daemon: {type: ["object", "boolean"], default: {}},
1356
+ _server: "object",
1357
+ }
1358
+
1359
+ uri: string | null;
1360
+ preview: boolean;
1361
+ preview_ip_whitelist: string[];
1362
+ client_opts: Record<string, any> | null;
1363
+ config: Record<string, any>;
1364
+ source: any; // Using vlib.Path type
1365
+ start_args: string[];
1366
+ _collections: Array<{
1367
+ name: string,
1368
+ ttl?: number | null,
1369
+ indexes?: Array<{
1370
+ keys: string[] | string | Record<string, any>,
1371
+ options?: Record<string, any>,
1372
+ commit_quorum?: any
1373
+ }>,
1374
+ }>;
1375
+ _uid_collections: Array<{
1376
+ name: string,
1377
+ ttl?: number | null,
1378
+ indexes?: Array<{
1379
+ keys: string[] | string | Record<string, any>,
1380
+ options?: Record<string, any>,
1381
+ commit_quorum?: any
1382
+ }>,
1383
+ }>;
1384
+ server: any;
1385
+ client: MongoClient | null;
1386
+ collections: Record<string, Collection | UIDCollection>;
1387
+ proc?: ChildProcess;
1388
+ daemon?: any;
1389
+ db?: any;
1390
+
1391
+ constructor({
1392
+ uri = null,
1393
+ source = null,
1394
+ config = null,
1395
+ start_args = [],
1396
+ client = null,
1397
+ collections = [],
1398
+ uid_collections = [],
1399
+ preview = true,
1400
+ preview_ip_whitelist = [],
1401
+ daemon = {},
1402
+ _server,
1403
+ }: {
1404
+ uri?: string | null,
1405
+ source?: string | null,
1406
+ config?: Record<string, any> | null,
1407
+ start_args?: string[],
1408
+ client?: Record<string, any> | null,
1409
+ collections?: Array<{
1410
+ name: string,
1411
+ ttl?: number | null,
1412
+ indexes?: Array<{
1413
+ keys: string[] | string | Record<string, any>,
1414
+ options?: Record<string, any>,
1415
+ commit_quorum?: any
1416
+ }>,
1417
+ }>,
1418
+ uid_collections?: Array<{
1419
+ name: string,
1420
+ ttl?: number | null,
1421
+ indexes?: Array<{
1422
+ keys: string[] | string | Record<string, any>,
1423
+ options?: Record<string, any>,
1424
+ commit_quorum?: any
1425
+ }>,
1426
+ }>,
1427
+ preview?: boolean,
1428
+ preview_ip_whitelist?: string[],
1429
+ daemon?: Record<string, any> | boolean,
1430
+ _server: any,
1431
+ }) {
1432
+ // Checks.
1433
+ if (_server.is_primary && uri == null) {
1434
+ ({uri, config, start_args, config, client} = vlib.Scheme.verify({
1435
+ object: arguments[0],
1436
+ check_unknown: true,
1437
+ scheme: Database.constructor_scheme
1438
+ }));
1439
+ }
1440
+ // Arguments.
1441
+ this.uri = uri;
1442
+ this.preview = preview;
1443
+ this.preview_ip_whitelist = preview_ip_whitelist;
1444
+ this.client_opts = client;
1445
+ this.config = config || {};
1446
+ this.source = source != null ? new vlib.Path(source) : _server.source.join(".db");
1447
+ this.start_args = start_args;
1448
+ this._collections = collections;
1449
+ this._uid_collections = uid_collections;
1450
+ this.server = _server;
1451
+
1452
+ // Attributes.
1453
+ this.client = null;
1454
+ this.collections = {};
1455
+
1456
+ // Initialize the service daemon.
1457
+ if (this.server.daemon && daemon !== false) {
1458
+ const log_source = this.server.source.join(".logs");
1459
+ if (!log_source.exists()) {
1460
+ log_source.mkdir_sync();
1461
+ }
1462
+ this.daemon = new vlib.Daemon({
1463
+ name: this.server.daemon.name + ".mongodb",
1464
+ user: (daemon as Record<string, any>).user || this.server.daemon.user,
1465
+ group: (daemon as Record<string, any>).group || this.server.daemon.group,
1466
+ command: "mongod",
1467
+ cwd: this.server.daemon.cwd,
1468
+ args: ["--config", this.source.join("mongod.json").str(), ...this.start_args],
1469
+ env: (daemon as Record<string, any>).env || this.server.daemon.env,
1470
+ description: (daemon as Record<string, any>).description || `Service daemon for the mongo database of website ${this.server.domain}.`,
1471
+ auto_restart: true,
1472
+ logs: (daemon as Record<string, any>).logs || log_source.join("logs.mongodb").str(),
1473
+ errors: (daemon as Record<string, any>).errors || log_source.join("errors.mongodb").str(),
1474
+ })
1475
+ }
1476
+ }
1477
+
1478
+ // Database preview.
1479
+ _initialize_db_preview(): void {
1480
+ /*
1481
+ if (this.preview && this.server.production === false) {
1482
+ this.server.endpoint(
1483
+
1484
+ // Database preview.
1485
+ {
1486
+ method: "GET" ,
1487
+ endpoint: "/volt/db/preview",
1488
+ view: {
1489
+ callback: () => {
1490
+ volt.utils.on_load(async () => {
1491
+
1492
+ // Style theme.
1493
+ const style = {
1494
+ // bg: "#151721",
1495
+ // sub_bg: "#191B28",
1496
+ // tag_bg: "#1C203A",
1497
+ // div_bg: "#282B40",
1498
+ // fg: "#FFFFFF",
1499
+ // sub_fg: "#FFFFFF99",
1500
+ // tag_fg: "#FFFFFF",
1501
+
1502
+ bg: "#F6F8F8",
1503
+ sub_bg: "#FFFFFF",
1504
+ tag_bg: "#F6F8F8",
1505
+ div_bg: "#00000010",
1506
+ fg: "#32334F",
1507
+ sub_fg: "#31344599",
1508
+ tag_fg: "#313445",
1509
+ };
1510
+
1511
+ // List all collections.
1512
+ const collections = (await volt.utils.request({url: "/volt/db/collections"})).collections;
1513
+
1514
+ // Render a list.
1515
+ const prev_lists = [];
1516
+ function RenderList ({
1517
+ title,
1518
+ list,
1519
+ doc = null,
1520
+ add_prev = true,
1521
+ }) {
1522
+ if (add_prev) {
1523
+ prev_lists.append({title, list, doc});
1524
+ }
1525
+
1526
+ // Object view.
1527
+ const obj_view = VStack();
1528
+ const refresh_obj_view = () => {
1529
+ obj_view.inner_html("");
1530
+ let index = 0;
1531
+ obj_view.append(
1532
+ ForEach(list, (key, value) => {
1533
+ ++index;
1534
+ let current_key = key;
1535
+ let value_type = Array.isArray(value) ? "array" : value == null ? "null" : typeof value;
1536
+ if (Array.isArray(value)) {
1537
+ value = JSON.stringify(value, null, 4)
1538
+ }
1539
+
1540
+ // Key input.
1541
+ const key_input = Input("key")
1542
+ .value(key)
1543
+ .font_family("'Menlo', 'Consolas', monospace")
1544
+ .color(style.sub_fg)
1545
+ .font_size(14)
1546
+ .padding(0)
1547
+ .readonly(key === "_path" || key === "_uid" || key === "uid")
1548
+ .on_mouse_over(e => e.color(style.fg))
1549
+ .on_mouse_out(e => e.color(style.sub_fg))
1550
+ .on_render((e) => e.width(e.text_width(key)))
1551
+ .on_input((e) => {
1552
+ if (key != current_key) {
1553
+ list[e.value()] = list[current_key];
1554
+ delete list[current_key];
1555
+ current_key = e.value();
1556
+ }
1557
+ e.width(e.text_width(current_key));
1558
+ });
1559
+
1560
+ // Value input.
1561
+ const value_input = Input("value")
1562
+ .value(value == null ? "null" : value)
1563
+ .font_family("'Menlo', 'Consolas', monospace")
1564
+ .color(style.sub_fg)
1565
+ .display("inline-block")
1566
+ .width("fit-content")
1567
+ .font_size(14)
1568
+ .readonly(key === "_path" || key === "_uid")
1569
+ .stretch(true)
1570
+ .on_mouse_over(e => e.color(style.fg))
1571
+ .on_mouse_out(e => e.color(style.sub_fg))
1572
+ .on_input(() => {
1573
+ clearTimeout(value_input.timeout)
1574
+ value_input.timeout = setTimeout(update_value, 500);
1575
+ })
1576
+
1577
+ // Type select.
1578
+ const type_select = ExtendedSelect({items: ["null", "boolean", "number", "string", "array", "object"]})
1579
+ .center()
1580
+ .margin(0)
1581
+ .max_width(73)
1582
+ .color(style.sub_fg)
1583
+ .font_size(14)
1584
+ .border_radius(10)
1585
+ .background(style.tag_bg)
1586
+ .border_color(style.div_bg)
1587
+ .value(value_type)
1588
+ .container
1589
+ .padding(2.5, 5)
1590
+ .parent();
1591
+
1592
+ // Update the list after edits.
1593
+ const update_value = () => {
1594
+ const type = type_select.value();
1595
+ const value = value_input.value();
1596
+ if (type === "null") {
1597
+ list[current_key] = null;
1598
+ value_input.value(list[current_key].toString());
1599
+ }
1600
+ else if (type === "boolean") {
1601
+ list[current_key] = value == "true" || value == "True" || value == "TRUE" || value == "1";
1602
+ value_input.value(list[current_key].toString());
1603
+ }
1604
+ else if (type === "number") {
1605
+ if (value.indexOf(".") === -1) {
1606
+ list[current_key] = paseInt(value);
1607
+ } else {
1608
+ list[current_key] = paseFloat(value);
1609
+ }
1610
+ if (isNaN(list[key_input.key])) {
1611
+ list[current_key] = 0;
1612
+ }
1613
+ value_input.value(list[current_key].toString());
1614
+ }
1615
+ else if (type === "string") {
1616
+ list[current_key] = value;
1617
+ }
1618
+ else if (type === "object") {
1619
+ list[current_key] = JSON.parse(value);
1620
+ }
1621
+ }
1622
+
1623
+ // Row.
1624
+ const row = HStack(
1625
+ key_input,
1626
+ Text(" : ")
1627
+ .white_space("pre")
1628
+ .font_family("'Menlo', 'Consolas', monospace")
1629
+ .color(style.sub_fg)
1630
+ .font_size(14),
1631
+ value_input,
1632
+ type_select,
1633
+
1634
+ index < Object.keys(list).length ? null : VStack("add")
1635
+ .background(style.tag_bg)
1636
+ .padding(5, 12.5)
1637
+ .border_radius(10)
1638
+ .font_size(13)
1639
+ .color("#3B8553")
1640
+ .margin_left(10)
1641
+ .border(1, style.div_bg)
1642
+ .on_click(() => {
1643
+ list["_new"] = "";
1644
+ refresh_obj_view();
1645
+ }),
1646
+
1647
+ VStack("delete")
1648
+ .background(style.tag_bg)
1649
+ .color("#B2321E")
1650
+ .padding(5, 12.5)
1651
+ .border_radius(10)
1652
+ .font_size(13)
1653
+ .margin_left(10)
1654
+ .border(1, style.div_bg)
1655
+ .on_click(async () => {
1656
+ }),
1657
+ )
1658
+ .center_vertical()
1659
+ .padding(7.5, 0)
1660
+ if (volt.utils.is_obj(value)) {
1661
+ row.on_click(() => RenderList({title: `${title}.${key}`, list: value}));
1662
+ }
1663
+ return [
1664
+ row,
1665
+ Divider().background(style.div_bg),
1666
+ ]
1667
+ })
1668
+ );
1669
+ }
1670
+
1671
+ // Add.
1672
+ preview.inner_html("");
1673
+ preview.append(
1674
+ Scroller(
1675
+ VStack(
1676
+ HStack(
1677
+ Title("Database")
1678
+ .font_family("'Menlo', 'Consolas', monospace")
1679
+ .font_size(12)
1680
+ .color(style.tag_fg)
1681
+ .background(style.tag_bg)
1682
+ .padding(5, 12.5)
1683
+ .border_radius(10)
1684
+ .margin(0, 0, 0, 0)
1685
+ .border(1, style.div_bg)
1686
+ .width("fit-content"),
1687
+
1688
+ Spacer(),
1689
+
1690
+ doc == null ? null : Button("Update")
1691
+ .background(style.tag_bg)
1692
+ .color("#3B8553")
1693
+ .padding(5, 12.5)
1694
+ .margin_right(10)
1695
+ .border_radius(10)
1696
+ .border(1, style.div_bg)
1697
+ .on_click(() => {
1698
+ // --prev_lists.length;
1699
+ // const last = prev_lists[prev_lists.length - 1];
1700
+ // RenderList({...last, add_prev: false})
1701
+ }),
1702
+
1703
+ doc == null ? null : Button("Delete")
1704
+ .background(style.tag_bg)
1705
+ .color("#B2321E")
1706
+ .padding(5, 12.5)
1707
+ .margin_right(10)
1708
+ .border_radius(10)
1709
+ .border(1, style.div_bg)
1710
+ .on_click(async () => {
1711
+ volt.utils.request({
1712
+ method: "DELETE",
1713
+ url: "/volt/db/document",
1714
+ data: doc,
1715
+ })
1716
+ --prev_lists.length;
1717
+ const last = prev_lists[prev_lists.length - 1];
1718
+
1719
+ const __name = doc.uid != null ? `${doc.uid}:${doc.id}` : doc.id
1720
+ const filtered_list = [];
1721
+ last.list.iterate((item) => {
1722
+ if (item.__name !== __name) {
1723
+ filtered_list.append(item);
1724
+ }
1725
+ })
1726
+ last.list = filtered_list;
1727
+ RenderList({...last, add_prev: false})
1728
+ }),
1729
+
1730
+ prev_lists.length == 1 ? null : Button("Prev")
1731
+ .background(style.tag_bg)
1732
+ .color(style.tag_fg)
1733
+ .padding(5, 12.5)
1734
+ .border_radius(10)
1735
+ .border(1, style.div_bg)
1736
+ .on_click(() => {
1737
+ --prev_lists.length;
1738
+ const last = prev_lists[prev_lists.length - 1];
1739
+ RenderList({...last, add_prev: false})
1740
+ }),
1741
+ ),
1742
+
1743
+ Title(title)
1744
+ .font_family("'Menlo', 'Consolas', monospace")
1745
+ .font_size(18)
1746
+ .color(style.fg)
1747
+ .margin(15, 0),
1748
+
1749
+ Divider().background(style.div_bg),
1750
+
1751
+ Array.isArray(list)
1752
+ ? ForEach(list, (item) => {
1753
+ return [
1754
+ VStack(
1755
+ Text(item.__name)
1756
+ .font_family("'Menlo', 'Consolas', monospace")
1757
+ .color(style.sub_fg)
1758
+ .font_size(14)
1759
+ .on_mouse_over(e => e.color(style.fg))
1760
+ .on_mouse_out(e => e.color(style.sub_fg))
1761
+ )
1762
+ .padding(7.5, 0)
1763
+ .on_click(item.__click),
1764
+
1765
+ Divider().background(style.div_bg),
1766
+ ]
1767
+ })
1768
+ : () => {refresh_obj_view(); return obj_view}
1769
+ )
1770
+ .margin(25, 50)
1771
+ .padding(25, 25)
1772
+ .background(style.sub_bg)
1773
+ .border_radius(10)
1774
+ .box_shadow("0px 0px 5px #00000090")
1775
+ )
1776
+ .font_family("Helvetica, sans-serif")
1777
+ .background(style.bg)
1778
+ .frame("100%", "100%")
1779
+ )
1780
+ }
1781
+
1782
+ // Render the collections.
1783
+ const RenderCollections = () => {
1784
+ RenderList({title: "/", list: collections.iterate_append((item) => {
1785
+ return {
1786
+ __name: `${item}/`,
1787
+ __click: () => RenderCollection(item),
1788
+ }
1789
+ })})
1790
+ }
1791
+
1792
+ // Render a collection.
1793
+ const RenderCollection = async (collection) => {
1794
+ const documents = (await volt.utils.request({url: "/volt/db/documents", data: {collection}})).documents;
1795
+ RenderList({title: `${collection}/`, list: documents.iterate_append((item) => {
1796
+ return {
1797
+ __name: item._uid != null ? `${item._uid}:${item._path}` : item._path,
1798
+ __click: () => RenderDocument(collection, item._path, item._uid),
1799
+ }
1800
+ })})
1801
+ }
1802
+
1803
+ // Render a document.
1804
+ const RenderDocument = async (collection, path, uid = null) => {
1805
+ let doc = (await volt.utils.request({url: "/volt/db/document", data: {collection, path, uid}})).document
1806
+ if (Array.isArray(doc)) {
1807
+ doc = {_content: doc};
1808
+ }
1809
+ RenderList({
1810
+ title: uid != null ? `${collection}/${uid}:${path}` : `${collection}/${path}`,
1811
+ list: doc,
1812
+ doc: {collection, uid, path},
1813
+ })
1814
+ }
1815
+
1816
+ // Stack.
1817
+ const preview = VStack()
1818
+ .position(0, 0, 0, 0);
1819
+
1820
+ // Render all collections.
1821
+ RenderCollections();
1822
+
1823
+ // Response.
1824
+ return preview;
1825
+
1826
+ });
1827
+ }
1828
+ }
1829
+ },
1830
+
1831
+ // Get collections.
1832
+ {
1833
+ method: "GET",
1834
+ endpoint: "/volt/db/collections",
1835
+ content_type: "application/json",
1836
+ rate_limit: "global",
1837
+ callback: async (stream) => {
1838
+
1839
+ // Check ip whitelist.
1840
+ if (!this.preview_ip_whitelist.includes(stream.ip)) {
1841
+ return stream.error({status: Status.forbidden});
1842
+ }
1843
+
1844
+ // Sign in.
1845
+ return stream.success({data: {
1846
+ message: "Successfully retrieved all collections.",
1847
+ collections: await this.get_collections(),
1848
+ }});
1849
+ }
1850
+ },
1851
+
1852
+ // Get collection documents.
1853
+ {
1854
+ method: "GET",
1855
+ endpoint: "/volt/db/documents",
1856
+ content_type: "application/json",
1857
+ rate_limit: "global",
1858
+ params: {
1859
+ collection: "string",
1860
+ },
1861
+ callback: async (stream, params) => {
1862
+
1863
+ // Check ip whitelist.
1864
+ if (!this.preview_ip_whitelist.includes(stream.ip)) {
1865
+ return stream.error({status: Status.forbidden});
1866
+ }
1867
+
1868
+ // Check collection.
1869
+ let col;
1870
+ if ((col = this.collections[params.collection]) == null) {
1871
+ return stream.error({data: {error: `Invalid collection "${params.collection}".`}})
1872
+ }
1873
+
1874
+ // Load docs.
1875
+ let docs = await col.list_all();
1876
+
1877
+ // Sign in.
1878
+ return stream.success({data: {
1879
+ message: "Successfully loaded the document.",
1880
+ documents: docs,
1881
+ }});
1882
+ }
1883
+ },
1884
+
1885
+ // Get document.
1886
+ {
1887
+ method: "GET",
1888
+ endpoint: "/volt/db/document",
1889
+ content_type: "application/json",
1890
+ rate_limit: "global",
1891
+ params: {
1892
+ collection: "string",
1893
+ path: ["string", "object"],
1894
+ uid: {type: ["string", "null"], default: null},
1895
+ },
1896
+ callback: async (stream, params) => {
1897
+
1898
+ // Check ip whitelist.
1899
+ if (!this.preview_ip_whitelist.includes(stream.ip)) {
1900
+ return stream.error({status: Status.forbidden});
1901
+ }
1902
+
1903
+ // Check collection.
1904
+ let col;
1905
+ if ((col = this.collections[params.collection]) == null) {
1906
+ return stream.error({data: {error: `Invalid collection "${params.collection}".`}})
1907
+ }
1908
+
1909
+ // Load doc.
1910
+ let doc;
1911
+ if (params.uid == null) {
1912
+ doc = await col.load(params.path);
1913
+ } else {
1914
+ doc = await col.load(params.uid, params.path);
1915
+ }
1916
+
1917
+ // Sign in.
1918
+ return stream.success({data: {
1919
+ message: "Successfully loaded the document.",
1920
+ document: doc,
1921
+ }});
1922
+ }
1923
+ },
1924
+
1925
+ // Delete document.
1926
+ {
1927
+ method: "DELETE",
1928
+ endpoint: "/volt/db/document",
1929
+ content_type: "application/json",
1930
+ rate_limit: "global",
1931
+ params: {
1932
+ collection: "string",
1933
+ path: ["string", "object"],
1934
+ uid: {type: ["string", "null"], default: null},
1935
+ },
1936
+ callback: async (stream, params) => {
1937
+
1938
+ // Check ip whitelist.
1939
+ if (!this.preview_ip_whitelist.includes(stream.ip)) {
1940
+ return stream.error({status: Status.forbidden});
1941
+ }
1942
+
1943
+ // Check collection.
1944
+ let col;
1945
+ if ((col = this.collections[params.collection]) == null) {
1946
+ return stream.error({data: {error: `Invalid collection "${params.collection}".`}})
1947
+ }
1948
+
1949
+ // Load doc.
1950
+ let doc;
1951
+ if (params.uid == null) {
1952
+ doc = await col.delete(params.path);
1953
+ } else {
1954
+ doc = await col.delete(params.uid, params.path);
1955
+ }
1956
+
1957
+ // Sign in.
1958
+ return stream.success({data: {
1959
+ message: "Successfully deleted the document.",
1960
+ }});
1961
+ }
1962
+ },
1963
+
1964
+ // Update document.
1965
+ {
1966
+ method: "PATCH",
1967
+ endpoint: "/volt/db/document",
1968
+ content_type: "application/json",
1969
+ rate_limit: "global",
1970
+ params: {
1971
+ collection: "string",
1972
+ path: ["string", "object"],
1973
+ uid: {type: ["string", "null"], default: null},
1974
+ content: "object",
1975
+ },
1976
+ callback: async (stream, params) => {
1977
+
1978
+ // Check ip whitelist.
1979
+ if (!this.preview_ip_whitelist.includes(stream.ip)) {
1980
+ return stream.error({status: Status.forbidden});
1981
+ }
1982
+
1983
+ // Check collection.
1984
+ let col;
1985
+ if ((col = this.collections[params.collection]) == null) {
1986
+ return stream.error({data: {error: `Invalid collection "${params.collection}".`}})
1987
+ }
1988
+
1989
+ // Load doc.
1990
+ let doc;
1991
+ if (params.uid == null) {
1992
+ doc = await col.save(params.path, params.content);
1993
+ } else {
1994
+ doc = await col.save(params.uid, params.path, params.content);
1995
+ }
1996
+
1997
+ // Sign in.
1998
+ return stream.success({data: {
1999
+ message: "Successfully updated the document.",
2000
+ }});
2001
+ }
2002
+ },
2003
+ )
2004
+ }
2005
+ */
2006
+ }
2007
+
2008
+ // Connect.
2009
+ async connect(): Promise<void> {
2010
+ try {
2011
+ await this.client?.connect();
2012
+ if (this.client) {
2013
+ this.db = this.client.db();
2014
+ }
2015
+ } catch (error) {
2016
+ console.error(error);
2017
+ throw new Error('Error connecting to the database');
2018
+ }
2019
+ }
2020
+
2021
+ // Initialize.
2022
+ async initialize(): Promise<void> {
2023
+ // Set default config.
2024
+ if (this.config.systemLog === undefined) { this.config.systemLog = {}; }
2025
+
2026
+ this.config.systemLog.path = this.source.join("mongod.log").str()
2027
+
2028
+ if (this.config.systemLog.destination === undefined) {
2029
+ this.config.systemLog.destination = "file";
2030
+ }
2031
+ if (this.config.systemLog.logAppend === undefined) {
2032
+ this.config.systemLog.logAppend = true;
2033
+ }
2034
+ if (this.config.systemLog.logRotate === undefined) {
2035
+ this.config.systemLog.logRotate = "reopen";
2036
+ }
2037
+ if (this.config.systemLog.verbosity === undefined) {
2038
+ this.config.systemLog.verbosity = this.server.production ? 0 : 1;
2039
+ }
2040
+
2041
+ if (this.config.storage === undefined) { this.config.storage = {}; }
2042
+
2043
+ const db_path = this.source.join("db");
2044
+ this.config.storage.dbPath = db_path.str()
2045
+ if (!db_path.exists()) {
2046
+ db_path.mkdir_sync();
2047
+ }
2048
+
2049
+ if (this.config.processManagement === undefined) { this.config.processManagement = {}; }
2050
+ this.config.processManagement.pidFilePath = this.source.join("mongod.pid").str()
2051
+
2052
+ if (this.config.net === undefined) { this.config.net = {}; }
2053
+ if (this.config.net.port === undefined) { this.config.net.port = 27017; }
2054
+ if (this.config.net.bindIp === undefined) { this.config.net.bindIp = "127.0.0.1"; }
2055
+
2056
+ // Mode 2: Start database.
2057
+ if (this.server.is_primary && this.uri == null) {
2058
+ // Create the database.
2059
+ if (!this.source.exists()) {
2060
+ this.source.mkdir_sync();
2061
+ }
2062
+
2063
+ // Set the uri.
2064
+ if (this.uri == null) {
2065
+ this.uri = `mongodb://${this.config.net.bindIp}:${this.config.net.port}/main`
2066
+ }
2067
+
2068
+ // Save the config.
2069
+ const config_path = this.source.join("mongod.json");
2070
+ config_path.save_sync(JSON.stringify(this.config));
2071
+
2072
+ // Start the database.
2073
+ this.proc = spawn(
2074
+ "mongod",
2075
+ ["--config", config_path.str(), ...this.start_args],
2076
+ {
2077
+ stdio: "pipe",
2078
+ detached: true,
2079
+ env: {...process.env},
2080
+ },
2081
+ )
2082
+ this.proc.stdout?.on('data', (data) => {
2083
+ console.log(data.toString());
2084
+ })
2085
+ this.proc.stderr?.on('data', (data) => {
2086
+ console.error(data.toString());
2087
+ })
2088
+ this.proc.on("error", (code, signal) => {
2089
+ console.error(`MongoDB crashed with error signal ${signal}.`);
2090
+ process.exit(code);
2091
+ })
2092
+ }
2093
+
2094
+ // Assign URI.
2095
+ else if (!this.server.is_primary && this.uri == null) {
2096
+ this.uri = `mongodb://${this.config.net.bindIp}:${this.config.net.port}/main`
2097
+ }
2098
+
2099
+ // Initialize client.
2100
+ if (this.uri) {
2101
+ this.client = new MongoClient(this.uri, this.client_opts || {});
2102
+ }
2103
+
2104
+ // Connect.
2105
+ await this.connect();
2106
+
2107
+ // Create collections.
2108
+ this._collections.forEach((info) => {
2109
+ if (this[info.name as keyof this] !== undefined) {
2110
+ throw Error(`Unable to initialize database collection "${info.name}", this attribute name is already used.`);
2111
+ }
2112
+ if (Array.isArray(info.indexes)) {
2113
+ for (const index of info.indexes) {
2114
+ if (index != null && typeof index === "object") {
2115
+ (index as any).forced = true;
2116
+ }
2117
+ }
2118
+ }
2119
+ this[info.name] = this.create_collection(info);
2120
+ })
2121
+ this._uid_collections.forEach((info) => {
2122
+ if (this[info.name as keyof this] !== undefined) {
2123
+ throw Error(`Unable to initialize database collection "${info.name}", this attribute name is already used.`);
2124
+ }
2125
+ if (Array.isArray(info.indexes)) {
2126
+ for (const index of info.indexes) {
2127
+ if (index != null && typeof index === "object") {
2128
+ (index as any).forced = true;
2129
+ }
2130
+ }
2131
+ }
2132
+ this[info.name] = this.create_uid_collection(info);
2133
+ })
2134
+ }
2135
+
2136
+ // Close.
2137
+ async close(): Promise<void> {
2138
+ logger.log(0, log_source, "Stopping the database.");
2139
+ await this.client?.close();
2140
+ }
2141
+
2142
+ /* @docs:
2143
+ * @title: Create Collection
2144
+ * @description: Create a database collection.
2145
+ */
2146
+ create_collection(info: {
2147
+ name: string,
2148
+ indexes?: Array<{
2149
+ keys: string[] | string | Record<string, any>,
2150
+ options?: Record<string, any>,
2151
+ commit_quorum?: any
2152
+ }>,
2153
+ ttl?: number | null
2154
+ } | string): Collection {
2155
+ // Set name by single string argument.
2156
+ let name: string;
2157
+ let indexes: Array<any> = [];
2158
+ let ttl: number | null = null;
2159
+
2160
+ if (typeof info === "string") {
2161
+ name = info;
2162
+ } else {
2163
+ name = info.name;
2164
+ indexes = info.indexes || [];
2165
+ ttl = info.ttl || null;
2166
+ }
2167
+
2168
+ // Check collection.
2169
+ if (name in this.collections) {
2170
+ throw Error(`Collection "${name}" is already initialized.`)
2171
+ }
2172
+ const col = new Collection(
2173
+ name,
2174
+ this.db.collection(name),
2175
+ ttl,
2176
+ indexes,
2177
+ );
2178
+ this.collections[name] = col;
2179
+ return col;
2180
+ }
2181
+
2182
+ /* @docs:
2183
+ * @title: Create UID Based Collection
2184
+ * @description: Create a UID based database collection.
2185
+ */
2186
+ create_uid_collection(info: {
2187
+ name: string,
2188
+ indexes?: Array<{
2189
+ keys: string[] | string | Record<string, any>,
2190
+ options?: Record<string, any>,
2191
+ commit_quorum?: any
2192
+ }>,
2193
+ ttl?: number | null
2194
+ } | string): UIDCollection {
2195
+ // Set name by single string argument.
2196
+ let name: string;
2197
+ let indexes: Array<any> = [];
2198
+ let ttl: number | null = null;
2199
+
2200
+ if (typeof info === "string") {
2201
+ name = info;
2202
+ } else {
2203
+ name = info.name;
2204
+ indexes = info.indexes || [];
2205
+ ttl = info.ttl || null;
2206
+ }
2207
+
2208
+ // Check collection.
2209
+ if (name in this.collections) {
2210
+ throw Error(`Collection "${name}" is already initialized.`)
2211
+ }
2212
+ const col = new UIDCollection(
2213
+ name,
2214
+ this.db.collection(name),
2215
+ indexes,
2216
+ ttl,
2217
+ );
2218
+ this.collections[name] = col;
2219
+ return col;
2220
+ }
2221
+
2222
+ /* @docs:
2223
+ * @title: Get Collections
2224
+ * @description: Get the names of the initializated database collections.
2225
+ */
2226
+ async get_collections(): Promise<string[]> {
2227
+ const created = Object.keys(this.collections);
2228
+ const database = (await this.db.listCollections().toArray()).map((item: any) => item.name);
2229
+ return created.concat(database)
2230
+ .filter((value, index, self) => self.indexOf(value) === index)
2231
+ .sort((a, b) => {
2232
+ const result = a.toLowerCase().localeCompare(b.toLowerCase());
2233
+ if (a.startsWith('_') && b.startsWith('_')) { return result; }
2234
+ if (a.startsWith('_')) { return 1; }
2235
+ if (b.startsWith('_')) { return -1; }
2236
+ return result;
2237
+ });
2238
+ }
2239
+ }
2240
+
2241
+ export { Collection, UIDCollection, Database };