dobo 2.0.0 → 2.2.0

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 (197) hide show
  1. package/.github/FUNDING.yml +13 -0
  2. package/.github/workflows/repo-lockdown.yml +24 -0
  3. package/.jsdoc.conf.json +45 -0
  4. package/LICENSE +1 -1
  5. package/README.md +38 -19
  6. package/docs/Dobo.html +26 -0
  7. package/docs/data/search.json +1 -0
  8. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  9. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  10. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  11. package/docs/global.html +7 -0
  12. package/docs/index.html +3 -0
  13. package/docs/index.js.html +578 -0
  14. package/docs/lib_collect-connections.js.html +39 -0
  15. package/docs/lib_collect-drivers.js.html +52 -0
  16. package/docs/lib_collect-features.js.html +36 -0
  17. package/docs/lib_collect-schemas.js.html +94 -0
  18. package/docs/lib_index.js.html +6 -0
  19. package/docs/method_model_create.js.html +35 -0
  20. package/docs/method_model_drop.js.html +34 -0
  21. package/docs/method_model_exists.js.html +40 -0
  22. package/docs/method_record_count.js.html +69 -0
  23. package/docs/method_record_create.js.html +114 -0
  24. package/docs/method_record_find-all.js.html +44 -0
  25. package/docs/method_record_find-one.js.html +73 -0
  26. package/docs/method_record_find.js.html +118 -0
  27. package/docs/method_record_get.js.html +92 -0
  28. package/docs/method_record_remove.js.html +75 -0
  29. package/docs/method_record_update.js.html +107 -0
  30. package/docs/method_record_upsert.js.html +54 -0
  31. package/docs/method_sanitize_body.js.html +88 -0
  32. package/docs/method_sanitize_date.js.html +30 -0
  33. package/docs/method_sanitize_id.js.html +20 -0
  34. package/docs/method_validate.js.html +249 -0
  35. package/docs/module-Lib.html +3 -0
  36. package/docs/scripts/core.js +725 -0
  37. package/docs/scripts/core.min.js +23 -0
  38. package/docs/scripts/resize.js +90 -0
  39. package/docs/scripts/search.js +265 -0
  40. package/docs/scripts/search.min.js +6 -0
  41. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  42. package/docs/scripts/third-party/fuse.js +9 -0
  43. package/docs/scripts/third-party/hljs-line-num-original.js +366 -0
  44. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  45. package/docs/scripts/third-party/hljs-original.js +5164 -0
  46. package/docs/scripts/third-party/hljs.js +1 -0
  47. package/docs/scripts/third-party/popper.js +5 -0
  48. package/docs/scripts/third-party/tippy.js +1 -0
  49. package/docs/scripts/third-party/tocbot.js +671 -0
  50. package/docs/scripts/third-party/tocbot.min.js +1 -0
  51. package/docs/static/bitcoin.jpeg +0 -0
  52. package/docs/static/home.md +25 -0
  53. package/docs/static/logo-ecosystem.png +0 -0
  54. package/docs/static/logo.png +0 -0
  55. package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
  56. package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
  57. package/docs/styles/clean-jsdoc-theme-light.css +482 -0
  58. package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  59. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  60. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  61. package/extend/bajo/intl/en-US.json +69 -30
  62. package/extend/bajo/intl/id.json +58 -29
  63. package/extend/bajoCli/applet/clear-record.js +22 -0
  64. package/extend/bajoCli/applet/connection.js +5 -5
  65. package/extend/bajoCli/applet/count-record.js +27 -0
  66. package/extend/bajoCli/applet/create-aggregate.js +33 -0
  67. package/extend/bajoCli/applet/create-histogram.js +33 -0
  68. package/extend/bajoCli/applet/create-record.js +39 -0
  69. package/extend/bajoCli/applet/find-record.js +27 -0
  70. package/extend/bajoCli/applet/get-record.js +27 -0
  71. package/extend/bajoCli/applet/lib/post-process.js +25 -26
  72. package/extend/bajoCli/applet/model.js +22 -0
  73. package/extend/bajoCli/applet/rebuild-model.js +91 -0
  74. package/extend/bajoCli/applet/remove-record.js +27 -0
  75. package/extend/bajoCli/applet/update-record.js +44 -0
  76. package/extend/bajoCli/applet.js +0 -0
  77. package/extend/dobo/driver/memory.js +170 -0
  78. package/extend/dobo/feature/created-at.js +10 -8
  79. package/extend/dobo/feature/dt.js +0 -0
  80. package/extend/dobo/feature/immutable.js +30 -0
  81. package/extend/dobo/feature/int-id.js +0 -0
  82. package/extend/dobo/feature/removed-at.js +35 -57
  83. package/extend/dobo/feature/updated-at.js +14 -12
  84. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +5 -9
  85. package/extend/waibuStatic/virtual.json +0 -0
  86. package/index.js +420 -337
  87. package/lib/collect-connections.js +60 -21
  88. package/lib/collect-drivers.js +29 -35
  89. package/lib/collect-features.js +40 -0
  90. package/lib/collect-models.js +319 -0
  91. package/lib/factory/action.js +161 -0
  92. package/lib/factory/connection.js +62 -0
  93. package/lib/factory/driver.js +358 -0
  94. package/lib/factory/feature.js +33 -0
  95. package/lib/factory/model/_util.js +402 -0
  96. package/lib/factory/model/build.js +15 -0
  97. package/lib/factory/model/clear-record.js +17 -0
  98. package/lib/factory/model/count-record.js +17 -0
  99. package/lib/factory/model/create-aggregate.js +17 -0
  100. package/lib/factory/model/create-attachment.js +29 -0
  101. package/lib/factory/model/create-histogram.js +17 -0
  102. package/lib/factory/model/create-record.js +35 -0
  103. package/lib/factory/model/drop.js +15 -0
  104. package/lib/factory/model/exists.js +21 -0
  105. package/lib/factory/model/find-all-record.js +71 -0
  106. package/lib/factory/model/find-attachment.js +29 -0
  107. package/lib/factory/model/find-one-record.js +19 -0
  108. package/lib/factory/model/find-record.js +103 -0
  109. package/lib/factory/model/get-attachment.js +15 -0
  110. package/lib/factory/model/get-record.js +79 -0
  111. package/lib/factory/model/list-attachment.js +37 -0
  112. package/lib/{add-fixtures.js → factory/model/load-fixtures.js} +69 -67
  113. package/lib/factory/model/remove-attachment.js +15 -0
  114. package/lib/factory/model/remove-record.js +59 -0
  115. package/lib/factory/model/sanitize-body.js +62 -0
  116. package/lib/factory/model/sanitize-id.js +7 -0
  117. package/lib/factory/model/sanitize-record.js +26 -0
  118. package/lib/factory/model/update-attachment.js +9 -0
  119. package/lib/factory/model/update-record.js +81 -0
  120. package/lib/factory/model/upsert-record.js +95 -0
  121. package/lib/factory/model/validate.js +232 -0
  122. package/lib/factory/model.js +150 -0
  123. package/lib/index.js +3 -0
  124. package/package.json +45 -36
  125. package/wiki/APPLETS.md +57 -0
  126. package/wiki/CHANGES.md +46 -0
  127. package/wiki/CONFIG.md +25 -0
  128. package/wiki/CONTRIBUTING.md +5 -0
  129. package/wiki/DEV-GUIDE.md +1 -0
  130. package/wiki/ECOSYSTEM.md +20 -0
  131. package/wiki/GETTING-STARTED.md +166 -0
  132. package/{docs/query-language.md → wiki/QUERY-LANGUAGE.md} +0 -0
  133. package/wiki/USER-GUIDE.md +1 -0
  134. package/extend/bajoCli/applet/model-clear.js +0 -11
  135. package/extend/bajoCli/applet/model-rebuild.js +0 -101
  136. package/extend/bajoCli/applet/record-create.js +0 -41
  137. package/extend/bajoCli/applet/record-find.js +0 -27
  138. package/extend/bajoCli/applet/record-get.js +0 -24
  139. package/extend/bajoCli/applet/record-remove.js +0 -24
  140. package/extend/bajoCli/applet/record-update.js +0 -47
  141. package/extend/bajoCli/applet/schema.js +0 -22
  142. package/extend/bajoCli/applet/stat-count.js +0 -24
  143. package/lib/build-bulk-action.js +0 -12
  144. package/lib/check-unique.js +0 -39
  145. package/lib/collect-feature.js +0 -25
  146. package/lib/collect-schemas.js +0 -83
  147. package/lib/exec-feature-hook.js +0 -13
  148. package/lib/exec-validation.js +0 -21
  149. package/lib/generic-prop-sanitizer.js +0 -31
  150. package/lib/handle-attachment-upload.js +0 -16
  151. package/lib/mem-db/conn-sanitizer.js +0 -8
  152. package/lib/mem-db/instantiate.js +0 -41
  153. package/lib/mem-db/method/model/clear.js +0 -6
  154. package/lib/mem-db/method/model/create.js +0 -5
  155. package/lib/mem-db/method/model/drop.js +0 -5
  156. package/lib/mem-db/method/model/exists.js +0 -5
  157. package/lib/mem-db/method/record/create.js +0 -12
  158. package/lib/mem-db/method/record/find.js +0 -20
  159. package/lib/mem-db/method/record/get.js +0 -9
  160. package/lib/mem-db/method/record/remove.js +0 -13
  161. package/lib/mem-db/method/record/update.js +0 -15
  162. package/lib/mem-db/method/stat/count.js +0 -11
  163. package/lib/mem-db/start.js +0 -25
  164. package/lib/merge-attachment-info.js +0 -16
  165. package/lib/multi-rel-rows.js +0 -42
  166. package/lib/resolve-method.js +0 -16
  167. package/lib/sanitize-schema.js +0 -197
  168. package/lib/single-rel-rows.js +0 -38
  169. package/method/attachment/copy-uploaded.js +0 -34
  170. package/method/attachment/create.js +0 -29
  171. package/method/attachment/find.js +0 -27
  172. package/method/attachment/get-path.js +0 -12
  173. package/method/attachment/get.js +0 -12
  174. package/method/attachment/pre-check.js +0 -9
  175. package/method/attachment/remove.js +0 -11
  176. package/method/attachment/update.js +0 -7
  177. package/method/bulk/create.js +0 -46
  178. package/method/model/clear.js +0 -22
  179. package/method/model/create.js +0 -19
  180. package/method/model/drop.js +0 -19
  181. package/method/model/exists.js +0 -24
  182. package/method/record/clear.js +0 -24
  183. package/method/record/count.js +0 -44
  184. package/method/record/create.js +0 -71
  185. package/method/record/find-all.js +0 -25
  186. package/method/record/find-one.js +0 -56
  187. package/method/record/find.js +0 -52
  188. package/method/record/get.js +0 -47
  189. package/method/record/remove.js +0 -41
  190. package/method/record/update.js +0 -63
  191. package/method/record/upsert.js +0 -35
  192. package/method/sanitize/body.js +0 -70
  193. package/method/sanitize/date.js +0 -14
  194. package/method/sanitize/id.js +0 -7
  195. package/method/stat/aggregate.js +0 -23
  196. package/method/stat/histogram.js +0 -26
  197. package/method/validate.js +0 -157
@@ -0,0 +1,578 @@
1
+ <!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: index.js</title><!--[if lt IE 9]>
2
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
3
+ <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Dobo API</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="Dobo.html">Dobo</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TPropType">TPropType</a></div><div class="sidebar-section-children"><a href="global.html#TRecordCountOptions">TRecordCountOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordCreateOptions">TRecordCreateOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFilter">TRecordFilter</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFindOptions">TRecordFindOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFindResult">TRecordFindResult</a></div><div class="sidebar-section-children"><a href="global.html#TRecordGetOptions">TRecordGetOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordGetResult">TRecordGetResult</a></div><div class="sidebar-section-children"><a href="global.html#TRecordPagination">TRecordPagination</a></div><div class="sidebar-section-children"><a href="global.html#TRecordRemoveOptions">TRecordRemoveOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordSort">TRecordSort</a></div><div class="sidebar-section-children"><a href="global.html#TRecordSortKey">TRecordSortKey</a></div><div class="sidebar-section-children"><a href="global.html#TRecordUpdateOptions">TRecordUpdateOptions</a></div><div class="sidebar-section-children"><a href="global.html#TValidator">TValidator</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorBoolean">TValidatorBoolean</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorDate">TValidatorDate</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorNumber">TValidatorNumber</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorString">TValidatorString</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorTimestamp">TValidatorTimestamp</a></div><div class="sidebar-section-children"><a href="global.html#factory">factory</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/dobo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/dobo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://dobo.bajo.app/" target="">Dobo</a></div><div class="navbar-item"><a id="" href="https://bajo.app/" target="">Bajo</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">index.js</h1></header><article><pre class="prettyprint source lang-js"><code>import collectConnections from './lib/collect-connections.js'
4
+ import collectDrivers from './lib/collect-drivers.js'
5
+ import collectFeatures from './lib/collect-features.js'
6
+ import collectSchemas from './lib/collect-schemas.js'
7
+ import memDbStart from './lib/mem-db/start.js'
8
+ import memDbInstantiate from './lib/mem-db/instantiate.js'
9
+ import nql from '@tryghost/nql'
10
+ import path from 'path'
11
+
12
+ /**
13
+ * @typedef {string} TRecordSortKey
14
+ */
15
+
16
+ /**
17
+ * Key value pairs used as sort information:
18
+ * - Key represent model's field name
19
+ * - value represent its sort order: ```1``` for ascending order, and ```-1``` for descending order
20
+ *
21
+ * Example: to sort by firstName (ascending) and lastName (descending)
22
+ * ```javascript
23
+ * const sort = {
24
+ * firstName: 1,
25
+ * lastName: -1
26
+ * }
27
+ * ```
28
+ *
29
+ * @typedef {Object.&lt;string, TRecordSortKey>} TRecordSort
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} TRecordPagination
34
+ * @property {number} limit - Number of records per page
35
+ * @property {number} page - Page number
36
+ * @property {number} skip - Records to skip
37
+ * @property {TRecordSort} sort - Sort order
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} TPropType
42
+ * @property {Object} integer
43
+ * @property {string} [integer.validator=number]
44
+ * @property {Object} smallint
45
+ * @property {string} [smallint.validator=number]
46
+ * @property {Object} text
47
+ * @property {string} [text.validator=string]
48
+ * @property {string} [text.textType=string]
49
+ * @property {string[]} [text.values=['text', 'mediumtext', 'longtext']]
50
+ * @property {Object} string
51
+ * @property {string} [string.validator=string]
52
+ * @property {maxLength} [string.maxLength=255]
53
+ * @property {minLength} [string.minLength=0]
54
+ * @property {Object} float
55
+ * @property {string} [float.validator=number]
56
+ * @property {Object} double
57
+ * @property {string} [double.validator=number]
58
+ * @property {Object} boolean
59
+ * @property {string} [boolean.validator=boolean]
60
+ * @property {Object} datetime
61
+ * @property {string} [datetime.validator=date]
62
+ * @property {Object} date
63
+ * @property {string} [date.validator=date]
64
+ * @property {Object} time
65
+ * @property {string} [time.validator=date]
66
+ * @property {Object} timestamp
67
+ * @property {string} [timestamp.validator=timestamp]
68
+ * @property {Object} object={}
69
+ * @property {Object} array={}
70
+ */
71
+ const propType = {
72
+ integer: {
73
+ validator: 'number'
74
+ },
75
+ smallint: {
76
+ validator: 'number'
77
+ },
78
+ text: {
79
+ validator: 'string',
80
+ textType: 'text',
81
+ values: ['text', 'mediumtext', 'longtext']
82
+ },
83
+ string: {
84
+ validator: 'string',
85
+ maxLength: 255,
86
+ minLength: 0
87
+ },
88
+ float: {
89
+ validator: 'number'
90
+ },
91
+ double: {
92
+ validator: 'number'
93
+ },
94
+ boolean: {
95
+ validator: 'boolean'
96
+ },
97
+ date: {
98
+ validator: 'date'
99
+ },
100
+ datetime: {
101
+ validator: 'date'
102
+ },
103
+ time: {
104
+ validator: 'date'
105
+ },
106
+ timestamp: {
107
+ validator: 'timestamp'
108
+ },
109
+ object: {},
110
+ array: {}
111
+ }
112
+
113
+ /**
114
+ * Plugin factory
115
+ *
116
+ * @param {string} pkgName - NPM package name
117
+ * @returns {class}
118
+ */
119
+ async function factory (pkgName) {
120
+ const me = this
121
+
122
+ /**
123
+ * Dobo Database Framework for {@link https://github.com/ardhi/bajo|Bajo}.
124
+ *
125
+ * See {@tutorial ecosystem} for available drivers &amp; tools
126
+ *
127
+ * @class
128
+ */
129
+ class Dobo extends this.app.pluginClass.base {
130
+ /**
131
+ * @constant {string}
132
+ * @memberof Dobo
133
+ * @default 'db'
134
+ */
135
+ static alias = 'db'
136
+
137
+ /**
138
+ * @constant {string[]}
139
+ * @memberof Dobo
140
+ * @default ['count', 'avg', 'min', 'max', 'sum']
141
+ */
142
+ static aggregateTypes = ['count', 'avg', 'min', 'max', 'sum']
143
+ /**
144
+ * @constant {TPropType}
145
+ * @memberof Dobo
146
+ */
147
+ static propType = propType
148
+
149
+ constructor () {
150
+ super(pkgName, me.app)
151
+ this.config = {
152
+ connections: [],
153
+ validationParams: {
154
+ abortEarly: false,
155
+ convert: false,
156
+ allowUnknown: true
157
+ },
158
+ default: {
159
+ property: {
160
+ text: {
161
+ textType: 'text'
162
+ },
163
+ string: {
164
+ length: 50
165
+ }
166
+ },
167
+ filter: {
168
+ limit: 25,
169
+ maxLimit: 200,
170
+ hardLimit: 10000,
171
+ sort: ['dt:-1', 'updatedAt:-1', 'updated_at:-1', 'createdAt:-1', 'createdAt:-1', 'ts:-1', 'username', 'name']
172
+ },
173
+ idField: {
174
+ type: 'string',
175
+ maxLength: 50,
176
+ required: true,
177
+ index: { type: 'primary' }
178
+ }
179
+ },
180
+ memDb: {
181
+ createDefConnAtStart: true,
182
+ persistence: {
183
+ syncPeriodDur: '1s'
184
+ }
185
+ },
186
+ applet: {
187
+ confirmation: false
188
+ }
189
+ }
190
+
191
+ /**
192
+ * @type {Object[]}
193
+ */
194
+ this.drivers = []
195
+
196
+ /**
197
+ * @type {Object[]}
198
+ */
199
+ this.connections = []
200
+
201
+ /**
202
+ * @type {Object[]}
203
+ */
204
+ this.features = []
205
+
206
+ /**
207
+ * @type {Object[]}
208
+ */
209
+ this.schemas = []
210
+ }
211
+
212
+ /**
213
+ * Initialize plugin and performing the following tasks:
214
+ * - {@link module:Lib.collectDrivers|Collecting all drivers}
215
+ * - {@link module:Lib.collectConnections|Collecting all connections}
216
+ * - {@link module:Lib.collectFeatures|Collecting all features}
217
+ * - {@link module:Lib.collectSchemas|Collecting all schemas}
218
+ * @method
219
+ * @async
220
+ */
221
+ init = async () => {
222
+ const { buildCollections } = this.app.bajo
223
+ const { fs } = this.app.lib
224
+ const checkType = async (item, items) => {
225
+ const { filter } = this.app.lib._
226
+ const existing = filter(items, { type: 'dobo:memory' })
227
+ if (existing.length > 1) this.fatal('onlyOneConnType%s', item.type)
228
+ }
229
+
230
+ fs.ensureDirSync(`${this.dir.data}/attachment`)
231
+ await collectDrivers.call(this)
232
+ if (this.config.memDb.createDefConnAtStart) {
233
+ this.config.connections.push({
234
+ type: 'dobo:memory',
235
+ name: 'memory'
236
+ })
237
+ }
238
+ this.connections = await buildCollections({ ns: this.ns, container: 'connections', handler: collectConnections, dupChecks: ['name', checkType] })
239
+ if (this.connections.length === 0) this.log.warn('notFound%s', this.t('connection'))
240
+ await collectFeatures.call(this)
241
+ await collectSchemas.call(this)
242
+ }
243
+
244
+ /**
245
+ * Start plugin
246
+ *
247
+ * @method
248
+ * @async
249
+ * @param {(string|Array)} [conns=all] - Which connections should be run on start
250
+ * @param {boolean} [noRebuild=false] - Set ```true``` to ALWAYS rebuild model on start. Yes, only set it to ```true``` if you REALLY know what you're doing!!!
251
+ */
252
+ start = async (conns = 'all', noRebuild = true) => {
253
+ const { importModule, breakNsPath } = this.app.bajo
254
+ const { find, filter, isString, map } = this.app.lib._
255
+ if (conns === 'all') conns = this.connections
256
+ else if (isString(conns)) conns = filter(this.connections, { name: conns })
257
+ else conns = map(conns, c => find(this.connections, { name: c }))
258
+ for (const c of conns) {
259
+ const { ns } = breakNsPath(c.type)
260
+ const schemas = filter(this.schemas, { connection: c.name })
261
+ const mod = c.type === 'dobo:memory' ? memDbInstantiate : await importModule(`${ns}:/extend/${this.ns}/boot/instantiate.js`)
262
+ await mod.call(this.app[ns], { connection: c, noRebuild, schemas })
263
+ this.log.trace('driverInstantiated%s%s', c.driver, c.name)
264
+ }
265
+ await memDbStart.call(this)
266
+ }
267
+
268
+ /**
269
+ * Pick only fields defined from a record
270
+ *
271
+ * @method
272
+ * @async
273
+ * @param {Object} [options={}] - Options object
274
+ * @param {Object} options.record - Record to pick fields from
275
+ * @param {Array} options.fields - Array of field names to be picked
276
+ * @param {Object} options.schema - Associated record's schema
277
+ * @param {Object} [options.hidden=[]] - Additional fields to be hidden in addition the one defined in schema
278
+ * @param {boolean} [options.forceNoHidden] - Force ALL fields to be picked, thus ignoring hidden fields
279
+ * @returns {Object}
280
+ */
281
+ pickRecord = async ({ record, fields, schema = {}, hidden = [], forceNoHidden } = {}) => {
282
+ const { isArray, pick, clone, isEmpty, omit } = this.app.lib._
283
+ const { dayjs } = this.app.lib
284
+
285
+ const transform = async ({ record, schema, hidden = [], forceNoHidden } = {}) => {
286
+ if (record._id) {
287
+ record.id = record._id
288
+ delete record._id
289
+ }
290
+ const defHidden = [...schema.hidden, ...hidden]
291
+ let result = {}
292
+ for (const p of schema.properties) {
293
+ if (!forceNoHidden &amp;&amp; defHidden.includes(p.name)) continue
294
+ result[p.name] = record[p.name] ?? null
295
+ if (record[p.name] === null) continue
296
+ switch (p.type) {
297
+ case 'boolean': result[p.name] = !!result[p.name]; break
298
+ case 'time': result[p.name] = dayjs(record[p.name]).format('HH:mm:ss'); break
299
+ case 'date': result[p.name] = dayjs(record[p.name]).format('YYYY-MM-DD'); break
300
+ case 'datetime': result[p.name] = dayjs(record[p.name]).toISOString(); break
301
+ }
302
+ }
303
+ result = await this.sanitizeBody({ body: result, schema, partial: true, ignoreNull: true })
304
+ if (record._rel) result._rel = record._rel
305
+ return result
306
+ }
307
+
308
+ if (isEmpty(record)) return record
309
+ if (hidden.length > 0) record = omit(record, hidden)
310
+ if (!isArray(fields)) return await transform.call(this, { record, schema, hidden, forceNoHidden })
311
+ const fl = clone(fields)
312
+ if (!fl.includes('id')) fl.unshift('id')
313
+ if (record._rel) fl.push('_rel')
314
+ return pick(await transform.call(this, { record, schema, hidden, forceNoHidden }), fl)
315
+ }
316
+
317
+ /**
318
+ * Prepare records pagination:
319
+ * - making sure records limit is obeyed
320
+ * - making sure page is a positive value
321
+ * - if skip is given, recalculate limit to use skip instead of page number
322
+ * - Build sort info
323
+ *
324
+ * @method
325
+ * @async
326
+ * @param {Object} [filter={}] - Filter object
327
+ * @param {Object} schema - Model's schema
328
+ * @param {Object} options - Options
329
+ * @returns {TRecordPagination}
330
+ */
331
+ prepPagination = async (filter = {}, schema, options = {}) => {
332
+ const buildPageSkipLimit = (filter) => {
333
+ let limit = parseInt(filter.limit) || this.config.default.filter.limit
334
+ if (limit === -1) limit = this.config.default.filter.maxLimit
335
+ if (limit > this.config.default.filter.maxLimit) limit = this.config.default.filter.maxLimit
336
+ if (limit &lt; 1) limit = 1
337
+ let page = parseInt(filter.page) || 1
338
+ if (page &lt; 1) page = 1
339
+ let skip = (page - 1) * limit
340
+ if (filter.skip) {
341
+ skip = parseInt(filter.skip) || skip
342
+ page = undefined
343
+ }
344
+ if (skip &lt; 0) skip = 0
345
+ return { page, skip, limit }
346
+ }
347
+
348
+ const buildSort = (input, schema, allowSortUnindexed) => {
349
+ const { isEmpty, map, each, isPlainObject, isString, trim, keys } = this.app.lib._
350
+ let sort
351
+ if (schema &amp;&amp; isEmpty(input)) {
352
+ const columns = map(schema.properties, 'name')
353
+ each(this.config.default.filter.sort, s => {
354
+ const [col] = s.split(':')
355
+ if (columns.includes(col)) {
356
+ input = s
357
+ return false
358
+ }
359
+ })
360
+ }
361
+ if (!isEmpty(input)) {
362
+ if (isPlainObject(input)) sort = input
363
+ else if (isString(input)) {
364
+ const item = {}
365
+ each(input.split('+'), text => {
366
+ let [col, dir] = map(trim(text).split(':'), i => trim(i))
367
+ dir = (dir ?? '').toUpperCase()
368
+ dir = dir === 'DESC' ? -1 : parseInt(dir) || 1
369
+ item[col] = dir / Math.abs(dir)
370
+ })
371
+ sort = item
372
+ }
373
+ if (schema) {
374
+ const items = keys(sort)
375
+ each(items, i => {
376
+ if (!schema.sortables.includes(i) &amp;&amp; !allowSortUnindexed) throw this.error('sortOnUnindexedField%s%s', i, schema.name)
377
+ // if (schema.fullText.fields.includes(i)) throw this.error('Can\'t sort on full-text index: \'%s@%s\'', i, schema.name)
378
+ })
379
+ }
380
+ }
381
+ return sort
382
+ }
383
+
384
+ const { page, skip, limit } = buildPageSkipLimit.call(this, filter)
385
+ let sortInput = filter.sort
386
+ try {
387
+ sortInput = JSON.parse(sortInput)
388
+ } catch (err) {}
389
+ const sort = buildSort.call(this, sortInput, schema, options.allowSortUnindexed)
390
+ return { limit, page, skip, sort }
391
+ }
392
+
393
+ buildMatch = ({ input = '', schema, options }) => {
394
+ const { isPlainObject, trim } = this.app.lib._
395
+ if (isPlainObject(input)) return input
396
+ const split = (value, schema) => {
397
+ let [field, val] = value.split(':').map(i => i.trim())
398
+ if (!val) {
399
+ val = field
400
+ field = '*'
401
+ }
402
+ return { field, value: val }
403
+ }
404
+ input = trim(input)
405
+ let items = {}
406
+ if (isPlainObject(input)) items = input
407
+ else if (input[0] === '{') items = JSON.parse(input)
408
+ else {
409
+ for (const item of input.split('+').map(i => i.trim())) {
410
+ const part = split(item, schema)
411
+ if (!items[part.field]) items[part.field] = []
412
+ items[part.field].push(...part.value.split(' ').filter(v => ![''].includes(v)))
413
+ }
414
+ }
415
+ const matcher = {}
416
+ for (const f of schema.fullText.fields) {
417
+ const value = []
418
+ if (typeof items[f] === 'string') items[f] = [items[f]]
419
+ if (Object.prototype.hasOwnProperty.call(items, f)) value.push(...items[f])
420
+ matcher[f] = value
421
+ }
422
+ if (Object.prototype.hasOwnProperty.call(items, '*')) matcher['*'] = items['*']
423
+ return matcher
424
+ }
425
+
426
+ buildQuery = ({ filter, schema, options = {} } = {}) => {
427
+ const { trim, find, isString, isPlainObject } = this.app.lib._
428
+ let query = {}
429
+ if (isString(filter.query)) {
430
+ try {
431
+ filter.query = trim(filter.query)
432
+ filter.orgQuery = filter.query
433
+ if (trim(filter.query).startsWith('{')) query = JSON.parse(filter.query)
434
+ else if (filter.query.includes(':')) query = nql(filter.query).parse()
435
+ else {
436
+ const fields = schema.sortables.filter(f => {
437
+ const field = find(schema.properties, { name: f, type: 'string' })
438
+ return !!field
439
+ })
440
+ const parts = fields.map(f => {
441
+ if (filter.query[0] === '*') return `${f}:~$'${filter.query.replaceAll('*', '')}'`
442
+ if (filter.query[filter.length - 1] === '*') return `${f}:~^'${filter.query.replaceAll('*', '')}'`
443
+ return `${f}:~'${filter.query.replaceAll('*', '')}'`
444
+ })
445
+ if (parts.length === 1) query = nql(parts[0]).parse()
446
+ else if (parts.length > 1) query = nql(parts.join(',')).parse()
447
+ }
448
+ } catch (err) {
449
+ this.error('invalidQuery', { orgMessage: err.message })
450
+ }
451
+ } else if (isPlainObject(filter.query)) query = filter.query
452
+ return this.sanitizeQuery(query, schema)
453
+ }
454
+
455
+ sanitizeQuery = (query, schema, parent) => {
456
+ const { cloneDeep, isPlainObject, isArray, find } = this.app.lib._
457
+ const { isSet } = this.app.lib.aneka
458
+ const { dayjs } = this.app.lib
459
+ const obj = cloneDeep(query)
460
+ const keys = Object.keys(obj)
461
+ const sanitize = (key, val, p) => {
462
+ if (!isSet(val)) return val
463
+ const prop = find(schema.properties, { name: key.startsWith('$') ? p : key })
464
+ if (!prop) return val
465
+ if (['datetime', 'date', 'time'].includes(prop.type)) {
466
+ const dt = dayjs(val)
467
+ return dt.isValid() ? dt.toDate() : val
468
+ } else if (['smallint', 'integer'].includes(prop.type)) return parseInt(val) || val
469
+ else if (['float', 'double'].includes(prop.type)) return parseFloat(val) || val
470
+ else if (['boolean'].includes(prop.type)) return !!val
471
+ return val
472
+ }
473
+ keys.forEach(k => {
474
+ const v = obj[k]
475
+ if (isPlainObject(v)) obj[k] = this.sanitizeQuery(v, schema, k)
476
+ else if (isArray(v)) {
477
+ v.forEach((i, idx) => {
478
+ if (isPlainObject(i)) obj[k][idx] = this.sanitizeQuery(i, schema, k)
479
+ })
480
+ } else obj[k] = sanitize(k, v, parent)
481
+ })
482
+ return obj
483
+ }
484
+
485
+ validationErrorMessage = (err) => {
486
+ let text = err.message
487
+ if (err.details) {
488
+ text += ' -> '
489
+ text += this.app.bajo.join(err.details.map((d, idx) => {
490
+ return `${d.field}@${err.model}: ${d.error} (${d.value})`
491
+ }))
492
+ }
493
+ return text
494
+ }
495
+
496
+ getConnection = (name) => {
497
+ const { find } = this.app.lib._
498
+ return find(this.connections, { name })
499
+ }
500
+
501
+ getInfo = (name) => {
502
+ const { breakNsPath } = this.app.bajo
503
+ const { find, map, isEmpty } = this.app.lib._
504
+ const schema = this.getSchema(name)
505
+ const conn = this.getConnection(schema.connection)
506
+ let { ns, path: type } = breakNsPath(conn.type)
507
+ if (isEmpty(type)) type = conn.type
508
+ const driver = find(this.drivers, { type, ns, driver: conn.driver })
509
+ const instance = find(this.app[driver.ns].instances, { name: schema.connection })
510
+ const opts = conn.type === 'mssql' ? { includeTriggerModifications: true } : undefined
511
+ const returning = [map(schema.properties, 'name'), opts]
512
+ return { instance, driver, connection: conn, returning, schema }
513
+ }
514
+
515
+ getSchema = (input, cloned = true) => {
516
+ const { find, isPlainObject, cloneDeep } = this.app.lib._
517
+ const { pascalCase } = this.app.lib.aneka
518
+ let name = isPlainObject(input) ? input.name : input
519
+ name = pascalCase(name)
520
+ const schema = find(this.schemas, { name })
521
+ if (!schema) throw this.error('unknownModelSchema%s', name)
522
+ return cloned ? cloneDeep(schema) : schema
523
+ }
524
+
525
+ getField = (name, model) => {
526
+ const { getInfo } = this.app.dobo
527
+ const { find } = this.app.lib._
528
+ const { schema } = getInfo(model)
529
+
530
+ return find(schema.properties, { name })
531
+ }
532
+
533
+ hasField = (name, model) => {
534
+ return !!this.getField(name, model)
535
+ }
536
+
537
+ getMemdbStorage = (name, fields = []) => {
538
+ const { map, pick } = this.app.lib._
539
+ const all = this.memDb.storage[name] ?? []
540
+ if (fields.length === 0) return all
541
+ return map(all, item => pick(item, fields))
542
+ }
543
+
544
+ listAttachments = async ({ model, id = '*', field = '*', file = '*' } = {}, { uriEncoded = true } = {}) => {
545
+ const { map, kebabCase } = this.app.lib._
546
+ const { pascalCase } = this.app.lib.aneka
547
+ const { importPkg, getPluginDataDir } = this.app.bajo
548
+ const mime = await importPkg('waibu:mime')
549
+ const { fastGlob } = this.app.lib
550
+ const root = `${getPluginDataDir('dobo')}/attachment`
551
+ model = pascalCase(model)
552
+ let pattern = `${root}/${model}/${id}/${field}/${file}`
553
+ if (uriEncoded) pattern = pattern.split('/').map(p => decodeURI(p)).join('/')
554
+ return map(await fastGlob(pattern), f => {
555
+ const mimeType = mime.getType(path.extname(f)) ?? ''
556
+ const fullPath = f.replace(root, '')
557
+ const row = {
558
+ file: f,
559
+ fileName: path.basename(fullPath),
560
+ fullPath,
561
+ mimeType,
562
+ params: { model, id, field, file }
563
+ }
564
+ if (this.app.waibuMpa) {
565
+ const { routePath } = this.app.waibu
566
+ const [, _model, _id, _field, _file] = fullPath.split('/')
567
+ row.url = routePath(`dobo:/attachment/${kebabCase(_model)}/${_id}/${_field}/${_file}`)
568
+ }
569
+ return row
570
+ })
571
+ }
572
+ }
573
+
574
+ return Dobo
575
+ }
576
+
577
+ export default factory
578
+ </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Dobo API</a><div class="mobile-nav-links"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/dobo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/dobo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://dobo.bajo.app/" target="">Dobo</a></div><div class="navbar-item"><a id="" href="https://bajo.app/" target="">Bajo</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="Dobo.html">Dobo</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TPropType">TPropType</a></div><div class="sidebar-section-children"><a href="global.html#TRecordCountOptions">TRecordCountOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordCreateOptions">TRecordCreateOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFilter">TRecordFilter</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFindOptions">TRecordFindOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordFindResult">TRecordFindResult</a></div><div class="sidebar-section-children"><a href="global.html#TRecordGetOptions">TRecordGetOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordGetResult">TRecordGetResult</a></div><div class="sidebar-section-children"><a href="global.html#TRecordPagination">TRecordPagination</a></div><div class="sidebar-section-children"><a href="global.html#TRecordRemoveOptions">TRecordRemoveOptions</a></div><div class="sidebar-section-children"><a href="global.html#TRecordSort">TRecordSort</a></div><div class="sidebar-section-children"><a href="global.html#TRecordSortKey">TRecordSortKey</a></div><div class="sidebar-section-children"><a href="global.html#TRecordUpdateOptions">TRecordUpdateOptions</a></div><div class="sidebar-section-children"><a href="global.html#TValidator">TValidator</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorBoolean">TValidatorBoolean</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorDate">TValidatorDate</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorNumber">TValidatorNumber</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorString">TValidatorString</a></div><div class="sidebar-section-children"><a href="global.html#TValidatorTimestamp">TValidatorTimestamp</a></div><div class="sidebar-section-children"><a href="global.html#factory">factory</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>