@vandenberghinc/volt 1.2.6 → 1.2.7

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 (462) hide show
  1. package/backend/dist/cjs/backend/src/blacklist.d.ts +12 -0
  2. package/backend/dist/cjs/backend/src/blacklist.js +78 -0
  3. package/backend/dist/cjs/backend/src/cli.d.ts +2 -0
  4. package/backend/dist/cjs/backend/src/cli.js +198 -0
  5. package/backend/dist/cjs/backend/src/database/collection.d.ts +1765 -0
  6. package/backend/dist/cjs/backend/src/database/collection.js +3301 -0
  7. package/backend/dist/cjs/backend/src/database/database.d.ts +92 -0
  8. package/backend/dist/cjs/backend/src/database/database.js +170 -0
  9. package/backend/dist/cjs/backend/src/database/document.d.ts +1 -0
  10. package/backend/dist/cjs/backend/src/database/document.js +15 -0
  11. package/backend/dist/cjs/backend/src/database/filters/filters.d.ts +6 -0
  12. package/backend/dist/cjs/backend/src/database/filters/filters.js +15 -0
  13. package/backend/dist/cjs/backend/src/database/filters/strict_filter.d.ts +223 -0
  14. package/backend/dist/cjs/backend/src/database/filters/strict_filter.js +15 -0
  15. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test.d.ts +1 -0
  16. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test.js +443 -0
  17. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test_v0.d.ts +1 -0
  18. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test_v0.js +15 -0
  19. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v0.d.ts +50 -0
  20. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v0.js +15 -0
  21. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v1.d.ts +76 -0
  22. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v1.js +15 -0
  23. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v2.d.ts +75 -0
  24. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v2.js +15 -0
  25. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v3.d.ts +219 -0
  26. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v3.js +15 -0
  27. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  28. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter.js +15 -0
  29. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter_test.d.ts +5 -0
  30. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter_test.js +355 -0
  31. package/backend/dist/cjs/backend/src/database/flatten.d.ts +78 -0
  32. package/backend/dist/cjs/backend/src/database/flatten.js +53 -0
  33. package/backend/dist/cjs/backend/src/database/flatten_test.d.ts +1 -0
  34. package/backend/dist/cjs/backend/src/database/flatten_test.js +175 -0
  35. package/backend/dist/cjs/backend/src/database/quota/quoata_v2.d.ts +533 -0
  36. package/backend/dist/cjs/backend/src/database/quota/quoata_v2.js +1046 -0
  37. package/backend/dist/cjs/backend/src/database/quota/quota.d.ts +551 -0
  38. package/backend/dist/cjs/backend/src/database/quota/quota.js +1108 -0
  39. package/backend/dist/cjs/backend/src/database/quota/quota_v1.d.ts +534 -0
  40. package/backend/dist/cjs/backend/src/database/quota/quota_v1.js +1087 -0
  41. package/backend/dist/cjs/backend/src/database/quota/safe_int.d.ts +412 -0
  42. package/backend/dist/cjs/backend/src/database/quota/safe_int.js +745 -0
  43. package/backend/dist/cjs/backend/src/endpoint.d.ts +346 -0
  44. package/backend/dist/cjs/backend/src/endpoint.js +468 -0
  45. package/backend/dist/cjs/backend/src/errors/index.d.ts +7 -0
  46. package/backend/dist/cjs/backend/src/errors/index.js +25 -0
  47. package/backend/dist/cjs/backend/src/errors/internal_external.d.ts +52 -0
  48. package/backend/dist/cjs/backend/src/errors/internal_external.js +95 -0
  49. package/backend/dist/cjs/backend/src/errors/invalid_usage_error.d.ts +41 -0
  50. package/backend/dist/cjs/backend/src/errors/invalid_usage_error.js +47 -0
  51. package/backend/dist/cjs/backend/src/errors/system_error.d.ts +261 -0
  52. package/backend/dist/cjs/backend/src/errors/system_error.js +436 -0
  53. package/backend/dist/cjs/backend/src/events.d.ts +97 -0
  54. package/backend/dist/cjs/backend/src/events.js +15 -0
  55. package/backend/dist/cjs/backend/src/frontend.d.ts +13 -0
  56. package/backend/dist/cjs/backend/src/frontend.js +56 -0
  57. package/backend/dist/cjs/backend/src/image_endpoint.d.ts +44 -0
  58. package/backend/dist/cjs/backend/src/image_endpoint.js +185 -0
  59. package/backend/dist/cjs/backend/src/index.d.ts +23 -0
  60. package/backend/dist/cjs/backend/src/index.js +70 -0
  61. package/backend/dist/cjs/backend/src/logger.d.ts +5 -0
  62. package/backend/dist/cjs/backend/src/logger.js +15 -0
  63. package/backend/dist/cjs/backend/src/meta.d.ts +112 -0
  64. package/backend/dist/cjs/backend/src/meta.js +181 -0
  65. package/backend/dist/cjs/backend/src/payments/paddle.d.ts +329 -0
  66. package/backend/dist/cjs/backend/src/payments/paddle.js +1996 -0
  67. package/backend/dist/cjs/backend/src/payments/stripe/checkout.d.ts +113 -0
  68. package/backend/dist/cjs/backend/src/payments/stripe/checkout.js +295 -0
  69. package/backend/dist/cjs/backend/src/payments/stripe/customers.d.ts +17 -0
  70. package/backend/dist/cjs/backend/src/payments/stripe/customers.js +164 -0
  71. package/backend/dist/cjs/backend/src/payments/stripe/error.d.ts +74 -0
  72. package/backend/dist/cjs/backend/src/payments/stripe/error.js +64 -0
  73. package/backend/dist/cjs/backend/src/payments/stripe/events.d.ts +155 -0
  74. package/backend/dist/cjs/backend/src/payments/stripe/events.js +15 -0
  75. package/backend/dist/cjs/backend/src/payments/stripe/meters.d.ts +105 -0
  76. package/backend/dist/cjs/backend/src/payments/stripe/meters.js +230 -0
  77. package/backend/dist/cjs/backend/src/payments/stripe/payment_methods.d.ts +58 -0
  78. package/backend/dist/cjs/backend/src/payments/stripe/payment_methods.js +109 -0
  79. package/backend/dist/cjs/backend/src/payments/stripe/products.d.ts +519 -0
  80. package/backend/dist/cjs/backend/src/payments/stripe/products.js +650 -0
  81. package/backend/dist/cjs/backend/src/payments/stripe/stripe.d.ts +215 -0
  82. package/backend/dist/cjs/backend/src/payments/stripe/stripe.js +468 -0
  83. package/backend/dist/cjs/backend/src/payments/stripe/subscriptions.d.ts +172 -0
  84. package/backend/dist/cjs/backend/src/payments/stripe/subscriptions.js +557 -0
  85. package/backend/dist/cjs/backend/src/payments/stripe/utils.d.ts +63 -0
  86. package/backend/dist/cjs/backend/src/payments/stripe/utils.js +118 -0
  87. package/backend/dist/cjs/backend/src/payments/stripe/webhooks.d.ts +105 -0
  88. package/backend/dist/cjs/backend/src/payments/stripe/webhooks.js +627 -0
  89. package/backend/dist/cjs/backend/src/plugins/browser.d.ts +1 -0
  90. package/backend/dist/cjs/backend/src/plugins/browser.js +15 -0
  91. package/backend/dist/cjs/backend/src/plugins/communication.d.ts +70 -0
  92. package/backend/dist/cjs/backend/src/plugins/communication.js +196 -0
  93. package/backend/dist/cjs/backend/src/plugins/mail/mail.d.ts +255 -0
  94. package/backend/dist/cjs/backend/src/plugins/mail/mail.js +381 -0
  95. package/backend/dist/cjs/backend/src/plugins/mail/ui.d.ts +297 -0
  96. package/backend/dist/cjs/backend/src/plugins/mail/ui.js +1370 -0
  97. package/backend/dist/cjs/backend/src/plugins/pdf.d.ts +1 -0
  98. package/backend/dist/cjs/backend/src/plugins/pdf.js +1456 -0
  99. package/backend/dist/cjs/backend/src/plugins/thread_monitor.d.ts +18 -0
  100. package/backend/dist/cjs/backend/src/plugins/thread_monitor.js +116 -0
  101. package/backend/dist/cjs/backend/src/rate_limit.d.ts +148 -0
  102. package/backend/dist/cjs/backend/src/rate_limit.js +543 -0
  103. package/backend/dist/cjs/backend/src/route.d.ts +39 -0
  104. package/backend/dist/cjs/backend/src/route.js +172 -0
  105. package/backend/dist/cjs/backend/src/server.d.ts +502 -0
  106. package/backend/dist/cjs/backend/src/server.js +1710 -0
  107. package/backend/dist/cjs/backend/src/server.old.d.ts +594 -0
  108. package/backend/dist/cjs/backend/src/server.old.js +2058 -0
  109. package/backend/dist/cjs/backend/src/splash_screen.d.ts +93 -0
  110. package/backend/dist/cjs/backend/src/splash_screen.js +119 -0
  111. package/backend/dist/cjs/backend/src/status.d.ts +89 -0
  112. package/backend/dist/cjs/backend/src/status.js +211 -0
  113. package/backend/dist/cjs/backend/src/stream.d.ts +494 -0
  114. package/backend/dist/cjs/backend/src/stream.js +1370 -0
  115. package/backend/dist/cjs/backend/src/users.d.ts +926 -0
  116. package/backend/dist/cjs/backend/src/users.js +2223 -0
  117. package/backend/dist/cjs/backend/src/utils.d.ts +22 -0
  118. package/backend/dist/cjs/backend/src/utils.js +626 -0
  119. package/backend/dist/cjs/backend/src/view.d.ts +115 -0
  120. package/backend/dist/cjs/backend/src/view.js +519 -0
  121. package/backend/dist/cjs/backend/src/vinc.d.ts +6 -0
  122. package/backend/dist/cjs/backend/src/vinc.js +40 -0
  123. package/backend/dist/cjs/backend/src/volt.d.ts +24 -0
  124. package/backend/dist/cjs/backend/src/volt.js +72 -0
  125. package/backend/dist/cjs/frontend/src/modules/request.d.ts +70 -0
  126. package/backend/dist/cjs/frontend/src/modules/request.js +99 -0
  127. package/backend/dist/cjs/package.json +1 -0
  128. package/backend/dist/esm/backend/src/blacklist.d.ts +12 -0
  129. package/backend/dist/esm/backend/src/blacklist.js +52 -0
  130. package/backend/dist/esm/backend/src/cli.d.ts +2 -0
  131. package/backend/dist/esm/backend/src/cli.js +211 -0
  132. package/backend/dist/esm/backend/src/database/collection.d.ts +1765 -0
  133. package/backend/dist/esm/backend/src/database/collection.js +3779 -0
  134. package/backend/dist/esm/backend/src/database/database.d.ts +92 -0
  135. package/backend/dist/esm/backend/src/database/database.js +214 -0
  136. package/backend/dist/esm/backend/src/database/document.d.ts +1 -0
  137. package/backend/dist/esm/backend/src/database/document.js +558 -0
  138. package/backend/dist/esm/backend/src/database/filters/filters.d.ts +6 -0
  139. package/backend/dist/esm/backend/src/database/filters/filters.js +1 -0
  140. package/backend/dist/esm/backend/src/database/filters/strict_filter.d.ts +223 -0
  141. package/backend/dist/esm/backend/src/database/filters/strict_filter.js +3 -0
  142. package/backend/dist/esm/backend/src/database/filters/strict_filter_test.d.ts +1 -0
  143. package/backend/dist/esm/backend/src/database/filters/strict_filter_test.js +505 -0
  144. package/backend/dist/esm/backend/src/database/filters/strict_filter_test_v0.d.ts +1 -0
  145. package/backend/dist/esm/backend/src/database/filters/strict_filter_test_v0.js +712 -0
  146. package/backend/dist/esm/backend/src/database/filters/strict_filter_v0.d.ts +50 -0
  147. package/backend/dist/esm/backend/src/database/filters/strict_filter_v0.js +5 -0
  148. package/backend/dist/esm/backend/src/database/filters/strict_filter_v1.d.ts +76 -0
  149. package/backend/dist/esm/backend/src/database/filters/strict_filter_v1.js +44 -0
  150. package/backend/dist/esm/backend/src/database/filters/strict_filter_v2.d.ts +75 -0
  151. package/backend/dist/esm/backend/src/database/filters/strict_filter_v2.js +5 -0
  152. package/backend/dist/esm/backend/src/database/filters/strict_filter_v3.d.ts +219 -0
  153. package/backend/dist/esm/backend/src/database/filters/strict_filter_v3.js +1 -0
  154. package/backend/dist/esm/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  155. package/backend/dist/esm/backend/src/database/filters/strict_update_filter.js +5 -0
  156. package/backend/dist/esm/backend/src/database/filters/strict_update_filter_test.d.ts +5 -0
  157. package/backend/dist/esm/backend/src/database/filters/strict_update_filter_test.js +415 -0
  158. package/backend/dist/esm/backend/src/database/flatten.d.ts +78 -0
  159. package/backend/dist/esm/backend/src/database/flatten.js +22 -0
  160. package/backend/dist/esm/backend/src/database/flatten_test.d.ts +1 -0
  161. package/backend/dist/esm/backend/src/database/flatten_test.js +174 -0
  162. package/backend/dist/esm/backend/src/database/quota/quoata_v2.d.ts +533 -0
  163. package/backend/dist/esm/backend/src/database/quota/quoata_v2.js +1155 -0
  164. package/backend/dist/esm/backend/src/database/quota/quota.d.ts +551 -0
  165. package/backend/dist/esm/backend/src/database/quota/quota.js +1219 -0
  166. package/backend/dist/esm/backend/src/database/quota/quota_v1.d.ts +534 -0
  167. package/backend/dist/esm/backend/src/database/quota/quota_v1.js +1242 -0
  168. package/backend/dist/esm/backend/src/database/quota/safe_int.d.ts +412 -0
  169. package/backend/dist/esm/backend/src/database/quota/safe_int.js +810 -0
  170. package/backend/dist/esm/backend/src/endpoint.d.ts +346 -0
  171. package/backend/dist/esm/backend/src/endpoint.js +479 -0
  172. package/backend/dist/esm/backend/src/errors/index.d.ts +7 -0
  173. package/backend/dist/esm/backend/src/errors/index.js +7 -0
  174. package/backend/dist/esm/backend/src/errors/internal_external.d.ts +52 -0
  175. package/backend/dist/esm/backend/src/errors/internal_external.js +86 -0
  176. package/backend/dist/esm/backend/src/errors/invalid_usage_error.d.ts +41 -0
  177. package/backend/dist/esm/backend/src/errors/invalid_usage_error.js +33 -0
  178. package/backend/dist/esm/backend/src/errors/system_error.d.ts +261 -0
  179. package/backend/dist/esm/backend/src/errors/system_error.js +444 -0
  180. package/backend/dist/esm/backend/src/events.d.ts +97 -0
  181. package/backend/dist/esm/backend/src/events.js +5 -0
  182. package/backend/dist/esm/backend/src/frontend.d.ts +13 -0
  183. package/backend/dist/esm/backend/src/frontend.js +23 -0
  184. package/backend/dist/esm/backend/src/image_endpoint.d.ts +44 -0
  185. package/backend/dist/esm/backend/src/image_endpoint.js +196 -0
  186. package/backend/dist/esm/backend/src/index.d.ts +23 -0
  187. package/backend/dist/esm/backend/src/index.js +26 -0
  188. package/backend/dist/esm/backend/src/logger.d.ts +5 -0
  189. package/backend/dist/esm/backend/src/logger.js +8 -0
  190. package/backend/dist/esm/backend/src/meta.d.ts +112 -0
  191. package/backend/dist/esm/backend/src/meta.js +152 -0
  192. package/backend/dist/esm/backend/src/payments/paddle.d.ts +329 -0
  193. package/backend/dist/esm/backend/src/payments/paddle.js +2276 -0
  194. package/backend/dist/esm/backend/src/payments/stripe/checkout.d.ts +113 -0
  195. package/backend/dist/esm/backend/src/payments/stripe/checkout.js +356 -0
  196. package/backend/dist/esm/backend/src/payments/stripe/customers.d.ts +17 -0
  197. package/backend/dist/esm/backend/src/payments/stripe/customers.js +193 -0
  198. package/backend/dist/esm/backend/src/payments/stripe/error.d.ts +74 -0
  199. package/backend/dist/esm/backend/src/payments/stripe/error.js +51 -0
  200. package/backend/dist/esm/backend/src/payments/stripe/events.d.ts +155 -0
  201. package/backend/dist/esm/backend/src/payments/stripe/events.js +5 -0
  202. package/backend/dist/esm/backend/src/payments/stripe/meters.d.ts +105 -0
  203. package/backend/dist/esm/backend/src/payments/stripe/meters.js +318 -0
  204. package/backend/dist/esm/backend/src/payments/stripe/payment_methods.d.ts +58 -0
  205. package/backend/dist/esm/backend/src/payments/stripe/payment_methods.js +135 -0
  206. package/backend/dist/esm/backend/src/payments/stripe/products.d.ts +519 -0
  207. package/backend/dist/esm/backend/src/payments/stripe/products.js +896 -0
  208. package/backend/dist/esm/backend/src/payments/stripe/stripe.d.ts +215 -0
  209. package/backend/dist/esm/backend/src/payments/stripe/stripe.js +464 -0
  210. package/backend/dist/esm/backend/src/payments/stripe/subscriptions.d.ts +172 -0
  211. package/backend/dist/esm/backend/src/payments/stripe/subscriptions.js +754 -0
  212. package/backend/dist/esm/backend/src/payments/stripe/utils.d.ts +63 -0
  213. package/backend/dist/esm/backend/src/payments/stripe/utils.js +131 -0
  214. package/backend/dist/esm/backend/src/payments/stripe/webhooks.d.ts +105 -0
  215. package/backend/dist/esm/backend/src/payments/stripe/webhooks.js +752 -0
  216. package/backend/dist/esm/backend/src/plugins/browser.d.ts +1 -0
  217. package/backend/dist/esm/backend/src/plugins/browser.js +170 -0
  218. package/backend/dist/esm/backend/src/plugins/communication.d.ts +70 -0
  219. package/backend/dist/esm/backend/src/plugins/communication.js +169 -0
  220. package/backend/dist/esm/backend/src/plugins/mail/mail.d.ts +255 -0
  221. package/backend/dist/esm/backend/src/plugins/mail/mail.js +396 -0
  222. package/backend/dist/esm/backend/src/plugins/mail/ui.d.ts +297 -0
  223. package/backend/dist/esm/backend/src/plugins/mail/ui.js +1400 -0
  224. package/backend/dist/esm/backend/src/plugins/pdf.d.ts +1 -0
  225. package/backend/dist/esm/backend/src/plugins/pdf.js +1694 -0
  226. package/backend/dist/esm/backend/src/plugins/thread_monitor.d.ts +18 -0
  227. package/backend/dist/esm/backend/src/plugins/thread_monitor.js +120 -0
  228. package/backend/dist/esm/backend/src/rate_limit.d.ts +148 -0
  229. package/backend/dist/esm/backend/src/rate_limit.js +667 -0
  230. package/backend/dist/esm/backend/src/route.d.ts +39 -0
  231. package/backend/dist/esm/backend/src/route.js +222 -0
  232. package/backend/dist/esm/backend/src/server.d.ts +502 -0
  233. package/backend/dist/esm/backend/src/server.js +2031 -0
  234. package/backend/dist/esm/backend/src/server.old.d.ts +594 -0
  235. package/backend/dist/esm/backend/src/server.old.js +2630 -0
  236. package/backend/dist/esm/backend/src/splash_screen.d.ts +93 -0
  237. package/backend/dist/esm/backend/src/splash_screen.js +156 -0
  238. package/backend/dist/esm/backend/src/status.d.ts +89 -0
  239. package/backend/dist/esm/backend/src/status.js +213 -0
  240. package/backend/dist/esm/backend/src/stream.d.ts +494 -0
  241. package/backend/dist/esm/backend/src/stream.js +1611 -0
  242. package/backend/dist/esm/backend/src/users.d.ts +926 -0
  243. package/backend/dist/esm/backend/src/users.js +2423 -0
  244. package/backend/dist/esm/backend/src/utils.d.ts +22 -0
  245. package/backend/dist/esm/backend/src/utils.js +463 -0
  246. package/backend/dist/esm/backend/src/view.d.ts +115 -0
  247. package/backend/dist/esm/backend/src/view.js +584 -0
  248. package/backend/dist/esm/backend/src/vinc.d.ts +6 -0
  249. package/backend/dist/esm/backend/src/vinc.js +6 -0
  250. package/backend/dist/esm/backend/src/volt.d.ts +24 -0
  251. package/backend/dist/esm/backend/src/volt.js +27 -0
  252. package/backend/dist/esm/frontend/src/modules/request.d.ts +70 -0
  253. package/backend/dist/esm/frontend/src/modules/request.js +117 -0
  254. package/frontend/dist/backend/src/database/collection.d.ts +1765 -0
  255. package/frontend/dist/backend/src/database/collection.js +3779 -0
  256. package/frontend/dist/backend/src/database/database.d.ts +92 -0
  257. package/frontend/dist/backend/src/database/database.js +214 -0
  258. package/frontend/dist/backend/src/database/filters/filters.d.ts +6 -0
  259. package/frontend/dist/backend/src/database/filters/filters.js +1 -0
  260. package/frontend/dist/backend/src/database/filters/strict_filter.d.ts +223 -0
  261. package/frontend/dist/backend/src/database/filters/strict_filter.js +3 -0
  262. package/frontend/dist/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  263. package/frontend/dist/backend/src/database/filters/strict_update_filter.js +5 -0
  264. package/frontend/dist/backend/src/database/flatten.d.ts +78 -0
  265. package/frontend/dist/backend/src/database/flatten.js +22 -0
  266. package/frontend/dist/backend/src/endpoint.d.ts +346 -0
  267. package/frontend/dist/backend/src/endpoint.js +479 -0
  268. package/frontend/dist/backend/src/errors/index.d.ts +7 -0
  269. package/frontend/dist/backend/src/errors/index.js +7 -0
  270. package/frontend/dist/backend/src/errors/internal_external.d.ts +52 -0
  271. package/frontend/dist/backend/src/errors/internal_external.js +86 -0
  272. package/frontend/dist/backend/src/errors/invalid_usage_error.d.ts +41 -0
  273. package/frontend/dist/backend/src/errors/invalid_usage_error.js +33 -0
  274. package/frontend/dist/backend/src/errors/system_error.d.ts +261 -0
  275. package/frontend/dist/backend/src/errors/system_error.js +444 -0
  276. package/frontend/dist/backend/src/events.d.ts +97 -0
  277. package/frontend/dist/backend/src/events.js +5 -0
  278. package/frontend/dist/backend/src/frontend.d.ts +13 -0
  279. package/frontend/dist/backend/src/frontend.js +23 -0
  280. package/frontend/dist/backend/src/image_endpoint.d.ts +44 -0
  281. package/frontend/dist/backend/src/image_endpoint.js +196 -0
  282. package/frontend/dist/backend/src/meta.d.ts +112 -0
  283. package/frontend/dist/backend/src/meta.js +152 -0
  284. package/frontend/dist/backend/src/payments/paddle.d.ts +329 -0
  285. package/frontend/dist/backend/src/payments/paddle.js +2276 -0
  286. package/frontend/dist/backend/src/payments/stripe/checkout.d.ts +113 -0
  287. package/frontend/dist/backend/src/payments/stripe/checkout.js +356 -0
  288. package/frontend/dist/backend/src/payments/stripe/customers.d.ts +17 -0
  289. package/frontend/dist/backend/src/payments/stripe/customers.js +193 -0
  290. package/frontend/dist/backend/src/payments/stripe/error.d.ts +74 -0
  291. package/frontend/dist/backend/src/payments/stripe/error.js +51 -0
  292. package/frontend/dist/backend/src/payments/stripe/events.d.ts +155 -0
  293. package/frontend/dist/backend/src/payments/stripe/events.js +5 -0
  294. package/frontend/dist/backend/src/payments/stripe/meters.d.ts +105 -0
  295. package/frontend/dist/backend/src/payments/stripe/meters.js +318 -0
  296. package/frontend/dist/backend/src/payments/stripe/payment_methods.d.ts +58 -0
  297. package/frontend/dist/backend/src/payments/stripe/payment_methods.js +135 -0
  298. package/frontend/dist/backend/src/payments/stripe/products.d.ts +519 -0
  299. package/frontend/dist/backend/src/payments/stripe/products.js +896 -0
  300. package/frontend/dist/backend/src/payments/stripe/stripe.d.ts +215 -0
  301. package/frontend/dist/backend/src/payments/stripe/stripe.js +464 -0
  302. package/frontend/dist/backend/src/payments/stripe/subscriptions.d.ts +172 -0
  303. package/frontend/dist/backend/src/payments/stripe/subscriptions.js +754 -0
  304. package/frontend/dist/backend/src/payments/stripe/utils.d.ts +63 -0
  305. package/frontend/dist/backend/src/payments/stripe/utils.js +131 -0
  306. package/frontend/dist/backend/src/payments/stripe/webhooks.d.ts +105 -0
  307. package/frontend/dist/backend/src/payments/stripe/webhooks.js +752 -0
  308. package/frontend/dist/backend/src/plugins/mail/mail.d.ts +255 -0
  309. package/frontend/dist/backend/src/plugins/mail/mail.js +396 -0
  310. package/frontend/dist/backend/src/plugins/mail/ui.d.ts +297 -0
  311. package/frontend/dist/backend/src/plugins/mail/ui.js +1400 -0
  312. package/frontend/dist/backend/src/rate_limit.d.ts +148 -0
  313. package/frontend/dist/backend/src/rate_limit.js +667 -0
  314. package/frontend/dist/backend/src/route.d.ts +39 -0
  315. package/frontend/dist/backend/src/route.js +222 -0
  316. package/frontend/dist/backend/src/server.d.ts +502 -0
  317. package/frontend/dist/backend/src/server.js +2031 -0
  318. package/frontend/dist/backend/src/splash_screen.d.ts +93 -0
  319. package/frontend/dist/backend/src/splash_screen.js +156 -0
  320. package/frontend/dist/backend/src/status.d.ts +89 -0
  321. package/frontend/dist/backend/src/status.js +213 -0
  322. package/frontend/dist/backend/src/stream.d.ts +494 -0
  323. package/frontend/dist/backend/src/stream.js +1611 -0
  324. package/frontend/dist/backend/src/users.d.ts +926 -0
  325. package/frontend/dist/backend/src/users.js +2423 -0
  326. package/frontend/dist/backend/src/utils.d.ts +22 -0
  327. package/frontend/dist/backend/src/utils.js +463 -0
  328. package/frontend/dist/backend/src/view.d.ts +115 -0
  329. package/frontend/dist/backend/src/view.js +584 -0
  330. package/frontend/dist/frontend/src/css/adyen.css +92 -0
  331. package/frontend/dist/frontend/src/css/volt.css +75 -0
  332. package/frontend/dist/frontend/src/elements/base.d.ts +3743 -0
  333. package/frontend/dist/frontend/src/elements/base.js +12151 -0
  334. package/frontend/dist/frontend/src/elements/module.d.ts +95 -0
  335. package/frontend/dist/frontend/src/elements/module.js +216 -0
  336. package/frontend/dist/frontend/src/elements/register_element.d.ts +3 -0
  337. package/frontend/dist/frontend/src/elements/register_element.js +22 -0
  338. package/frontend/dist/frontend/src/elements/resize_query_manager.d.ts +0 -0
  339. package/frontend/dist/frontend/src/elements/resize_query_manager.js +150 -0
  340. package/frontend/dist/frontend/src/elements/types.d.ts +52 -0
  341. package/frontend/dist/frontend/src/elements/types.js +5 -0
  342. package/frontend/dist/frontend/src/index.d.ts +21 -0
  343. package/frontend/dist/frontend/src/index.js +29 -0
  344. package/frontend/dist/frontend/src/modules/attachment.d.ts +126 -0
  345. package/frontend/dist/frontend/src/modules/attachment.js +306 -0
  346. package/frontend/dist/frontend/src/modules/auth.d.ts +44 -0
  347. package/frontend/dist/frontend/src/modules/auth.js +80 -0
  348. package/frontend/dist/frontend/src/modules/color.d.ts +160 -0
  349. package/frontend/dist/frontend/src/modules/color.js +316 -0
  350. package/frontend/dist/frontend/src/modules/compression.d.ts +39 -0
  351. package/frontend/dist/frontend/src/modules/compression.js +102 -0
  352. package/frontend/dist/frontend/src/modules/cookies.d.ts +44 -0
  353. package/frontend/dist/frontend/src/modules/cookies.js +143 -0
  354. package/frontend/dist/frontend/src/modules/events.d.ts +31 -0
  355. package/frontend/dist/frontend/src/modules/events.js +79 -0
  356. package/frontend/dist/frontend/src/modules/google.d.ts +23 -0
  357. package/frontend/dist/frontend/src/modules/google.js +52 -0
  358. package/frontend/dist/frontend/src/modules/meta.d.ts +14 -0
  359. package/frontend/dist/frontend/src/modules/meta.js +48 -0
  360. package/frontend/dist/frontend/src/modules/paddle.d.ts +1207 -0
  361. package/frontend/dist/frontend/src/modules/paddle.js +2594 -0
  362. package/frontend/dist/frontend/src/modules/request.d.ts +70 -0
  363. package/frontend/dist/frontend/src/modules/request.js +117 -0
  364. package/frontend/dist/frontend/src/modules/settings.d.ts +3 -0
  365. package/frontend/dist/frontend/src/modules/settings.js +5 -0
  366. package/frontend/dist/frontend/src/modules/statics.d.ts +21 -0
  367. package/frontend/dist/frontend/src/modules/statics.js +43 -0
  368. package/frontend/dist/frontend/src/modules/stripe/cart.d.ts +112 -0
  369. package/frontend/dist/frontend/src/modules/stripe/cart.js +321 -0
  370. package/frontend/dist/frontend/src/modules/stripe/checkout.d.ts +7 -0
  371. package/frontend/dist/frontend/src/modules/stripe/checkout.js +37 -0
  372. package/frontend/dist/frontend/src/modules/stripe/index.m.d.ts +6 -0
  373. package/frontend/dist/frontend/src/modules/stripe/index.m.js +6 -0
  374. package/frontend/dist/frontend/src/modules/stripe/payments.d.ts +58 -0
  375. package/frontend/dist/frontend/src/modules/stripe/payments.js +92 -0
  376. package/frontend/dist/frontend/src/modules/support.d.ts +30 -0
  377. package/frontend/dist/frontend/src/modules/support.js +53 -0
  378. package/frontend/dist/frontend/src/modules/theme.d.ts +133 -0
  379. package/frontend/dist/frontend/src/modules/theme.js +406 -0
  380. package/frontend/dist/frontend/src/modules/themes.d.ts +12 -0
  381. package/frontend/dist/frontend/src/modules/themes.js +22 -0
  382. package/frontend/dist/frontend/src/modules/user.d.ts +164 -0
  383. package/frontend/dist/frontend/src/modules/user.js +270 -0
  384. package/frontend/dist/frontend/src/modules/utils.d.ts +176 -0
  385. package/frontend/dist/frontend/src/modules/utils.js +569 -0
  386. package/frontend/dist/frontend/src/types/gradient.d.ts +29 -0
  387. package/frontend/dist/frontend/src/types/gradient.js +79 -0
  388. package/frontend/dist/frontend/src/ui/border_button.d.ts +94 -0
  389. package/frontend/dist/frontend/src/ui/border_button.js +228 -0
  390. package/frontend/dist/frontend/src/ui/button.d.ts +241 -0
  391. package/frontend/dist/frontend/src/ui/button.js +682 -0
  392. package/frontend/dist/frontend/src/ui/canvas.d.ts +138 -0
  393. package/frontend/dist/frontend/src/ui/canvas.js +444 -0
  394. package/frontend/dist/frontend/src/ui/checkbox.d.ts +74 -0
  395. package/frontend/dist/frontend/src/ui/checkbox.js +321 -0
  396. package/frontend/dist/frontend/src/ui/code.d.ts +235 -0
  397. package/frontend/dist/frontend/src/ui/code.js +1007 -0
  398. package/frontend/dist/frontend/src/ui/context_menu.d.ts +36 -0
  399. package/frontend/dist/frontend/src/ui/context_menu.js +205 -0
  400. package/frontend/dist/frontend/src/ui/css.d.ts +16 -0
  401. package/frontend/dist/frontend/src/ui/css.js +48 -0
  402. package/frontend/dist/frontend/src/ui/divider.d.ts +15 -0
  403. package/frontend/dist/frontend/src/ui/divider.js +78 -0
  404. package/frontend/dist/frontend/src/ui/dropdown.d.ts +176 -0
  405. package/frontend/dist/frontend/src/ui/dropdown.js +481 -0
  406. package/frontend/dist/frontend/src/ui/for_each.d.ts +37 -0
  407. package/frontend/dist/frontend/src/ui/for_each.js +92 -0
  408. package/frontend/dist/frontend/src/ui/form.d.ts +34 -0
  409. package/frontend/dist/frontend/src/ui/form.js +233 -0
  410. package/frontend/dist/frontend/src/ui/frame_modes.d.ts +37 -0
  411. package/frontend/dist/frontend/src/ui/frame_modes.js +108 -0
  412. package/frontend/dist/frontend/src/ui/google_map.d.ts +24 -0
  413. package/frontend/dist/frontend/src/ui/google_map.js +106 -0
  414. package/frontend/dist/frontend/src/ui/gradient.d.ts +25 -0
  415. package/frontend/dist/frontend/src/ui/gradient.js +131 -0
  416. package/frontend/dist/frontend/src/ui/image.d.ts +111 -0
  417. package/frontend/dist/frontend/src/ui/image.js +576 -0
  418. package/frontend/dist/frontend/src/ui/input.d.ts +392 -0
  419. package/frontend/dist/frontend/src/ui/input.js +1201 -0
  420. package/frontend/dist/frontend/src/ui/link.d.ts +25 -0
  421. package/frontend/dist/frontend/src/ui/link.js +140 -0
  422. package/frontend/dist/frontend/src/ui/list.d.ts +37 -0
  423. package/frontend/dist/frontend/src/ui/list.js +170 -0
  424. package/frontend/dist/frontend/src/ui/loader_button.d.ts +80 -0
  425. package/frontend/dist/frontend/src/ui/loader_button.js +193 -0
  426. package/frontend/dist/frontend/src/ui/loaders.d.ts +57 -0
  427. package/frontend/dist/frontend/src/ui/loaders.js +157 -0
  428. package/frontend/dist/frontend/src/ui/popup.d.ts +94 -0
  429. package/frontend/dist/frontend/src/ui/popup.js +510 -0
  430. package/frontend/dist/frontend/src/ui/pseudo.d.ts +44 -0
  431. package/frontend/dist/frontend/src/ui/pseudo.js +154 -0
  432. package/frontend/dist/frontend/src/ui/scroller.d.ts +105 -0
  433. package/frontend/dist/frontend/src/ui/scroller.js +1253 -0
  434. package/frontend/dist/frontend/src/ui/slider.d.ts +45 -0
  435. package/frontend/dist/frontend/src/ui/slider.js +217 -0
  436. package/frontend/dist/frontend/src/ui/spacer.d.ts +15 -0
  437. package/frontend/dist/frontend/src/ui/spacer.js +78 -0
  438. package/frontend/dist/frontend/src/ui/span.d.ts +15 -0
  439. package/frontend/dist/frontend/src/ui/span.js +73 -0
  440. package/frontend/dist/frontend/src/ui/stack.d.ts +66 -0
  441. package/frontend/dist/frontend/src/ui/stack.js +335 -0
  442. package/frontend/dist/frontend/src/ui/steps.d.ts +131 -0
  443. package/frontend/dist/frontend/src/ui/steps.js +308 -0
  444. package/frontend/dist/frontend/src/ui/style.d.ts +17 -0
  445. package/frontend/dist/frontend/src/ui/style.js +73 -0
  446. package/frontend/dist/frontend/src/ui/switch.d.ts +69 -0
  447. package/frontend/dist/frontend/src/ui/switch.js +357 -0
  448. package/frontend/dist/frontend/src/ui/table.d.ts +100 -0
  449. package/frontend/dist/frontend/src/ui/table.js +405 -0
  450. package/frontend/dist/frontend/src/ui/tabs.d.ts +111 -0
  451. package/frontend/dist/frontend/src/ui/tabs.js +424 -0
  452. package/frontend/dist/frontend/src/ui/text.d.ts +15 -0
  453. package/frontend/dist/frontend/src/ui/text.js +83 -0
  454. package/frontend/dist/frontend/src/ui/title.d.ts +91 -0
  455. package/frontend/dist/frontend/src/ui/title.js +272 -0
  456. package/frontend/dist/frontend/src/ui/ui.d.ts +35 -0
  457. package/frontend/dist/frontend/src/ui/ui.js +38 -0
  458. package/frontend/dist/frontend/src/ui/view.d.ts +15 -0
  459. package/frontend/dist/frontend/src/ui/view.js +88 -0
  460. package/frontend/dist/frontend/src/volt.d.ts +20 -0
  461. package/frontend/dist/frontend/src/volt.js +27 -0
  462. package/package.json +7 -2
@@ -0,0 +1,3301 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var stdin_exports = {};
29
+ __export(stdin_exports, {
30
+ Collection: () => Collection,
31
+ TransactionCollection: () => TransactionCollection
32
+ });
33
+ module.exports = __toCommonJS(stdin_exports);
34
+ var mongodb = __toESM(require("mongodb"));
35
+ var vlib = __toESM(require("@vandenberghinc/vlib"));
36
+ var import_flatten = require("./flatten.js");
37
+ var import_errors = require("../errors/index.js");
38
+ class Collection {
39
+ /** Collection name */
40
+ name;
41
+ /** The mongo collection. */
42
+ _col;
43
+ /**
44
+ * The Database parent class, used to initialize the collection on demand.
45
+ * So the user can define collections at root level before the database is initialized.
46
+ */
47
+ db;
48
+ /** Is initialized. */
49
+ initialized = false;
50
+ /** Whether this collection instance is transaction-based. */
51
+ is_transaction = false;
52
+ /** Whether this transaction has been finalized (committed or aborted). */
53
+ is_finalized_transaction = false;
54
+ /** Time to live in msec for all documents. */
55
+ ttl;
56
+ /** Is ttl behaviour enabled? */
57
+ ttl_enabled;
58
+ /** Enable sliding ttl (refreshes ttl on update), or static ttl (sets ttl on insert) */
59
+ sliding_ttl;
60
+ /**
61
+ * The temporary indexes passed to the constructor for the init method.
62
+ * @note This is not private so it can be updated by {@link QuotaManager}.
63
+ */
64
+ _init_indexes;
65
+ /** The MongoDB client session for transaction support. */
66
+ _session;
67
+ /**
68
+ * The record type version for the database.
69
+ * See {@link Collection.Opts.record_version} for more info.
70
+ *
71
+ * Ensure its always defined so we always set the version to `1`,
72
+ * in case the user decides later that it would need the transform version
73
+ * for older documents. Otherwise they would not have the old `1` version.
74
+ */
75
+ record_version;
76
+ /**
77
+ * The function to transform an older document version to the current version.
78
+ * See {@link Collection.Opts.on_transform_version} for more info.
79
+ */
80
+ on_transform_version;
81
+ /**
82
+ * Save fully transformed documents again to prevent unneeded future transformations.
83
+ * See {@link Collection.Opts.persist_transformed_on_load} for more info.
84
+ */
85
+ persist_transformed_on_load;
86
+ /**
87
+ * The function to call when a document is loaded (also when a default value is used).
88
+ * See {@link Collection.Opts.on_load} for more info.
89
+ */
90
+ on_load_cb;
91
+ /**
92
+ * Constructs a new Collection instance.
93
+ *
94
+ * @param opts The constructor options for the collection.
95
+ *
96
+ * @throws An error when attempting to initialize a transaction-based collection without initializing the derived collection first.
97
+ *
98
+ * @docs
99
+ */
100
+ constructor(opts) {
101
+ if (!opts.transaction_based) {
102
+ this.name = opts.name;
103
+ this._col = opts.col;
104
+ this.db = opts.db;
105
+ this._init_indexes = opts.indexes;
106
+ this.is_transaction = false;
107
+ let ttl_ms;
108
+ let ttl_sliding = true;
109
+ if (typeof opts.ttl === "number") {
110
+ ttl_ms = opts.ttl;
111
+ ttl_sliding = true;
112
+ } else if (opts.ttl && typeof opts.ttl === "object") {
113
+ ttl_ms = opts.ttl.milliseconds;
114
+ ttl_sliding = opts.ttl.sliding ?? true;
115
+ } else {
116
+ ttl_ms = void 0;
117
+ ttl_sliding = true;
118
+ }
119
+ this.ttl = ttl_ms;
120
+ this.ttl_enabled = this.ttl != null;
121
+ this.sliding_ttl = ttl_sliding;
122
+ if (opts.on_transform_version != null && opts.record_version == null) {
123
+ throw new import_errors.InvalidUsageError({
124
+ message: "Option 'on_transform_version' requires 'record_version' to be defined.",
125
+ reason: "missing_record_version"
126
+ });
127
+ }
128
+ if (opts.record_version != null && (!Number.isInteger(opts.record_version) || opts.record_version < 1)) {
129
+ throw new import_errors.InvalidUsageError({
130
+ message: "Option 'record_version' must be a positive integer.",
131
+ reason: "invalid_record_version"
132
+ });
133
+ }
134
+ const version = opts.record_version ?? 1;
135
+ if (version !== 1 && opts.on_transform_version == null) {
136
+ throw new import_errors.InvalidUsageError({
137
+ message: "Option 'on_transform_version' must be set when 'record_version' is not 1.",
138
+ reason: "missing_transform_version"
139
+ });
140
+ }
141
+ this.record_version = opts.record_version ?? 1;
142
+ this.on_transform_version = opts.on_transform_version;
143
+ this.on_load_cb = opts.on_load;
144
+ this.persist_transformed_on_load = opts.persist_transformed_on_load ?? true;
145
+ } else {
146
+ if (!opts.derived_collection.initialized) {
147
+ throw new import_errors.InvalidUsageError({
148
+ message: `Derived collection "${opts.derived_collection.name}" is not yet initialized, this is required in order to construct a transaction based collection.`,
149
+ reason: "collection_not_initialized"
150
+ });
151
+ }
152
+ this.name = opts.derived_collection.name;
153
+ this._col = opts.derived_collection._col;
154
+ this.ttl = opts.derived_collection.ttl;
155
+ this.sliding_ttl = opts.derived_collection.sliding_ttl;
156
+ this.ttl_enabled = opts.derived_collection.ttl_enabled;
157
+ this.db = opts.derived_collection.db;
158
+ this.is_transaction = true;
159
+ this.record_version = opts.derived_collection.record_version;
160
+ this.on_transform_version = opts.derived_collection.on_transform_version;
161
+ this.on_load_cb = opts.derived_collection.on_load_cb;
162
+ this.persist_transformed_on_load = opts.derived_collection.persist_transformed_on_load;
163
+ }
164
+ }
165
+ // -------------------------------------------------------------------
166
+ // Private methods.
167
+ // -------------------------------------------------------------------
168
+ /**
169
+ * Initialize a database query from path or object.
170
+ * @throws An error if the input type is incorrect, and optionally if the query is empty.
171
+ */
172
+ _init_query(query, allow_empty, param_name) {
173
+ if (!query || typeof query !== "object" || Array.isArray(query)) {
174
+ throw new import_errors.InvalidUsageError({
175
+ message: `Parameter "${param_name}" is not a valid query.`,
176
+ reason: "invalid_query",
177
+ field: param_name
178
+ });
179
+ }
180
+ if (!allow_empty && Object.keys(query).length === 0) {
181
+ throw new import_errors.InvalidUsageError({
182
+ message: `Parameter "${param_name}" is an empty object.`,
183
+ reason: "empty_query",
184
+ field: param_name
185
+ });
186
+ }
187
+ return query;
188
+ }
189
+ /**
190
+ * Setup the ttl configuration.
191
+ *
192
+ * @note When transaction mode is enabled, the session option will not be used.
193
+ */
194
+ async _setup_ttl() {
195
+ this.assert_not_transaction_based();
196
+ if (!this.initialized) {
197
+ await this.init();
198
+ }
199
+ this.assert_init();
200
+ if (!this.ttl_enabled || this.ttl == null) {
201
+ return;
202
+ }
203
+ const desired_seconds = Math.max(1, Math.ceil(this.ttl / 1e3));
204
+ const indexes = await this._col.indexes();
205
+ const ttl_index = indexes.find((ix) => ix && typeof ix.key === "object" && ix.key.__ttl_timestamp === 1);
206
+ if (!ttl_index) {
207
+ await this._col.createIndex({ __ttl_timestamp: 1 }, { expireAfterSeconds: desired_seconds });
208
+ return;
209
+ }
210
+ if (ttl_index.expireAfterSeconds !== desired_seconds) {
211
+ let coll_mod_succeeded = false;
212
+ try {
213
+ await this.db._db.command({
214
+ collMod: this.name,
215
+ index: {
216
+ name: ttl_index.name,
217
+ expireAfterSeconds: desired_seconds
218
+ }
219
+ });
220
+ coll_mod_succeeded = true;
221
+ } catch (error) {
222
+ }
223
+ if (!coll_mod_succeeded) {
224
+ try {
225
+ await this._col.dropIndex(ttl_index.name ?? "__ttl_timestamp_1");
226
+ } catch {
227
+ }
228
+ await this._col.createIndex({ __ttl_timestamp: 1 }, { expireAfterSeconds: desired_seconds });
229
+ }
230
+ }
231
+ }
232
+ /**
233
+ * Apply the ttl timestamp to a database operation (update doc or pipeline).
234
+ * Do not upsert if the user explicitly sets `upsert: false` in the operation.
235
+ */
236
+ _apply_ttl_to_operation(operation, upsert) {
237
+ if (!this.ttl_enabled)
238
+ return;
239
+ const now = /* @__PURE__ */ new Date();
240
+ if (Array.isArray(operation)) {
241
+ if (this.sliding_ttl) {
242
+ operation.push({ $set: { __ttl_timestamp: now } });
243
+ } else {
244
+ operation.push({ $set: { __ttl_timestamp: { $ifNull: ["$__ttl_timestamp", now] } } });
245
+ }
246
+ return;
247
+ }
248
+ const opKey = this.sliding_ttl ? "$set" : "$setOnInsert";
249
+ if (this.sliding_ttl || upsert !== false) {
250
+ const bucket = operation[opKey];
251
+ if (bucket == null) {
252
+ operation[opKey] = { __ttl_timestamp: now };
253
+ } else if (typeof bucket === "object") {
254
+ bucket.__ttl_timestamp = now;
255
+ } else {
256
+ throw new import_errors.InvalidUsageError({
257
+ message: `Invalid update operator object for TTL control at "${opKey}".`,
258
+ reason: "bad_ttl_operator"
259
+ });
260
+ }
261
+ }
262
+ }
263
+ /**
264
+ * Injects `__record_version` into an update **only on insert paths**.
265
+ *
266
+ * Rules:
267
+ * - **Pipeline updates** (`update: Document[]`): no-op here (MongoDB has no `$setOnInsert` in pipelines).
268
+ * If you rely on upsert+pipeline, set `__record_version` explicitly in your pipeline.
269
+ * - **Replacement doc** (no operators):
270
+ * - When `upsert === true`, set `__record_version` **only if missing**.
271
+ * - When `upsert !== true`, do nothing (don’t mask older stored versions).
272
+ * - **Operator doc**:
273
+ * - Respect any user-provided `__record_version` in `$set` or `$setOnInsert`.
274
+ * - When `upsert === true` and the user didn’t provide a value, set it via `$setOnInsert`.
275
+ *
276
+ * Rationale:
277
+ * This avoids bumping `__record_version` during normal updates (which would mask older versions)
278
+ * while still stamping newly inserted documents.
279
+ */
280
+ _apply_record_version_to_operation(operation, upsert) {
281
+ const current = this.record_version;
282
+ if (current == null)
283
+ return;
284
+ if (Array.isArray(operation))
285
+ return;
286
+ const op = operation;
287
+ const hasDollar = Object.keys(op).some((k) => k[0] === "$");
288
+ if (!hasDollar) {
289
+ if (!upsert)
290
+ return;
291
+ if (op.__record_version == null) {
292
+ op.__record_version = current;
293
+ }
294
+ return;
295
+ }
296
+ const userSet = op?.$set?.__record_version;
297
+ const userOnIns = op?.$setOnInsert?.__record_version;
298
+ if (userSet != null || userOnIns != null)
299
+ return;
300
+ if (upsert) {
301
+ op.$setOnInsert = { ...op.$setOnInsert ?? {}, __record_version: current };
302
+ }
303
+ }
304
+ /**
305
+ * Decide if an error is worth a bounded retry.
306
+ * Prefers label-based detection and adds well-known transient/network surfaces.
307
+ *
308
+ * @param unknown_err The thrown error.
309
+ * @returns True for retryable/transient errors; false otherwise.
310
+ */
311
+ _should_retry_error(unknown_err) {
312
+ if (typeof unknown_err !== "object" || !unknown_err || Array.isArray(unknown_err)) {
313
+ return false;
314
+ }
315
+ const err = unknown_err;
316
+ const name = err?.name;
317
+ const code_name = err?.codeName;
318
+ const has_label = (label) => {
319
+ if (typeof err?.hasErrorLabel === "function") {
320
+ try {
321
+ return !!err.hasErrorLabel(label);
322
+ } catch {
323
+ return false;
324
+ }
325
+ }
326
+ const labels = err?.errorLabels;
327
+ return Array.isArray(labels) && labels.includes(label);
328
+ };
329
+ const raw_code = err?.code;
330
+ const numeric_code = typeof raw_code === "number" ? raw_code : typeof raw_code === "string" && /^\d+$/.test(raw_code) ? Number(raw_code) : void 0;
331
+ const sys_code = typeof raw_code === "string" && isNaN(Number(raw_code)) ? raw_code : void 0;
332
+ const transient_sys = /* @__PURE__ */ new Set([
333
+ "ECONNRESET",
334
+ "ETIMEDOUT",
335
+ "EPIPE",
336
+ "ECONNREFUSED",
337
+ "ENETUNREACH",
338
+ "ENETDOWN",
339
+ "EHOSTUNREACH",
340
+ "EAI_AGAIN"
341
+ ]);
342
+ if (name === "AbortError")
343
+ return false;
344
+ if (has_label("TransientTransactionError") || has_label("UnknownTransactionCommitResult") || has_label("RetryableWriteError")) {
345
+ return true;
346
+ }
347
+ if (name === "MongoNetworkError" || name === "MongoNetworkTimeoutError" || name === "MongoServerSelectionError" || name === "MongoTopologyClosedError" || sys_code && transient_sys.has(sys_code)) {
348
+ return true;
349
+ }
350
+ switch (numeric_code) {
351
+ case 6:
352
+ return true;
353
+ case 7:
354
+ return true;
355
+ case 50:
356
+ return true;
357
+ case 89:
358
+ return true;
359
+ case 91:
360
+ return true;
361
+ case 112:
362
+ return true;
363
+ case 189:
364
+ return true;
365
+ case 262:
366
+ return true;
367
+ case 10107:
368
+ return true;
369
+ case 11600:
370
+ return true;
371
+ case 11602:
372
+ return true;
373
+ case 13435:
374
+ return true;
375
+ case 13436:
376
+ return true;
377
+ case 9001:
378
+ return true;
379
+ default:
380
+ break;
381
+ }
382
+ if (code_name === "PoolClearedError")
383
+ return true;
384
+ return false;
385
+ }
386
+ /**
387
+ * Execute an async function with bounded, exponential backoff retries for retryable errors.
388
+ *
389
+ * - attempts: 1 ⇒ no retry (single execution).
390
+ * - Uses small bounded jitter to smooth load (see Collection.Retry).
391
+ *
392
+ * @param fn The async operation to execute.
393
+ * @param retry Number of attempts (1 = no retries) or {@link Collection.Retry.Opts}.
394
+ * @returns The function result when successful.
395
+ * @throws The last error if not retryable or retries exhausted.
396
+ */
397
+ async _with_retry(fn, retry) {
398
+ const opts = Collection.Retry.normalize(retry);
399
+ if (opts.attempts <= 1) {
400
+ return await Promise.resolve().then(fn);
401
+ }
402
+ const last_index = opts.attempts - 1;
403
+ for (let i = 0; i < opts.attempts; i++) {
404
+ try {
405
+ return await Promise.resolve().then(fn);
406
+ } catch (err) {
407
+ if (!this._should_retry_error(err) || i >= last_index) {
408
+ throw err;
409
+ }
410
+ const delay = Collection.Retry.compute_backoff_delay(i, opts);
411
+ if (delay > 0) {
412
+ await new Promise((resolve) => setTimeout(resolve, delay));
413
+ }
414
+ }
415
+ }
416
+ throw new Error("Unexpected retry loop termination in _with_retry");
417
+ }
418
+ /**
419
+ * Ensure `__record_version` is properly included for projections so version
420
+ * transformation can determine the original version reliably.
421
+ *
422
+ * @param projection The user-specified projection (if any).
423
+ * @returns A projection with `__record_version` enforced where needed.
424
+ */
425
+ _ensure_version_in_projection(projection) {
426
+ if (!projection)
427
+ return projection;
428
+ if (Array.isArray(projection)) {
429
+ return projection.includes("__record_version") ? projection : [...projection, "__record_version"];
430
+ }
431
+ if (Object.values(projection).some((v) => v === 0 || v === false)) {
432
+ if (projection["__record_version"] != null) {
433
+ const clone = { ...projection };
434
+ delete clone["__record_version"];
435
+ return clone;
436
+ }
437
+ return projection;
438
+ }
439
+ if (projection["__record_version"] !== 1 && projection["__record_version"] !== true) {
440
+ return { ...projection, __record_version: 1 };
441
+ }
442
+ return projection;
443
+ }
444
+ /**
445
+ * Determine whether a projection should be considered partial.
446
+ * @param projection The user-specified projection (if any).
447
+ * @returns True when a non-empty projection was provided.
448
+ */
449
+ _is_partial_projection(projection) {
450
+ if (!projection)
451
+ return false;
452
+ if (Array.isArray(projection))
453
+ return projection.length > 0;
454
+ return Object.keys(projection).length > 0;
455
+ }
456
+ /**
457
+ * Check whether the given update is operator-style (or a pipeline).
458
+ * - Aggregation pipeline: Array → valid.
459
+ * - Operator update: at least one top-level key starts with '$' → valid.
460
+ * - Plain object without '$' keys → NOT valid for updateOne/findOneAndUpdate.
461
+ */
462
+ _is_operator_update_or_pipeline(operation) {
463
+ return Array.isArray(operation) || operation && typeof operation === "object" && Object.keys(operation).some((k) => k[0] === "$");
464
+ }
465
+ _index_key_signature(keys) {
466
+ return Object.entries(keys).map(([k, v]) => `${k}:${v}`).join("|");
467
+ }
468
+ _keys_equal(a, b) {
469
+ const aEnt = Object.entries(a);
470
+ const bEnt = Object.entries(b);
471
+ if (aEnt.length !== bEnt.length)
472
+ return false;
473
+ for (let i = 0; i < aEnt.length; i++) {
474
+ const [ak, av] = aEnt[i];
475
+ const [bk, bv] = bEnt[i];
476
+ if (ak !== bk || av !== bv)
477
+ return false;
478
+ }
479
+ return true;
480
+ }
481
+ _normalize_index_opts(opts) {
482
+ let key;
483
+ let keys;
484
+ let options;
485
+ let unique;
486
+ let sparse;
487
+ let forced = false;
488
+ if (typeof opts === "string") {
489
+ key = opts;
490
+ unique = void 0;
491
+ sparse = void 0;
492
+ } else {
493
+ ({ key, keys, forced = false } = opts);
494
+ const user_options = opts.options;
495
+ options = user_options ? { ...user_options } : void 0;
496
+ if (opts.unique != null && options?.unique != null && opts.unique !== options.unique) {
497
+ throw new import_errors.InvalidUsageError({
498
+ message: `Encountered different values for attribute 'unique': ${opts.unique} and 'options.unique': ${options.unique}.`,
499
+ reason: "invalid_unique_option"
500
+ });
501
+ }
502
+ unique = opts.unique ?? options?.unique;
503
+ if (opts.sparse != null && options?.sparse != null && opts.sparse !== options.sparse) {
504
+ throw new import_errors.InvalidUsageError({
505
+ message: `Encountered different values for attribute 'sparse': ${opts.sparse} and 'options.sparse': ${options.sparse}.`,
506
+ reason: "invalid_sparse_option"
507
+ });
508
+ }
509
+ sparse = opts.sparse ?? options?.sparse;
510
+ }
511
+ if (unique != null) {
512
+ options = options || {};
513
+ options.unique = unique;
514
+ }
515
+ if (sparse != null) {
516
+ options = options || {};
517
+ options.sparse = sparse;
518
+ }
519
+ let keys_obj;
520
+ if (key) {
521
+ keys_obj = { [key]: 1 };
522
+ } else if (Array.isArray(keys) && keys.length > 0) {
523
+ keys_obj = {};
524
+ for (const k of keys)
525
+ keys_obj[k] = 1;
526
+ } else if (keys != null && typeof keys === "object") {
527
+ keys_obj = keys;
528
+ } else {
529
+ throw new import_errors.InvalidUsageError({
530
+ message: "Define one of the following parameters: [key, keys].",
531
+ reason: "invalid_index_definition"
532
+ });
533
+ }
534
+ return { keys_obj, options, forced };
535
+ }
536
+ /**
537
+ * Drop all indexes that are NOT part of this._init_indexes, excluding _id_ (and TTL index if enabled).
538
+ *
539
+ * @note We match by key pattern rather than name because names can differ.
540
+ */
541
+ async _drop_non_init_indexes() {
542
+ this.assert_not_transaction_based();
543
+ this.assert_init();
544
+ const existing = await this._col.listIndexes().toArray();
545
+ const desired = /* @__PURE__ */ new Set();
546
+ for (const item of this._init_indexes ?? []) {
547
+ const { keys_obj } = this._normalize_index_opts(item);
548
+ desired.add(this._index_key_signature(keys_obj));
549
+ }
550
+ const protected_names = /* @__PURE__ */ new Set(["_id_"]);
551
+ const keep_ttl = this.ttl_enabled;
552
+ const ttl_sig = this._index_key_signature({ __ttl_timestamp: 1 });
553
+ for (const ix of existing) {
554
+ const name = ix?.name;
555
+ if (!name)
556
+ continue;
557
+ if (protected_names.has(name))
558
+ continue;
559
+ const keyObj = ix.key;
560
+ if (!keyObj)
561
+ continue;
562
+ const sig = this._index_key_signature(keyObj);
563
+ if (desired.has(sig))
564
+ continue;
565
+ if (keep_ttl && sig === ttl_sig)
566
+ continue;
567
+ try {
568
+ this.db.server.log(3, `Dropping stale index "${name}" on collection: ${this.name}`);
569
+ await this._col.dropIndex(name);
570
+ } catch (err) {
571
+ if (err?.codeName !== "IndexNotFound")
572
+ throw err;
573
+ }
574
+ }
575
+ }
576
+ /**
577
+ * Creates indexes on collections.
578
+ *
579
+ * @note When transaction mode is enabled, the session option will not be used.
580
+ *
581
+ * @param opts The index create options.
582
+ */
583
+ async _create_index(opts) {
584
+ this.assert_not_transaction_based();
585
+ if (!this.initialized) {
586
+ await this.init();
587
+ }
588
+ this.assert_init();
589
+ const { keys_obj, options, forced } = this._normalize_index_opts(opts);
590
+ const drop_index = async () => {
591
+ const existing = await this._col.listIndexes().toArray();
592
+ const match = existing.find((ix) => {
593
+ const ix_key = ix?.key;
594
+ if (!ix_key)
595
+ return false;
596
+ return this._keys_equal(ix_key, keys_obj);
597
+ });
598
+ if (match?.name) {
599
+ try {
600
+ await this._col.dropIndex(match.name);
601
+ } catch (err) {
602
+ if (err?.codeName !== "IndexNotFound")
603
+ throw err;
604
+ }
605
+ return;
606
+ }
607
+ if (options?.name) {
608
+ try {
609
+ await this._col.dropIndex(options.name);
610
+ } catch (err) {
611
+ if (err?.codeName !== "IndexNotFound")
612
+ throw err;
613
+ }
614
+ return;
615
+ }
616
+ const synthesized = Object.entries(keys_obj).map(([k, v]) => `${k}_${v}`).join("_");
617
+ try {
618
+ await this._col.dropIndex(synthesized);
619
+ } catch (err) {
620
+ if (err?.codeName !== "IndexNotFound")
621
+ throw err;
622
+ }
623
+ };
624
+ try {
625
+ try {
626
+ return await this._col.createIndex(keys_obj, options);
627
+ } catch (err) {
628
+ if (forced && err && typeof err === "object" && err.codeName === "IndexKeySpecsConflict") {
629
+ await drop_index();
630
+ return await this._col.createIndex(keys_obj, options);
631
+ }
632
+ throw err;
633
+ }
634
+ } catch (err) {
635
+ throw new Error(`Failed to create index on collection "${this.name}": ${err}`, { cause: err });
636
+ }
637
+ }
638
+ // async create_index(opts: string | Collection.IndexOpts): Promise<string> {
639
+ // // Not supported on transaction-based collections.
640
+ // this.assert_not_transaction_based();
641
+ // // Ensure initialized
642
+ // if (!this.initialized) { await this.init(); } this.assert_init();
643
+ // // ---- Normalize inputs ----
644
+ // let key: string | undefined;
645
+ // let keys: string[] | Record<string, number> | undefined;
646
+ // let options: mongodb.CreateIndexesOptions | undefined;
647
+ // let unique: boolean | undefined;
648
+ // let sparse: boolean | undefined;
649
+ // let forced = false;
650
+ // if (typeof opts === "string") {
651
+ // key = opts;
652
+ // unique = undefined;
653
+ // sparse = undefined;
654
+ // } else {
655
+ // ({ key, keys, forced = false } = opts);
656
+ // const options = opts.options as unknown as undefined | mongodb.CreateIndexesOptions;
657
+ // // Conflict guard between `unique` and `options.unique`
658
+ // if (opts.unique != null && options?.unique != null && opts.unique !== options.unique) {
659
+ // throw new InvalidUsageError({
660
+ // message: `Encountered different values for attribute 'unique': ${opts.unique} and 'options.unique': ${options.unique}.`,
661
+ // reason: "invalid_unique_option",
662
+ // });
663
+ // }
664
+ // unique = opts.unique ?? options?.unique;
665
+ // // Conflict guard between `sparse` and `options.sparse`
666
+ // if (opts.sparse != null && options?.sparse != null && opts.sparse !== options.sparse) {
667
+ // throw new InvalidUsageError({
668
+ // message: `Encountered different values for attribute 'sparse': ${opts.sparse} and 'options.sparse': ${options.sparse}.`,
669
+ // reason: "invalid_sparse_option",
670
+ // });
671
+ // }
672
+ // sparse = opts.sparse ?? options?.sparse;
673
+ // }
674
+ // // Ensure `unique` in options when provided
675
+ // if (unique) {
676
+ // options = options || {};
677
+ // options.unique = unique;
678
+ // }
679
+ // // Ensure `sparse` in options when provided
680
+ // if (sparse) {
681
+ // options = options || {};
682
+ // options.sparse = sparse;
683
+ // }
684
+ // // Build keys object
685
+ // let keys_obj: Record<string, number>;
686
+ // if (key) {
687
+ // keys_obj = { [key]: 1 };
688
+ // } else if (Array.isArray(keys) && keys.length > 0) {
689
+ // keys_obj = {};
690
+ // for (const k of keys) keys_obj[k] = 1;
691
+ // } else if (keys != null && typeof keys === "object") {
692
+ // keys_obj = keys as Record<string, number>;
693
+ // } else {
694
+ // throw new InvalidUsageError({
695
+ // message: "Define one of the following parameters: [key, keys].",
696
+ // reason: "invalid_index_definition",
697
+ // });
698
+ // }
699
+ // const drop_index = async () => {
700
+ // try {
701
+ // const existing = await this._col.listIndexes().toArray();
702
+ // const match = existing.find(ix => {
703
+ // const ix_key = ix?.key as Record<string, number> | undefined;
704
+ // if (!ix_key) return false;
705
+ // const a = Object.entries(ix_key);
706
+ // const b = Object.entries(keys_obj);
707
+ // if (a.length !== b.length) return false;
708
+ // // exact key-value equality (order-insensitive)
709
+ // const as = new Map(a);
710
+ // for (const [kk, vv] of b) {
711
+ // if (as.get(kk) !== vv) return false;
712
+ // }
713
+ // return true;
714
+ // });
715
+ // // Prefer matched key's real name
716
+ // if (match?.name) {
717
+ // try { await this._col.dropIndex(match.name); }
718
+ // catch (err: any) { if (err?.codeName !== "IndexNotFound") throw err; }
719
+ // } else if (options?.name) {
720
+ // try { await this._col.dropIndex(options.name); }
721
+ // catch (err: any) { if (err?.codeName !== "IndexNotFound") throw err; }
722
+ // } else {
723
+ // // last-resort synthesized name (simple cases)
724
+ // const synthesized = Object.entries(keys_obj).map(([k, v]) => `${k}_${v}`).join("_");
725
+ // try { await this._col.dropIndex(synthesized); }
726
+ // catch (err: any) { if (err?.codeName !== "IndexNotFound") throw err; }
727
+ // }
728
+ // } catch (err) {
729
+ // // If listIndexes itself fails for some reason, do not hide the error
730
+ // throw new Error(`Failed to create index on collection "${this.name}": ${err}`, { cause: err });
731
+ // }
732
+ // }
733
+ // try {
734
+ // // Create (or re-create)
735
+ // try {
736
+ // return await this._col.createIndex(keys_obj, options);
737
+ // }
738
+ // // Retry once on IndexKeySpecsConflict when forced=true
739
+ // catch (err) {
740
+ // if (forced && err && typeof err === "object" && (
741
+ // (err as any).codeName === "IndexKeySpecsConflict"
742
+ // )) {
743
+ // await drop_index();
744
+ // return await this._col.createIndex(keys_obj, options);
745
+ // }
746
+ // throw err;
747
+ // }
748
+ // } catch (err) {
749
+ // throw new Error(`Failed to create index on collection "${this.name}": ${err}`, { cause: err });
750
+ // }
751
+ // }
752
+ // -------------------------------------------------------------------
753
+ // Public methods.
754
+ // -------------------------------------------------------------------
755
+ /**
756
+ * Initialize the collection, creating indexes and setting up TTL if needed.
757
+ * @returns The initialized collection instance.
758
+ *
759
+ * @docs
760
+ */
761
+ async init() {
762
+ if (this.initialized === false) {
763
+ this.db.server.log(3, "Initializing collection: ", this.name);
764
+ if (!this.is_transaction) {
765
+ if (this._col == null) {
766
+ this.db.server.log(3, "Checking collection: ", this.name);
767
+ if (!this.db.server.production) {
768
+ await this.db.ensure_connection();
769
+ }
770
+ if (!this.db.connected || !this.db._db) {
771
+ throw new import_errors.InvalidUsageError({
772
+ message: `Database client is not connected.`,
773
+ reason: "client_not_connected"
774
+ });
775
+ }
776
+ if (this.db._listed_cols == null) {
777
+ this.db.server.log(3, "Listing collections...");
778
+ this.db._listed_cols = await this.db._db.listCollections().toArray();
779
+ this.db.server.log(3, "Listed collections: " + this.db._listed_cols.map((x) => x.name).join(", "));
780
+ }
781
+ if (!this.db._listed_cols.find((x) => x.name === this.name)) {
782
+ this.db.server.log(3, "Creating collection: " + this.name);
783
+ let create_col_retries = 3;
784
+ let last_error = null;
785
+ let collection_created = false;
786
+ while (create_col_retries > 0 && !collection_created) {
787
+ try {
788
+ await this.db._db.createCollection(this.name);
789
+ collection_created = true;
790
+ } catch (error) {
791
+ last_error = error;
792
+ if (error.codeName === "NamespaceExists") {
793
+ collection_created = true;
794
+ } else if (create_col_retries > 1 && (error.code === 11e3 || error.code === 48)) {
795
+ create_col_retries--;
796
+ await new Promise((r) => setTimeout(r, 100));
797
+ } else {
798
+ throw error;
799
+ }
800
+ }
801
+ }
802
+ if (!collection_created && last_error) {
803
+ throw last_error;
804
+ }
805
+ }
806
+ this.db.server.log(3, "Initializing mongodb collection connection: " + this.name);
807
+ this._col = this.db._db.collection(this.name);
808
+ }
809
+ this.initialized = true;
810
+ if (this.ttl_enabled) {
811
+ this.db.server.log(3, "Setting up TTL index for collection: " + this.name);
812
+ await this._setup_ttl();
813
+ }
814
+ await this._drop_non_init_indexes();
815
+ if (this._init_indexes?.length) {
816
+ for (const item of this._init_indexes) {
817
+ this.db.server.log(3, "Creating index " + JSON.stringify(item) + " on collection: " + this.name);
818
+ await this._create_index(item);
819
+ }
820
+ }
821
+ } else {
822
+ if (!this.db.client) {
823
+ throw new import_errors.InvalidUsageError({
824
+ message: "Database client is not initialized, this is likely because you did not initialize the transaction based collection through 'Collection.start_transaction'.",
825
+ reason: "client_not_connected"
826
+ });
827
+ }
828
+ if (!this._col) {
829
+ throw new import_errors.InvalidUsageError({
830
+ message: "Derived collection is not initialized, this should have been initialized before passing it to a transaction based collection constructor.",
831
+ reason: "derived_collection_not_initialized"
832
+ });
833
+ }
834
+ this._session = this.db.client.startSession();
835
+ this._session.startTransaction();
836
+ this.initialized = true;
837
+ }
838
+ }
839
+ return this;
840
+ }
841
+ /**
842
+ * Assert that the collection is initialized and has a valid MongoDB collection.
843
+ * @throws {Error} Throws if the collection is not initialized or _col is null
844
+ * @returns An initialized collection type assertion
845
+ *
846
+ * @docs
847
+ */
848
+ assert_init() {
849
+ if (!this.initialized || this._col == null) {
850
+ throw new import_errors.InvalidUsageError({
851
+ message: `Collection "${this.name}" is not initialized.`,
852
+ reason: "collection_not_initialized"
853
+ });
854
+ }
855
+ }
856
+ /**
857
+ * Assert that if this is a transaction, it has not been finalized.
858
+ * @throws Error if this is a finalized transaction.
859
+ *
860
+ * @docs
861
+ */
862
+ assert_not_finalized() {
863
+ if (this.is_transaction && this.is_finalized_transaction) {
864
+ throw new import_errors.InvalidUsageError({
865
+ message: `Transaction has already been finalized (committed or aborted).`,
866
+ reason: "transaction_finalized"
867
+ });
868
+ }
869
+ }
870
+ /**
871
+ * Assert that this collection is not transaction based.
872
+ *
873
+ * @docs
874
+ */
875
+ assert_not_transaction_based() {
876
+ if (this.is_transaction) {
877
+ throw new import_errors.InvalidUsageError({
878
+ message: `Collection "${this.name}" is transaction based.`,
879
+ reason: "collection_is_transaction"
880
+ });
881
+ }
882
+ }
883
+ /**
884
+ * Get operation options with session if this is a transaction.
885
+ * @returns Options object with session if applicable.
886
+ *
887
+ * @docs
888
+ */
889
+ get_operation_options(opts) {
890
+ if (this.is_transaction && this._session) {
891
+ return { ...opts, session: this._session };
892
+ }
893
+ return opts ?? {};
894
+ }
895
+ /**
896
+ * Get the raw and initialized MongoDB collection.
897
+ * @returns The MongoDB collection instance.
898
+ *
899
+ * @docs
900
+ */
901
+ async col() {
902
+ await this.init();
903
+ return this._col;
904
+ }
905
+ /**
906
+ * Check if an index exists.
907
+ * @note Not supported for transaction based collections.
908
+ * @param index The name of the index to check.
909
+ * @returns True if the index exists, false otherwise.
910
+ *
911
+ * @docs
912
+ */
913
+ async has_index(index) {
914
+ if (!this.initialized) {
915
+ await this.init();
916
+ }
917
+ this.assert_init();
918
+ this.assert_not_finalized();
919
+ this.assert_not_transaction_based();
920
+ return (await this._col.listIndexes().toArray()).some((x) => x.name === index);
921
+ }
922
+ /**
923
+ * Standalone helper: merge `source` into `target` for missing keys only.
924
+ * Clones assigned nested objects/arrays/dates once (when `clone` is true).
925
+ *
926
+ * @throws An error if the max depth recursion depth has been exceeded.
927
+ *
928
+ * @docs
929
+ */
930
+ static insert_defaults(target, source, opts = {}) {
931
+ const max_depth = opts.max_depth ?? 1e3;
932
+ const depth = opts.depth ?? 0;
933
+ const should_clone = opts.clone ?? true;
934
+ const isPlainObject = (v) => v != null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
935
+ const cloneAssigned = (val, d) => {
936
+ if (!should_clone)
937
+ return val;
938
+ if (d > max_depth)
939
+ return val;
940
+ if (Array.isArray(val)) {
941
+ return val.map((item) => cloneAssigned(item, d + 1));
942
+ }
943
+ if (val instanceof Date) {
944
+ return new Date(val.getTime());
945
+ }
946
+ if (isPlainObject(val)) {
947
+ const out = {};
948
+ for (const k of Object.keys(val)) {
949
+ out[k] = cloneAssigned(val[k], d + 1);
950
+ }
951
+ return out;
952
+ }
953
+ return val;
954
+ };
955
+ if (depth > max_depth) {
956
+ throw new Error(`Maximum recursion depth (${max_depth}) exceeded in 'insert_defaults'`);
957
+ }
958
+ for (const key of Object.keys(source)) {
959
+ const v = target[key];
960
+ const d = source[key];
961
+ if (v === void 0) {
962
+ target[key] = cloneAssigned(d, depth + 1);
963
+ } else if (isPlainObject(v) && isPlainObject(d)) {
964
+ Collection.insert_defaults(v, d, { depth: depth + 1, max_depth, clone: should_clone });
965
+ }
966
+ }
967
+ }
968
+ flatten(obj, prefix = "") {
969
+ return (0, import_flatten.flatten)(obj, prefix);
970
+ }
971
+ /**
972
+ * Execute `on_transform_version` and `on_load_cb` on a loaded document.
973
+ * Ensures `__record_version` is set when {@link record_version} is defined.
974
+ *
975
+ * @note This is done automatically during load operations.
976
+ *
977
+ * @param data The loaded document.
978
+ * @param opts Additional options.
979
+ *
980
+ * @returns The transformed document.
981
+ *
982
+ * @throws {Collection.OnTransformError} When an error occurs during the {@link Collection.Opts.on_transform_version} callback.
983
+ * @throws {Collection.OnLoadError} When an error occurs during the {@link Collection.Opts.on_load} callback.
984
+ *
985
+ * @docs
986
+ */
987
+ async apply_on_load(data, opts) {
988
+ let transformed = false;
989
+ const is_partial = this._is_partial_projection(opts.projection);
990
+ if (this.record_version != null && this.on_transform_version != null && data && data.__record_version !== this.record_version) {
991
+ try {
992
+ data = await this.on_transform_version(data, {
993
+ from_version: data.__record_version,
994
+ to_version: this.record_version,
995
+ projection: opts.projection,
996
+ is_partial
997
+ });
998
+ transformed = true;
999
+ } catch (error) {
1000
+ throw new Collection.OnTransformError({
1001
+ message: `Failed to transform document from version '${data.__record_version}' to '${this.record_version}'.`,
1002
+ query: {},
1003
+ reason: "callback_error",
1004
+ cause: error
1005
+ });
1006
+ }
1007
+ data.__record_version = this.record_version;
1008
+ }
1009
+ if (this.on_load_cb) {
1010
+ try {
1011
+ data = await this.on_load_cb(data, {
1012
+ projection: opts.projection,
1013
+ is_partial
1014
+ });
1015
+ } catch (error) {
1016
+ throw new Collection.OnLoadError({
1017
+ message: `Encountered an error during the 'on_load' callback.`,
1018
+ query: {},
1019
+ reason: "callback_error",
1020
+ cause: error
1021
+ });
1022
+ }
1023
+ }
1024
+ if (transformed && this.persist_transformed_on_load && opts.persist && // only persist if doc came from DB (not a default)
1025
+ !is_partial && // only when we have a full document
1026
+ data?._id != null) {
1027
+ try {
1028
+ if (this.persist_transformed_on_load === "replace") {
1029
+ const replace_doc = { ...data };
1030
+ if (this.ttl_enabled && replace_doc.__ttl_timestamp == null) {
1031
+ replace_doc.__ttl_timestamp = /* @__PURE__ */ new Date();
1032
+ }
1033
+ if (this.record_version != null && replace_doc.__record_version == null) {
1034
+ replace_doc.__record_version = this.record_version;
1035
+ }
1036
+ const res = this.replace(
1037
+ { _id: data._id },
1038
+ replace_doc,
1039
+ { upsert: false, throw: false, apply_ttl: false }
1040
+ // do not create on read
1041
+ );
1042
+ if (opts.await_persist) {
1043
+ await res;
1044
+ } else {
1045
+ void res;
1046
+ }
1047
+ } else {
1048
+ const set_doc = { ...data };
1049
+ delete set_doc._id;
1050
+ delete set_doc.__ttl_timestamp;
1051
+ const res = this.save(
1052
+ { _id: data._id },
1053
+ { $set: set_doc },
1054
+ { upsert: false, throw: false, apply_ttl: false }
1055
+ // do not create on read
1056
+ );
1057
+ if (opts.await_persist) {
1058
+ await res;
1059
+ } else {
1060
+ void res;
1061
+ }
1062
+ }
1063
+ } catch {
1064
+ }
1065
+ }
1066
+ return data;
1067
+ }
1068
+ /**
1069
+ * Count documents accurately using MongoDB's `countDocuments`.
1070
+ *
1071
+ * @param query An optional filter to count matching documents. When omitted, counts all documents.
1072
+ * @param opts Additional options, see {@link Collection.CountOpts}.
1073
+ *
1074
+ * @note The `opts.throw` option defaults to `true`.
1075
+ *
1076
+ * @returns
1077
+ * - A number representing the accurate count when successful.
1078
+ * - A {@link Collection.CountError} when `opts.throw === false` and an error occurs.
1079
+ *
1080
+ * @throws {Collection.CountError} When `throw !== false` and the count fails.
1081
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1082
+ *
1083
+ * @docs
1084
+ */
1085
+ async count(query, opts) {
1086
+ if (!this.initialized) {
1087
+ await this.init();
1088
+ }
1089
+ this.assert_init();
1090
+ this.assert_not_finalized();
1091
+ const query_op = this._init_query(query ?? {}, true, "query");
1092
+ const throw_errors = opts?.throw ?? true;
1093
+ try {
1094
+ const n = await this._with_retry(() => this._col.countDocuments(query_op, this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
1095
+ return n;
1096
+ } catch (e) {
1097
+ const err = new Collection.CountError({
1098
+ message: "Count operation failed due to an unexpected error.",
1099
+ query: query_op,
1100
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1101
+ cause: e
1102
+ });
1103
+ if (throw_errors)
1104
+ throw err;
1105
+ return err;
1106
+ }
1107
+ }
1108
+ /**
1109
+ * Return a fast, approximate count of the entire collection using
1110
+ * MongoDB's `estimatedDocumentCount`. This method does **not** accept
1111
+ * a filter and may be off under heavy churn.
1112
+ *
1113
+ * @param opts Additional options, see {@link Collection.CountOpts}.
1114
+ *
1115
+ * @note The `opts.throw` option defaults to `true`.
1116
+ *
1117
+ * @returns
1118
+ * - A number representing the estimated total number of documents when successful.
1119
+ * - A {@link Collection.CountError} when `opts.throw === false` and an error occurs.
1120
+ *
1121
+ * @throws {Collection.CountError} When `throw !== false` and the count fails.
1122
+ * @throws {InvalidUsageError} (always) When the collection was not used properly.
1123
+ *
1124
+ * @docs
1125
+ */
1126
+ async count_estimated(opts) {
1127
+ if (!this.initialized) {
1128
+ await this.init();
1129
+ }
1130
+ this.assert_init();
1131
+ this.assert_not_finalized();
1132
+ const throw_errors = opts?.throw ?? true;
1133
+ try {
1134
+ const n = await this._with_retry(() => this._col.estimatedDocumentCount(this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
1135
+ return n;
1136
+ } catch (e) {
1137
+ const err = new Collection.CountError({
1138
+ message: "Estimated count operation failed due to an unexpected error.",
1139
+ query: {},
1140
+ // no filter for estimatedDocumentCount
1141
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1142
+ cause: e
1143
+ });
1144
+ if (throw_errors)
1145
+ throw err;
1146
+ return err;
1147
+ }
1148
+ }
1149
+ /**
1150
+ * List all documents for a specific query.
1151
+ *
1152
+ * @param query The database directory path.
1153
+ * @param opts The list options, see {@link Collection.ListOpts}.
1154
+ * @param allow_empty_query When `true`, allows an empty query (i.e. `{}`) to be passed, which would otherwise throw an error.
1155
+ *
1156
+ * @note The `opts.throw` option defaults to `true`.
1157
+ * @note The {@link Collection.Opts.on_load} and {@link Collection.Opts.on_transform_version} callbacks
1158
+ * are not executed when `opts.cursor === true`.
1159
+ * @note When `opts.callback` is a function (and `opts.cursor !== true`), this method streams documents and
1160
+ * invokes the callback for each processed document, then returns `undefined` on success.
1161
+ * This mode is memory-friendly and avoids accumulating the entire result set.
1162
+ *
1163
+ * @returns
1164
+ * - An error if `opts.throw === false` and a {@link Collection.ListError} has occurred.
1165
+ * - The find cursor when `opts.cursor === true`.
1166
+ * - When `opts.callback && !opts.cursor` is provided, `undefined` on success.
1167
+ * - When `opts.page_info === true && !opts.cursor && !opts.callback`, returns {@link Collection.ListedPage}.
1168
+ * - Otherwise, an array of documents matching the path.
1169
+ *
1170
+ * @throws {Collection.ListError} When `throw !== false` if an error occurred during the operation, in which case {@link Collection.ListError.cause} is defined.
1171
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1172
+ *
1173
+ * @docs
1174
+ */
1175
+ async list(query, opts, allow_empty_query = false) {
1176
+ if (!this.initialized) {
1177
+ await this.init();
1178
+ }
1179
+ this.assert_init();
1180
+ this.assert_not_finalized();
1181
+ const throw_errors = opts?.throw ?? true;
1182
+ const has_callback = typeof opts?.callback === "function";
1183
+ const page_info_requested = opts?.page_info === true && opts?.cursor !== true && !has_callback;
1184
+ if (has_callback && opts?.cursor === true) {
1185
+ throw new import_errors.InvalidUsageError({
1186
+ message: "Option 'callback' cannot be combined with 'cursor: true'.",
1187
+ reason: "invalid_option_combination",
1188
+ field: "opts.callback"
1189
+ });
1190
+ }
1191
+ if (has_callback && opts?.page_info === true) {
1192
+ throw new import_errors.InvalidUsageError({
1193
+ message: "Option 'callback' cannot be combined with 'page_info: true'.",
1194
+ reason: "invalid_option_combination",
1195
+ field: "opts.callback"
1196
+ });
1197
+ }
1198
+ const user_limit = opts?.limit;
1199
+ if (typeof user_limit === "number") {
1200
+ const effective_user_limit = page_info_requested ? user_limit + 1 : user_limit;
1201
+ const is_integer = Number.isInteger(user_limit);
1202
+ const is_valid = user_limit >= 0 && Number.isFinite(effective_user_limit);
1203
+ if (!is_integer || !is_valid) {
1204
+ throw new import_errors.InvalidUsageError({
1205
+ message: `Option 'limit' must be a non-negative finite integer${page_info_requested ? " (including +1 for pagination)." : "."}`,
1206
+ reason: "invalid_limit",
1207
+ field: "opts.limit"
1208
+ });
1209
+ }
1210
+ }
1211
+ const probing_limit = typeof user_limit === "number" && page_info_requested ? user_limit + 1 : user_limit;
1212
+ if (opts?.skip != null) {
1213
+ if (!Number.isInteger(opts.skip) || opts.skip < 0) {
1214
+ throw new import_errors.InvalidUsageError({
1215
+ message: "Option 'skip' must be a non-negative integer.",
1216
+ reason: "invalid_skip",
1217
+ field: "opts.skip"
1218
+ });
1219
+ }
1220
+ }
1221
+ if (user_limit === 0 && !opts?.cursor) {
1222
+ if (has_callback) {
1223
+ return void 0;
1224
+ }
1225
+ return page_info_requested ? { items: [], has_more: false } : [];
1226
+ }
1227
+ let batch_size = typeof opts?.pagination?.batch_size === "number" ? Math.floor(opts.pagination.batch_size) : 1e3;
1228
+ if (!Number.isFinite(batch_size) || batch_size < 1 || batch_size > 1e4) {
1229
+ throw new import_errors.InvalidUsageError({
1230
+ message: "Option `pagination.batch_size` must be an integer between '1' and '10000'.",
1231
+ reason: "invalid_pagination_batch_size",
1232
+ field: "opts.pagination.batch_size"
1233
+ });
1234
+ }
1235
+ if (typeof probing_limit === "number" && probing_limit > 0 && probing_limit < 1e4) {
1236
+ batch_size = Math.min(batch_size, probing_limit);
1237
+ }
1238
+ const query_op = this._init_query(query, allow_empty_query, "query");
1239
+ const find_options = {
1240
+ projection: opts?.projection ? Collection.Projection.init(this._ensure_version_in_projection(opts.projection)) : void 0,
1241
+ sort: opts?.sort,
1242
+ skip: opts?.skip,
1243
+ // no default so we can stream all docs if no limit was set.
1244
+ // allow +1 probe for page_info
1245
+ limit: probing_limit
1246
+ };
1247
+ if (typeof opts?.timeout === "number") {
1248
+ find_options.maxTimeMS = opts.timeout;
1249
+ }
1250
+ try {
1251
+ const cursor = await this._with_retry(() => this._col.find(query_op, this.get_operation_options(find_options)), opts?.retry);
1252
+ cursor.batchSize(batch_size);
1253
+ if (typeof opts?.timeout === "number") {
1254
+ cursor.maxTimeMS(opts.timeout);
1255
+ }
1256
+ if (opts?.cursor)
1257
+ return cursor;
1258
+ if (has_callback) {
1259
+ const max_docs2 = user_limit ?? Number.POSITIVE_INFINITY;
1260
+ let processed_count = 0;
1261
+ try {
1262
+ while (processed_count < max_docs2) {
1263
+ const first = await this._with_retry(() => cursor.next(), opts?.retry);
1264
+ if (first == null)
1265
+ break;
1266
+ let processed = first;
1267
+ if (processed && typeof processed === "object") {
1268
+ processed = await this.apply_on_load(processed, {
1269
+ projection: opts?.projection,
1270
+ persist: true,
1271
+ await_persist: false
1272
+ });
1273
+ }
1274
+ try {
1275
+ await opts.callback(processed);
1276
+ } catch (cb_err) {
1277
+ throw new Collection.ListError({
1278
+ message: "List callback failed for a streamed document.",
1279
+ query: query_op,
1280
+ reason: "callback_error",
1281
+ cause: cb_err
1282
+ });
1283
+ }
1284
+ processed_count++;
1285
+ if (processed_count >= max_docs2)
1286
+ break;
1287
+ let drained = 1;
1288
+ while (drained < batch_size && processed_count < max_docs2) {
1289
+ const next_in_buffer = await cursor.tryNext();
1290
+ if (next_in_buffer == null)
1291
+ break;
1292
+ let processed2 = next_in_buffer;
1293
+ if (processed2 && typeof processed2 === "object") {
1294
+ processed2 = await this.apply_on_load(processed2, {
1295
+ projection: opts?.projection,
1296
+ persist: true,
1297
+ await_persist: false
1298
+ });
1299
+ }
1300
+ try {
1301
+ await opts.callback(processed2);
1302
+ } catch (cb_err) {
1303
+ throw new Collection.ListError({
1304
+ message: "List callback failed for a streamed document.",
1305
+ query: query_op,
1306
+ reason: "callback_error",
1307
+ cause: cb_err
1308
+ });
1309
+ }
1310
+ processed_count++;
1311
+ drained++;
1312
+ }
1313
+ }
1314
+ } finally {
1315
+ if (!cursor.closed) {
1316
+ await cursor.close().catch(() => {
1317
+ });
1318
+ }
1319
+ }
1320
+ return void 0;
1321
+ }
1322
+ const max_docs = user_limit ?? Number.POSITIVE_INFINITY;
1323
+ const target = page_info_requested && typeof user_limit === "number" ? user_limit + 1 : max_docs;
1324
+ const docs = [];
1325
+ let fetched = 0;
1326
+ try {
1327
+ while (fetched < target) {
1328
+ const first = await this._with_retry(() => cursor.next(), opts?.retry);
1329
+ if (first == null) {
1330
+ break;
1331
+ }
1332
+ let processed = first;
1333
+ if (processed && typeof processed === "object") {
1334
+ processed = await this.apply_on_load(processed, {
1335
+ projection: opts?.projection,
1336
+ persist: true,
1337
+ await_persist: false
1338
+ });
1339
+ }
1340
+ docs.push(processed);
1341
+ fetched++;
1342
+ if (fetched >= target) {
1343
+ break;
1344
+ }
1345
+ let drained = 1;
1346
+ while (drained < batch_size && fetched < target) {
1347
+ const next_in_buffer = await cursor.tryNext();
1348
+ if (next_in_buffer == null) {
1349
+ break;
1350
+ }
1351
+ let processed2 = next_in_buffer;
1352
+ if (processed2 && typeof processed2 === "object") {
1353
+ processed2 = await this.apply_on_load(processed2, {
1354
+ projection: opts?.projection,
1355
+ persist: true,
1356
+ await_persist: false
1357
+ });
1358
+ }
1359
+ docs.push(processed2);
1360
+ fetched++;
1361
+ drained++;
1362
+ }
1363
+ }
1364
+ } finally {
1365
+ if (!cursor.closed) {
1366
+ await cursor.close().catch(() => {
1367
+ });
1368
+ }
1369
+ }
1370
+ if (page_info_requested) {
1371
+ let has_more = false;
1372
+ let out = docs;
1373
+ if (typeof user_limit === "number" && docs.length > user_limit) {
1374
+ has_more = true;
1375
+ out = docs.slice(0, user_limit);
1376
+ }
1377
+ return { items: out, has_more };
1378
+ }
1379
+ if (docs.length > max_docs && max_docs !== Number.POSITIVE_INFINITY) {
1380
+ return docs.slice(0, max_docs);
1381
+ }
1382
+ return docs;
1383
+ } catch (e) {
1384
+ if (e instanceof Collection.ListError) {
1385
+ if (throw_errors)
1386
+ throw e;
1387
+ return e;
1388
+ }
1389
+ const error = new Collection.ListError({
1390
+ message: "Encountered an error while listing documents.",
1391
+ query: query_op,
1392
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1393
+ cause: e
1394
+ });
1395
+ if (throw_errors)
1396
+ throw error;
1397
+ return error;
1398
+ }
1399
+ }
1400
+ /**
1401
+ * List all documents of the collection.
1402
+ *
1403
+ * @param opts The list options, see {@link Collection.ListOpts}.
1404
+ *
1405
+ * @note The `opts.throw` option defaults to `true`.
1406
+ * @note The {@link Collection.Opts.on_load} and {@link Collection.Opts.on_transform_version} callbacks
1407
+ * are not executed when `opts.cursor === true`.
1408
+ * @note When `opts.callback` is a function (and `opts.cursor !== true`), this method streams documents and
1409
+ * invokes the callback for each processed document, then returns `undefined` on success.
1410
+ *
1411
+ * @returns
1412
+ * - Array of all documents in the collection.
1413
+ * - The find cursor when `opts.cursor === true`.
1414
+ * - `undefined` when `opts.callback && !opts.cursor`.
1415
+ * - An error if `opts.throw === false` and a {@link Collection.ListError} has occurred.
1416
+ *
1417
+ * @throws {Collection.ListError} When `throw !== false` if an error occurred during the operation, in which case {@link Collection.ListError.cause} is defined.
1418
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1419
+ *
1420
+ * @docs
1421
+ */
1422
+ async list_all(opts) {
1423
+ return this.list({}, opts, true);
1424
+ }
1425
+ /**
1426
+ * Check if a document exists by only loading the document's id.
1427
+ *
1428
+ * @param query The database path to the document.
1429
+ * @param opts The exists options, see {@link Collection.ExistsOpts}.
1430
+ *
1431
+ * @note The `opts.throw` option defaults to `true`.
1432
+ * @note This method does not execute the {@link Collection.Opts.on_load}
1433
+ * and {@link Collection.Opts.on_transform_version} callbacks.
1434
+ *
1435
+ * @returns
1436
+ * - An error if `opts.throw === false` and a {@link Collection.ExistsError} has occurred.
1437
+ * - True if the document exists, false otherwise.
1438
+ *
1439
+ * @throws {Collection.ExistsError} When `throw !== false` if an error occurred during the operation, in which case {@link Collection.ExistsError.cause} is defined.
1440
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1441
+ *
1442
+ * @docs
1443
+ */
1444
+ async exists(query, opts) {
1445
+ if (!this.initialized) {
1446
+ await this.init();
1447
+ }
1448
+ this.assert_init();
1449
+ this.assert_not_finalized();
1450
+ const query_op = this._init_query(query, false, "query");
1451
+ const throw_errors = opts?.throw ?? true;
1452
+ try {
1453
+ const find_opts = {
1454
+ projection: { _id: 1 }
1455
+ };
1456
+ if (typeof opts?.timeout === "number") {
1457
+ find_opts.maxTimeMS = opts.timeout;
1458
+ }
1459
+ const doc = await this._with_retry(() => this._col.findOne(query_op, this.get_operation_options(find_opts)), opts?.retry);
1460
+ return doc != null;
1461
+ } catch (e) {
1462
+ const err = new Collection.ExistsError({
1463
+ message: "Failed to check if the queried document exists due to an unexpected error.",
1464
+ query: query_op,
1465
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1466
+ cause: e
1467
+ });
1468
+ if (throw_errors)
1469
+ throw err;
1470
+ return err;
1471
+ }
1472
+ }
1473
+ /**
1474
+ * Load a single document by query.
1475
+ *
1476
+ * Applies an optional projection and, if a `default` is provided, inserts any
1477
+ * missing keys from the default into the loaded document (values are deep-cloned).
1478
+ *
1479
+ * @note The `default` value is deep-cloned if it is returned or inserted.
1480
+ * @note The `opts.throw` option defaults to `true`.
1481
+ *
1482
+ * @param query The database query.
1483
+ * @param opts Additional load options {@link Collection.LoadOpts}.
1484
+ *
1485
+ * @returns
1486
+ * - When `opts.throw === false`:
1487
+ * - If found: the loaded (projected) document.
1488
+ * - If not found and `opts.default` is provided: the deep-cloned default data.
1489
+ * - If not found and no default: a {@link Collection.NotFoundError}.
1490
+ * - On load failure: a {@link Collection.LoadError}.
1491
+ * - When `opts.throw !== false` (default):
1492
+ * - If found: the loaded (projected) document.
1493
+ * - If not found and `opts.default` is provided: the deep-cloned default data.
1494
+ * - If not found and no default: a {@link Collection.NotFoundError} is **thrown**.
1495
+ * - On load failure: a {@link Collection.LoadError} is **thrown**.
1496
+ *
1497
+ * @throws {Collection.LoadError} Only when `opts.throw !== false` and the load fails.
1498
+ * @throws {Collection.NotFoundError} When the document is not found and `opts.throw !== false && opts.default == null`.
1499
+ * @throws {InvalidUsageError} When the provided arguments are invalid or if the collection was not used properly.
1500
+ *
1501
+ * @docs
1502
+ */
1503
+ async load(query, opts) {
1504
+ if (!this.initialized) {
1505
+ await this.init();
1506
+ }
1507
+ this.assert_init();
1508
+ this.assert_not_finalized();
1509
+ const retry = opts?.retry;
1510
+ const throw_errors = opts?.throw ?? true;
1511
+ const find_query = this._init_query(query, false, "query");
1512
+ const base_find = {};
1513
+ if (opts?.projection)
1514
+ base_find.projection = Collection.Projection.init(this._ensure_version_in_projection(opts.projection));
1515
+ if (typeof opts?.timeout === "number")
1516
+ base_find.maxTimeMS = opts.timeout;
1517
+ const find_opts = this.get_operation_options(base_find);
1518
+ try {
1519
+ const doc = await this._with_retry(() => this._col.findOne(find_query, find_opts), opts?.retry);
1520
+ if (!doc) {
1521
+ if (opts?.default) {
1522
+ let default_doc;
1523
+ if (typeof opts.default === "function") {
1524
+ default_doc = vlib.Object.deep_copy(opts.default());
1525
+ } else {
1526
+ default_doc = vlib.Object.deep_copy(opts.default);
1527
+ }
1528
+ if (default_doc._id == null) {
1529
+ default_doc._id = new mongodb.ObjectId();
1530
+ }
1531
+ if (this.record_version != null) {
1532
+ default_doc.__record_version = this.record_version;
1533
+ }
1534
+ let out = default_doc;
1535
+ const is_partial = this._is_partial_projection(opts?.projection);
1536
+ out = await this.apply_on_load(out, {
1537
+ projection: opts?.projection,
1538
+ persist: false,
1539
+ // do not persist defaults.
1540
+ await_persist: true
1541
+ });
1542
+ return out;
1543
+ }
1544
+ const err = new Collection.NotFoundError({
1545
+ message: "Document not found.",
1546
+ query: find_query,
1547
+ reason: "not_found"
1548
+ });
1549
+ if (throw_errors)
1550
+ throw err;
1551
+ return err;
1552
+ }
1553
+ let working = doc;
1554
+ if (opts?.default) {
1555
+ if (typeof opts.default === "function") {
1556
+ Collection.insert_defaults(working, opts.default(), { clone: true });
1557
+ } else {
1558
+ Collection.insert_defaults(working, opts.default, { clone: true });
1559
+ }
1560
+ }
1561
+ working = await this.apply_on_load(working, {
1562
+ projection: opts?.projection,
1563
+ persist: true,
1564
+ await_persist: true
1565
+ });
1566
+ return working;
1567
+ } catch (e) {
1568
+ if (e instanceof Collection.NotFoundError) {
1569
+ if (throw_errors)
1570
+ throw e;
1571
+ return e;
1572
+ }
1573
+ const err = new Collection.LoadError({
1574
+ message: "Load failed due to an unexpected error.",
1575
+ query: find_query,
1576
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1577
+ cause: e
1578
+ });
1579
+ if (throw_errors)
1580
+ throw err;
1581
+ return err;
1582
+ }
1583
+ }
1584
+ /**
1585
+ * Save data with predefined `$set` behaviour.
1586
+ * When the document already exists this function only updates the specified content attributes.
1587
+ * When a document does not exist it will automatically be created, unless `opts.upsert !== false`.
1588
+ *
1589
+ * @param query The database query / path to the document.
1590
+ * @param content The data to save.
1591
+ * @param opts Additional options, see {@link Collection.SetOpts}.
1592
+ *
1593
+ * @note The `opts.throw` option defaults to `true`.
1594
+ * @note The `opts.upsert` option defaults to `true`.
1595
+ * @note `set()` does not deep-merge nested objects.
1596
+ * With `flatten: false`, nested objects are written as whole objects—so their keys are replaced
1597
+ * by exactly what you provide (missing nested keys can be overwritten/removed).
1598
+ * With `flatten: true`, nested objects are flattened into dot notation, allowing you to update specific nested keys without affecting siblings.
1599
+ *
1600
+ * @returns
1601
+ * - When `opts.bulk === true`: an unexecuted bulk operation.
1602
+ * - When `opts.return === true`: the **updated** document; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1603
+ * - Otherwise: `undefined` on success; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1604
+ *
1605
+ * @throws {Collection.SaveError} Only when `opts.throw !== false` and the write fails.
1606
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1607
+ *
1608
+ * @docs
1609
+ */
1610
+ async set(query, content, opts) {
1611
+ if (opts?.flatten)
1612
+ content = this.flatten(content);
1613
+ const operation = { $set: content };
1614
+ return await this.save(query, operation, opts);
1615
+ }
1616
+ /**
1617
+ * Save a single document without performing any default `$set` or `$inc` like operations.
1618
+ * When a document does not exist it will automatically be created unless `opts.upsert === false`.
1619
+ *
1620
+ * @param query The database query / path to the document.
1621
+ * @param operation The MongoDB update document or pipeline (e.g. `{ $set: { key: value } }`).
1622
+ * @param opts Additional options, see {@link Collection.SaveOpts}.
1623
+ *
1624
+ * @note The `opts.throw` option defaults to `true`.
1625
+ * @note The `opts.upsert` option defaults to `true`.
1626
+ * @note Replacement documents are not allowed here. An update operator
1627
+ * document (e.g. `$set`, `$inc`) or an aggregation pipeline is required.
1628
+ * To replace a document use {@link replace}.
1629
+ *
1630
+ * @note This writes the value you pass in—it does not deep-merge nested objects.
1631
+ * If you save a nested object, any nested keys you leave out may be overwritten/removed.
1632
+ * Use `set()` in combination with `flatten: true` to perform deep merges on nested objects without worrying about this behavior.
1633
+ *
1634
+ * @returns
1635
+ * - When `opts.bulk === true`: an unexecuted bulk operation.
1636
+ * - When `opts.return === true`: the **updated** document; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1637
+ * - Otherwise: {@link mongodb.UpdateResult} on success; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1638
+ *
1639
+ * @throws {Collection.SaveError} Only when `opts.throw !== false` and the write fails.
1640
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1641
+ *
1642
+ * @docs
1643
+ */
1644
+ async save(query, operation, opts) {
1645
+ if (!this.initialized) {
1646
+ await this.init();
1647
+ }
1648
+ this.assert_init();
1649
+ this.assert_not_finalized();
1650
+ if (!this._is_operator_update_or_pipeline(operation)) {
1651
+ throw new import_errors.InvalidUsageError({
1652
+ message: "Plain replacement documents are not allowed for 'save()' (uses updateOne/findOneAndUpdate). Pass an operator update or aggregation pipeline. To replace a document, call 'replace()'.",
1653
+ reason: "invalid_update_document",
1654
+ field: "operation"
1655
+ });
1656
+ }
1657
+ const query_op = this._init_query(query, false, "query");
1658
+ const throw_errors = opts?.throw ?? true;
1659
+ const retry = opts?.retry;
1660
+ const upsert = opts?.upsert ?? true;
1661
+ if (this.ttl_enabled && opts?.apply_ttl !== false)
1662
+ this._apply_ttl_to_operation(operation, upsert);
1663
+ if (this.record_version != null)
1664
+ this._apply_record_version_to_operation(operation, upsert);
1665
+ if (opts?.bulk) {
1666
+ const b_op = {
1667
+ updateOne: {
1668
+ filter: query_op,
1669
+ update: operation,
1670
+ upsert
1671
+ }
1672
+ };
1673
+ return b_op;
1674
+ }
1675
+ if (opts?.return) {
1676
+ let res;
1677
+ try {
1678
+ res = await this._with_retry(() => this._col.findOneAndUpdate(query_op, operation, this.get_operation_options({
1679
+ upsert,
1680
+ returnDocument: mongodb.ReturnDocument.AFTER,
1681
+ includeResultMetadata: false,
1682
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
1683
+ })), retry);
1684
+ } catch (e) {
1685
+ const err = new Collection.SaveError({
1686
+ message: "Update failed due to an unexpected error.",
1687
+ query: query_op,
1688
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1689
+ cause: e
1690
+ });
1691
+ if (throw_errors)
1692
+ throw err;
1693
+ return err;
1694
+ }
1695
+ if (!res) {
1696
+ const err = new Collection.SaveError({
1697
+ message: "Document write was not acknowledged.",
1698
+ query: query_op,
1699
+ reason: "not_acknowledged"
1700
+ });
1701
+ if (throw_errors)
1702
+ throw err;
1703
+ return err;
1704
+ }
1705
+ try {
1706
+ const processed = await this.apply_on_load(res, {
1707
+ projection: void 0,
1708
+ persist: true,
1709
+ await_persist: true
1710
+ });
1711
+ return processed;
1712
+ } catch (e) {
1713
+ const err = new Collection.SaveError({
1714
+ message: "Update succeeded but post-load processing failed.",
1715
+ query: query_op,
1716
+ reason: "post_process_failed",
1717
+ cause: e
1718
+ });
1719
+ if (throw_errors)
1720
+ throw err;
1721
+ return err;
1722
+ }
1723
+ } else {
1724
+ let res;
1725
+ try {
1726
+ res = await this._with_retry(() => this._col.updateOne(query_op, operation, this.get_operation_options({
1727
+ upsert,
1728
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
1729
+ })), retry);
1730
+ } catch (e) {
1731
+ const err = new Collection.SaveError({
1732
+ message: "Update failed due to an unexpected error.",
1733
+ query: query_op,
1734
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1735
+ cause: e
1736
+ });
1737
+ if (throw_errors)
1738
+ throw err;
1739
+ return err;
1740
+ }
1741
+ if (!res.acknowledged || res.matchedCount === 0 && res.upsertedCount === 0) {
1742
+ const err = new Collection.SaveError({
1743
+ message: !res.acknowledged ? "Document write was not acknowledged." : "No document matched the filter and no upsert occurred.",
1744
+ query: query_op,
1745
+ reason: !res.acknowledged ? "not_acknowledged" : "no_match"
1746
+ });
1747
+ if (throw_errors)
1748
+ throw err;
1749
+ return err;
1750
+ }
1751
+ return res;
1752
+ }
1753
+ }
1754
+ /**
1755
+ * Save multiple documents without performing any default `$set` or `$inc` operations.
1756
+ * Uses MongoDB `updateMany` (unlike {@link save}, which uses `updateOne`).
1757
+ *
1758
+ * @param query The database query / path to the documents.
1759
+ * @param operation The MongoDB update document or pipeline (e.g. `{ $set: { ... } }`).
1760
+ * @param opts Additional options, see {@link Collection.SaveManyOpts}.
1761
+ *
1762
+ * @note The `opts.throw` option defaults to `true`.
1763
+ * @note The `opts.upsert` option defaults to `false` (unlike {@link save}, which defaults to `true`).
1764
+ * @note When `opts.return` is truthy, this performs a **follow-up** {@link list} with the same `query`
1765
+ * to return the (post-update) documents. This is **less efficient** than `save(..., { return: true })`
1766
+ * because it requires an additional list query after the write.
1767
+ * @note If the follow-up `list()` fails:
1768
+ * - with `opts.throw !== false`, it will throw a {@link Collection.ListError};
1769
+ * - with `opts.throw === false`, it will return a {@link Collection.ListError}.
1770
+ *
1771
+ * @returns
1772
+ * - When `opts.bulk === true`: an unexecuted bulk operation (`{ updateMany: ... }`).
1773
+ * - When `opts.return` is falsy: {@link mongodb.UpdateResult} on success; or a {@link Collection.SaveError} when `throw:false`.
1774
+ * - When `opts.return` is truthy: the matched/updated docs (via `list()`); or
1775
+ * - a {@link Collection.SaveError} when the write fails and `throw:false`, or
1776
+ * - a {@link Collection.ListError} when the follow-up read fails and `throw:false`.
1777
+ *
1778
+ * @throws {Collection.SaveError} Only when `opts.throw !== false` and the write fails.
1779
+ * @throws {Collection.ListError} Only when `opts.throw !== false` and the follow-up list fails.
1780
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or the collection was misused.
1781
+ *
1782
+ * @docs
1783
+ */
1784
+ async save_many(query, operation, opts) {
1785
+ if (!this.initialized) {
1786
+ await this.init();
1787
+ }
1788
+ this.assert_init();
1789
+ this.assert_not_finalized();
1790
+ if (!this._is_operator_update_or_pipeline(operation)) {
1791
+ throw new import_errors.InvalidUsageError({
1792
+ message: "Plain replacement documents are not allowed for 'save_many()' (uses updateMany). Pass an operator update or aggregation pipeline. To replace documents, call 'replace_many()'.",
1793
+ reason: "invalid_update_document",
1794
+ field: "operation"
1795
+ });
1796
+ }
1797
+ const query_op = this._init_query(query, false, "query");
1798
+ const throw_errors = opts?.throw ?? true;
1799
+ const retry = opts?.retry;
1800
+ const upsert = opts?.upsert ?? false;
1801
+ if (this.ttl_enabled && opts?.apply_ttl !== false) {
1802
+ this._apply_ttl_to_operation(operation, upsert);
1803
+ }
1804
+ if (this.record_version != null) {
1805
+ this._apply_record_version_to_operation(operation, upsert);
1806
+ }
1807
+ if (opts?.bulk) {
1808
+ const b_op = {
1809
+ updateMany: {
1810
+ filter: query_op,
1811
+ update: operation,
1812
+ upsert
1813
+ }
1814
+ };
1815
+ return b_op;
1816
+ }
1817
+ let write;
1818
+ try {
1819
+ write = await this._with_retry(() => this._col.updateMany(query_op, operation, this.get_operation_options({
1820
+ upsert,
1821
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
1822
+ })), retry);
1823
+ } catch (e) {
1824
+ const err = new Collection.SaveError({
1825
+ message: "Update-many failed due to an unexpected error.",
1826
+ query: query_op,
1827
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
1828
+ cause: e
1829
+ });
1830
+ if (throw_errors)
1831
+ throw err;
1832
+ return err;
1833
+ }
1834
+ if (!write.acknowledged || write.matchedCount === 0 && write.upsertedCount === 0) {
1835
+ const err = new Collection.SaveError({
1836
+ message: !write.acknowledged ? "Document write was not acknowledged." : "No document matched the filter and no upsert occurred.",
1837
+ query: query_op,
1838
+ reason: !write.acknowledged ? "not_acknowledged" : "no_match"
1839
+ });
1840
+ if (throw_errors)
1841
+ throw err;
1842
+ return err;
1843
+ }
1844
+ if (!opts?.return) {
1845
+ return write;
1846
+ }
1847
+ const follow = typeof opts.return === "object" ? opts.return : {};
1848
+ const out = await this.list(query, {
1849
+ ...follow,
1850
+ // copy control fields from the write options
1851
+ throw: opts.throw,
1852
+ retry: opts.retry,
1853
+ timeout: opts.timeout
1854
+ // Note: we intentionally do NOT set cursor/page_info (they're excluded in SaveManyReturnOpts).
1855
+ });
1856
+ return out;
1857
+ }
1858
+ /**
1859
+ * Build an aggregation replacement pipeline that preserves _id on matches and
1860
+ * applies versioning/TTL consistently with non-pipeline paths.
1861
+ *
1862
+ * - On matches: preserve stored `__record_version` and (for static TTL) stored `__ttl_timestamp`.
1863
+ * - On upserts:
1864
+ * - `__record_version`: respect user value if provided, else stamp `this.record_version`.
1865
+ * - `__ttl_timestamp`:
1866
+ * • sliding TTL → always set to "now"
1867
+ * • static TTL → respect user value if provided, else set to "now"
1868
+ *
1869
+ * @param base_replacement A shallow clone of the user replacement. For replace_many, pass without `_id`.
1870
+ * @param upsert Whether the write is an upsert.
1871
+ * @param apply_ttl Whether TTL logic should be applied (`this.ttl_enabled && opts?.apply_ttl !== false`).
1872
+ * @returns A MongoDB aggregation pipeline that performs the replacement.
1873
+ */
1874
+ _build_replace_pipeline(base_replacement, upsert, apply_ttl) {
1875
+ const now = /* @__PURE__ */ new Date();
1876
+ const merge_objects = [
1877
+ base_replacement,
1878
+ {
1879
+ $cond: [
1880
+ { $ne: ["$_id", null] },
1881
+ { _id: "$_id" },
1882
+ {}
1883
+ ]
1884
+ }
1885
+ ];
1886
+ if (this.record_version != null) {
1887
+ merge_objects.push({ __old_rv: "$__record_version" });
1888
+ }
1889
+ if (apply_ttl) {
1890
+ merge_objects.push({ __old_ttl: "$__ttl_timestamp" });
1891
+ } else {
1892
+ merge_objects.push({ __ttl_timestamp: "$__ttl_timestamp" });
1893
+ }
1894
+ const pipeline = [
1895
+ { $replaceWith: { $mergeObjects: merge_objects } }
1896
+ ];
1897
+ if (this.record_version != null) {
1898
+ pipeline.push({
1899
+ $set: {
1900
+ /**
1901
+ * Matches:
1902
+ * Prefer stored version (`__old_rv`), otherwise keep any user-provided value.
1903
+ * Upserts:
1904
+ * Respect user-provided value if present; otherwise default to `this.record_version`.
1905
+ */
1906
+ __record_version: {
1907
+ $cond: [
1908
+ { $ne: ["$__old_rv", null] },
1909
+ "$__old_rv",
1910
+ upsert ? { $ifNull: ["$__record_version", this.record_version] } : "$__record_version"
1911
+ ]
1912
+ }
1913
+ }
1914
+ });
1915
+ }
1916
+ if (apply_ttl) {
1917
+ pipeline.push({
1918
+ $set: this.sliding_ttl ? { __ttl_timestamp: now } : {
1919
+ __ttl_timestamp: {
1920
+ $cond: [
1921
+ { $ne: ["$__old_ttl", null] },
1922
+ "$__old_ttl",
1923
+ { $ifNull: ["$__ttl_timestamp", now] }
1924
+ ]
1925
+ }
1926
+ }
1927
+ });
1928
+ }
1929
+ pipeline.push({ $unset: ["__old_rv", "__old_ttl"] });
1930
+ return pipeline;
1931
+ }
1932
+ /**
1933
+ * Replace a single document.
1934
+ * Accepts a replacement document only (no update operators/pipelines).
1935
+ *
1936
+ * Internally uses an aggregation pipeline to emulate a full replacement while preserving `_id`
1937
+ * for matched documents and applying record-version/TTL semantics consistently.
1938
+ *
1939
+ * @param query The match filter.
1940
+ * @param replacement The replacement document, no `$` operators.
1941
+ * @param opts Options, see {@link Collection.ReplaceOpts}.
1942
+ *
1943
+ * @note The `opts.throw` option defaults to `true`.
1944
+ * @note The `opts.upsert` option defaults to `true`.
1945
+ * @note TTL semantics:
1946
+ * - When `opts.apply_ttl === false` (or TTL is disabled), the existing TTL is preserved for matched docs.
1947
+ * - With sliding TTL, `__ttl_timestamp` is refreshed on every write.
1948
+ * - With static TTL, matched docs keep their original TTL; upserts receive a fresh timestamp.
1949
+ *
1950
+ * @warning Updating the document id `_id` will cause undefined behaviour on matches. On matched documents,
1951
+ * a user-supplied `_id` is ignored and the existing `_id` is preserved. On true upserts, a
1952
+ * user-supplied `_id` is allowed and will be used by the server.
1953
+ *
1954
+ * @returns
1955
+ * - When `opts.bulk === true`: an unexecuted bulk operation.
1956
+ * - When `opts.return === true`: the **updated** document; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1957
+ * - Otherwise: {@link mongodb.UpdateResult} on success; or a {@link Collection.SaveError} when `throw:false` and a write failure occurs.
1958
+ *
1959
+ * @throws {Collection.SaveError} Only when `opts.throw !== false` and the write fails.
1960
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
1961
+ *
1962
+ * @docs
1963
+ */
1964
+ async replace(query, replacement, opts) {
1965
+ if (!this.initialized) {
1966
+ await this.init();
1967
+ }
1968
+ this.assert_init();
1969
+ this.assert_not_finalized();
1970
+ if (this._is_operator_update_or_pipeline(replacement)) {
1971
+ throw new import_errors.InvalidUsageError({
1972
+ message: "The 'replace()' method accepts a replacement document only (no update operators or pipelines).",
1973
+ reason: "invalid_replacement_document",
1974
+ field: "replacement"
1975
+ });
1976
+ }
1977
+ const query_op = this._init_query(query, false, "query");
1978
+ const throw_errors = opts?.throw ?? true;
1979
+ const retry = opts?.retry;
1980
+ const upsert = opts?.upsert ?? true;
1981
+ const apply_ttl = this.ttl_enabled && opts?.apply_ttl !== false;
1982
+ const base_replacement = { ...replacement };
1983
+ if (upsert === false) {
1984
+ delete base_replacement._id;
1985
+ }
1986
+ const pipeline = this._build_replace_pipeline(base_replacement, upsert, apply_ttl);
1987
+ if (opts?.bulk) {
1988
+ const b_op = {
1989
+ updateOne: {
1990
+ filter: query_op,
1991
+ update: pipeline,
1992
+ upsert
1993
+ }
1994
+ };
1995
+ return b_op;
1996
+ }
1997
+ if (opts?.return) {
1998
+ let res;
1999
+ try {
2000
+ res = await this._with_retry(() => this._col.findOneAndUpdate(query_op, pipeline, this.get_operation_options({
2001
+ upsert,
2002
+ returnDocument: mongodb.ReturnDocument.AFTER,
2003
+ includeResultMetadata: false,
2004
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
2005
+ })), retry);
2006
+ } catch (e) {
2007
+ const err = new Collection.SaveError({
2008
+ message: "Replace failed due to an unexpected error.",
2009
+ query: query_op,
2010
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2011
+ cause: e
2012
+ });
2013
+ if (throw_errors)
2014
+ throw err;
2015
+ return err;
2016
+ }
2017
+ if (!res) {
2018
+ const err = new Collection.SaveError({
2019
+ message: "Document write was not acknowledged.",
2020
+ query: query_op,
2021
+ reason: "not_acknowledged"
2022
+ });
2023
+ if (throw_errors)
2024
+ throw err;
2025
+ return err;
2026
+ }
2027
+ try {
2028
+ const processed = await this.apply_on_load(res, {
2029
+ projection: void 0,
2030
+ persist: true,
2031
+ await_persist: true
2032
+ });
2033
+ return processed;
2034
+ } catch (e) {
2035
+ const err = new Collection.SaveError({
2036
+ message: "Replace succeeded but post-load processing failed.",
2037
+ query: query_op,
2038
+ reason: "post_process_failed",
2039
+ cause: e
2040
+ });
2041
+ if (throw_errors)
2042
+ throw err;
2043
+ return err;
2044
+ }
2045
+ }
2046
+ let write;
2047
+ try {
2048
+ write = await this._with_retry(() => this._col.updateOne(query_op, pipeline, this.get_operation_options({
2049
+ upsert,
2050
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
2051
+ })), retry);
2052
+ } catch (e) {
2053
+ const err = new Collection.SaveError({
2054
+ message: "Replace failed due to an unexpected error.",
2055
+ query: query_op,
2056
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2057
+ cause: e
2058
+ });
2059
+ if (throw_errors)
2060
+ throw err;
2061
+ return err;
2062
+ }
2063
+ if (!write.acknowledged || write.matchedCount === 0 && write.upsertedCount === 0) {
2064
+ const err = new Collection.SaveError({
2065
+ message: !write.acknowledged ? "Document write was not acknowledged." : "No document matched the filter and no upsert occurred.",
2066
+ query: query_op,
2067
+ reason: !write.acknowledged ? "not_acknowledged" : "no_match"
2068
+ });
2069
+ if (throw_errors)
2070
+ throw err;
2071
+ return err;
2072
+ }
2073
+ return write;
2074
+ }
2075
+ /**
2076
+ * Replace multiple documents matched by `query`.
2077
+ * Accepts a **replacement document only** (no update operators or pipelines).
2078
+ *
2079
+ * Internally uses an aggregation pipeline to emulate a full replacement while preserving `_id`
2080
+ * for matched documents and applying record-version/TTL semantics consistently.
2081
+ *
2082
+ * @param query The match filter.
2083
+ * @param replacement The replacement document, no `$` operators.
2084
+ * @param opts Options, see {@link Collection.ReplaceManyOpts}.
2085
+ *
2086
+ * @note The `opts.throw` option defaults to `true`.
2087
+ * @note The `opts.upsert` option defaults to `false` (unlike {@link replace}, which defaults to `true`).
2088
+ * @note When `opts.return` is truthy, this performs a **follow-up** {@link list} with the same `query`
2089
+ * to return the (post-update) documents. This is **less efficient** than `replace(..., { return: true })`
2090
+ * because it requires an additional list query after the write.
2091
+ * @note TTL semantics:
2092
+ * - When `opts.apply_ttl === false` (or TTL is disabled), the existing TTL is preserved for matched docs.
2093
+ * - With sliding TTL, `__ttl_timestamp` is refreshed on every write.
2094
+ * - With static TTL, matched docs keep their original TTL; upserts receive a fresh timestamp.
2095
+ *
2096
+ * @warning The `_id` field is handled with special care:
2097
+ * - Any `_id` present in the `replacement` is **ignored/stripped** for `replace_many`.
2098
+ * This prevents attempts to change immutable ids across multiple documents.
2099
+ * - For matched documents, the existing `_id` is always preserved.
2100
+ * - For true upserts (`opts.upsert === true` when no match occurs), the server will
2101
+ * generate a new `_id`. If you need to upsert with a caller-chosen `_id`, use
2102
+ * {@link replace} (single-document) instead.
2103
+ *
2104
+ * @returns
2105
+ * - When `opts.bulk === true`: an unexecuted bulk operation (`{ updateMany: ... }`).
2106
+ * - When `opts.return` is falsy: {@link mongodb.UpdateResult} on success; or a
2107
+ * {@link Collection.SaveError} when `throw:false` and a write failure occurs.
2108
+ * - When `opts.return` is truthy: the matched/updated docs (via a follow-up {@link list});
2109
+ * or a {@link Collection.SaveError} / {@link Collection.ListError} when `throw:false`.
2110
+ *
2111
+ * @throws {Collection.SaveError} Only when `opts.throw !== false` and the write fails.
2112
+ * @throws {Collection.ListError} Only when `opts.throw !== false` and the follow-up list fails.
2113
+ * @throws {InvalidUsageError} (always) When arguments are invalid or the collection was misused.
2114
+ *
2115
+ * @docs
2116
+ */
2117
+ async replace_many(query, replacement, opts) {
2118
+ if (!this.initialized) {
2119
+ await this.init();
2120
+ }
2121
+ this.assert_init();
2122
+ this.assert_not_finalized();
2123
+ if (this._is_operator_update_or_pipeline(replacement)) {
2124
+ throw new import_errors.InvalidUsageError({
2125
+ message: "The 'replace_many()' method accepts a replacement document only (no update operators or pipelines).",
2126
+ reason: "invalid_replacement_document",
2127
+ field: "replacement"
2128
+ });
2129
+ }
2130
+ const query_op = this._init_query(query, false, "query");
2131
+ const throw_errors = opts?.throw ?? true;
2132
+ const retry = opts?.retry;
2133
+ const upsert = opts?.upsert ?? false;
2134
+ const apply_ttl = this.ttl_enabled && opts?.apply_ttl !== false;
2135
+ const base_replacement = { ...replacement };
2136
+ delete base_replacement._id;
2137
+ const pipeline = this._build_replace_pipeline(base_replacement, upsert, apply_ttl);
2138
+ if (opts?.bulk) {
2139
+ const b_op = {
2140
+ updateMany: {
2141
+ filter: query_op,
2142
+ update: pipeline,
2143
+ upsert
2144
+ }
2145
+ };
2146
+ return b_op;
2147
+ }
2148
+ let write;
2149
+ try {
2150
+ write = await this._with_retry(() => this._col.updateMany(query_op, pipeline, this.get_operation_options({
2151
+ upsert,
2152
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
2153
+ })), retry);
2154
+ } catch (e) {
2155
+ const err = new Collection.SaveError({
2156
+ message: "Replace-many failed due to an unexpected error.",
2157
+ query: query_op,
2158
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2159
+ cause: e
2160
+ });
2161
+ if (throw_errors)
2162
+ throw err;
2163
+ return err;
2164
+ }
2165
+ if (!write.acknowledged || write.matchedCount === 0 && write.upsertedCount === 0) {
2166
+ const err = new Collection.SaveError({
2167
+ message: !write.acknowledged ? "Document write was not acknowledged." : "No document matched the filter and no upsert occurred.",
2168
+ query: query_op,
2169
+ reason: !write.acknowledged ? "not_acknowledged" : "no_match"
2170
+ });
2171
+ if (throw_errors)
2172
+ throw err;
2173
+ return err;
2174
+ }
2175
+ if (!opts?.return) {
2176
+ return write;
2177
+ }
2178
+ const follow = typeof opts.return === "object" ? opts.return : {};
2179
+ const out = await this.list(query, {
2180
+ ...follow,
2181
+ throw: opts.throw,
2182
+ retry: opts.retry,
2183
+ timeout: opts.timeout
2184
+ });
2185
+ return out;
2186
+ }
2187
+ /**
2188
+ * Delete a document of the collection.
2189
+ *
2190
+ * @param query The database query to the document.
2191
+ * @param opts Additional options, see {@link Collection.DeleteOpts}.
2192
+ *
2193
+ * @note The `opts.throw` option defaults to `true`.
2194
+ *
2195
+ * @returns
2196
+ * - An unexecuted bulk operation object if `bulk === true`.
2197
+ * - A {@link Collection.DeleteError} when occurred and `opts.throw === false`.
2198
+ * - A {@link mongodb.DeleteResult}.
2199
+ *
2200
+ * @throws {Collection.DeleteError} When `opts.throw !== false` and if the deletion was not acknowledged, this does not check against the deleted document count.
2201
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2202
+ *
2203
+ * @docs
2204
+ */
2205
+ async delete(query, opts) {
2206
+ if (!this.initialized) {
2207
+ await this.init();
2208
+ }
2209
+ this.assert_init();
2210
+ this.assert_not_finalized();
2211
+ const throw_errors = opts?.throw ?? true;
2212
+ const query_op = this._init_query(query, false, "query");
2213
+ if (opts != null && opts.bulk) {
2214
+ const b_op = {
2215
+ deleteOne: {
2216
+ filter: query_op
2217
+ }
2218
+ };
2219
+ return b_op;
2220
+ } else {
2221
+ let res;
2222
+ try {
2223
+ res = await this._with_retry(() => this._col.deleteOne(query_op, this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
2224
+ } catch (e) {
2225
+ const err = new Collection.DeleteError({
2226
+ message: `Failed to delete document(s) in collection "${this.name}".`,
2227
+ query: query_op,
2228
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2229
+ cause: e
2230
+ });
2231
+ if (throw_errors)
2232
+ throw err;
2233
+ return err;
2234
+ }
2235
+ if (!res.acknowledged) {
2236
+ const err = new Collection.DeleteError({
2237
+ message: `Failed to delete document(s) in collection "${this.name}".`,
2238
+ query: query_op,
2239
+ reason: "not_acknowledged"
2240
+ });
2241
+ if (throw_errors)
2242
+ throw err;
2243
+ return err;
2244
+ }
2245
+ return res;
2246
+ }
2247
+ }
2248
+ /**
2249
+ * Delete multiple documents matching the query.
2250
+ *
2251
+ * @param query The database query to the document(s).
2252
+ * @param opts Additional options, see {@link Collection.DeleteOpts}.
2253
+ * @param allow_empty_query When `true`, allows an empty query (i.e. `{}`) to be passed, which would otherwise throw an error.
2254
+ *
2255
+ * @note The `opts.throw` option defaults to `true`.
2256
+ *
2257
+ * @returns
2258
+ * - An unexecuted bulk operation object if `bulk === true`.
2259
+ * - A {@link Collection.DeleteError} when occurred and `opts.throw == false`.
2260
+ * - A {@link mongodb.DeleteResult}.
2261
+ *
2262
+ * @throws {Collection.DeleteError} When `opts.throw !== false` and if the deletion was not acknowledged, this does not check against the deleted document count.
2263
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2264
+ *
2265
+ * @docs
2266
+ */
2267
+ async delete_many(query, opts, allow_empty_query = false) {
2268
+ if (!this.initialized) {
2269
+ await this.init();
2270
+ }
2271
+ this.assert_init();
2272
+ this.assert_not_finalized();
2273
+ const throw_errors = opts?.throw ?? true;
2274
+ const query_op = this._init_query(query, allow_empty_query, "query");
2275
+ if (opts != null && opts.bulk) {
2276
+ const b_op = {
2277
+ deleteMany: {
2278
+ filter: query_op
2279
+ }
2280
+ };
2281
+ return b_op;
2282
+ } else {
2283
+ let res;
2284
+ try {
2285
+ res = await this._with_retry(() => this._col.deleteMany(query_op, this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
2286
+ } catch (e) {
2287
+ const err = new Collection.DeleteError({
2288
+ message: `Failed to delete document(s) in collection "${this.name}".`,
2289
+ query: query_op,
2290
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2291
+ cause: e
2292
+ });
2293
+ if (throw_errors)
2294
+ throw err;
2295
+ return err;
2296
+ }
2297
+ if (!res.acknowledged) {
2298
+ const err = new Collection.DeleteError({
2299
+ message: `Failed to delete document(s) in collection "${this.name}".`,
2300
+ query: query_op,
2301
+ reason: "not_acknowledged"
2302
+ });
2303
+ if (throw_errors)
2304
+ throw err;
2305
+ return err;
2306
+ }
2307
+ return res;
2308
+ }
2309
+ }
2310
+ /**
2311
+ * Delete all documents in the collection.
2312
+ *
2313
+ * @param opts Additional options, see {@link Collection.DeleteOpts}.
2314
+ *
2315
+ * @note The `opts.throw` option defaults to `true`.
2316
+ *
2317
+ * @returns
2318
+ * - An unexecuted bulk operation object if `bulk === true`.
2319
+ * - A {@link Collection.DeleteError} when occurred and `opts.throw == false`.
2320
+ * - A {@link mongodb.DeleteResult}.
2321
+ *
2322
+ * @throws {Collection.DeleteError} When `opts.throw !== false` and if the deletion was not acknowledged, this does not check against the deleted document count.
2323
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2324
+ *
2325
+ * @docs
2326
+ */
2327
+ async delete_all(opts) {
2328
+ return this.delete_many({}, opts, true);
2329
+ }
2330
+ /**
2331
+ * Delete all documents from the collection and drop the collection.
2332
+ *
2333
+ * @note This function is not supported for transaction based collections.
2334
+ *
2335
+ * @param opts Additional options, see {@link Collection.DeleteOpts}.
2336
+ *
2337
+ * @note The `opts.throw` option defaults to `true`.
2338
+ *
2339
+ * @returns
2340
+ * - A {@link Collection.DeleteError} when occurred and `opts.throw === false`.
2341
+ * - Undefined upon success.
2342
+ *
2343
+ * @throws {Collection.DeleteError} When `opts.throw !== false` and if the deletion was not acknowledged, this does not check against the deleted document count.
2344
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2345
+ *
2346
+ * @docs
2347
+ */
2348
+ async delete_collection(opts) {
2349
+ if (!this.initialized) {
2350
+ await this.init();
2351
+ }
2352
+ this.assert_init();
2353
+ this.assert_not_finalized();
2354
+ this.assert_not_transaction_based();
2355
+ const throw_errors = opts?.throw ?? true;
2356
+ let res;
2357
+ try {
2358
+ res = await this._with_retry(() => this._col.drop(this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
2359
+ } catch (e) {
2360
+ if (e && typeof e === "object" && (e?.code === 26 || e?.codeName === "NamespaceNotFound")) {
2361
+ return void 0;
2362
+ }
2363
+ const err = new Collection.DeleteError({
2364
+ message: `Failed to drop collection "${this.name}".`,
2365
+ query: {},
2366
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2367
+ cause: e
2368
+ });
2369
+ if (throw_errors)
2370
+ throw err;
2371
+ return err;
2372
+ }
2373
+ if (!res) {
2374
+ const err = new Collection.DeleteError({
2375
+ message: `Failed to drop collection "${this.name}", detected by a falsy return.`,
2376
+ query: {},
2377
+ reason: "not_acknowledged"
2378
+ });
2379
+ if (throw_errors)
2380
+ throw err;
2381
+ return err;
2382
+ }
2383
+ }
2384
+ // /**
2385
+ // * @todo implement
2386
+ // * Enhanced bulk operations with retry logic for failed operations
2387
+ // * @param operations - Array of bulk write operations
2388
+ // * @param retries - Number of retry attempts for failed operations. Set to -1 to disable retries. Default is 3.
2389
+ // * @returns Simplified BulkWriteResult with aggregated counts from all attempts
2390
+ // */
2391
+ // async bulk_operations(
2392
+ // operations: any[] = [],
2393
+ // retries: number = 3
2394
+ // ): Promise<{
2395
+ // ok: boolean;
2396
+ // inserted_count: number;
2397
+ // matched_count: number;
2398
+ // modified_count: number;
2399
+ // deleted_count: number;
2400
+ // upserted_count: number;
2401
+ // upserted_ids: { [key: number]: any };
2402
+ // inserted_ids: { [key: number]: any };
2403
+ // failed_operations: number[];
2404
+ // errors?: any[];
2405
+ // }> {
2406
+ // if (!this.initialized) { await this.init(); }
2407
+ // this.assert_init();
2408
+ // // Validate operations
2409
+ // if (!Array.isArray(operations)) {
2410
+ // throw new TypeError('Operations must be an array');
2411
+ // }
2412
+ // // Return early for empty operations
2413
+ // if (operations.length === 0) {
2414
+ // return {
2415
+ // ok: true,
2416
+ // inserted_count: 0,
2417
+ // matched_count: 0,
2418
+ // modified_count: 0,
2419
+ // deleted_count: 0,
2420
+ // upserted_count: 0,
2421
+ // upserted_ids: {},
2422
+ // inserted_ids: {},
2423
+ // failed_operations: []
2424
+ // };
2425
+ // }
2426
+ // // MongoDB bulk write limit
2427
+ // const MAX_BATCH_SIZE = 100000;
2428
+ // if (operations.length > MAX_BATCH_SIZE) {
2429
+ // throw new Error(`Bulk operations exceed MongoDB limit of ${MAX_BATCH_SIZE}. Please batch your operations.`);
2430
+ // }
2431
+ // // Initialize aggregated results
2432
+ // const aggregated_result = {
2433
+ // ok: true,
2434
+ // inserted_count: 0,
2435
+ // matched_count: 0,
2436
+ // modified_count: 0,
2437
+ // deleted_count: 0,
2438
+ // upserted_count: 0,
2439
+ // upserted_ids: {} as { [key: number]: any },
2440
+ // inserted_ids: {} as { [key: number]: any },
2441
+ // failed_operations: [] as number[],
2442
+ // errors: [] as any[]
2443
+ // };
2444
+ // // Track operation status (true = succeeded, false = failed/pending)
2445
+ // const operation_status: Map<number, boolean> = new Map();
2446
+ // operations.forEach((_, index) => operation_status.set(index, false));
2447
+ // // Track latest errors for each operation (will be cleared if operation succeeds)
2448
+ // const latest_errors: Map<number, any> = new Map();
2449
+ // // Track operations that need to be executed
2450
+ // let pending_operations = operations.map((op, index) => ({ op, original_index: index }));
2451
+ // let attempt_count = 0;
2452
+ // const max_attempts = retries < 0 ? 1 : retries + 1;
2453
+ // while (pending_operations.length > 0 && attempt_count < max_attempts) {
2454
+ // attempt_count++;
2455
+ // try {
2456
+ // // Execute bulk operations
2457
+ // const result = await this._col.bulkWrite(
2458
+ // pending_operations.map(item => item.op),
2459
+ // { ordered: false } // Use unordered for better error handling
2460
+ // );
2461
+ // // Track which operations succeeded in this attempt
2462
+ // const succeeded_in_this_attempt = new Set<number>();
2463
+ // // Aggregate successful results
2464
+ // aggregated_result.inserted_count += result.insertedCount;
2465
+ // aggregated_result.matched_count += result.matchedCount;
2466
+ // aggregated_result.modified_count += result.modifiedCount;
2467
+ // aggregated_result.deleted_count += result.deletedCount;
2468
+ // aggregated_result.upserted_count += result.upsertedCount;
2469
+ // // Map inserted/upserted IDs back to original indices
2470
+ // if (result.insertedIds && typeof result.insertedIds === 'object') {
2471
+ // for (const [key, value] of Object.entries(result.insertedIds)) {
2472
+ // const idx = parseInt(key);
2473
+ // if (!isNaN(idx) && pending_operations[idx]) {
2474
+ // const original_index = pending_operations[idx].original_index;
2475
+ // aggregated_result.inserted_ids[original_index] = value;
2476
+ // succeeded_in_this_attempt.add(original_index);
2477
+ // }
2478
+ // }
2479
+ // }
2480
+ // if (result.upsertedIds && typeof result.upsertedIds === 'object') {
2481
+ // for (const [key, value] of Object.entries(result.upsertedIds)) {
2482
+ // const idx = parseInt(key);
2483
+ // if (!isNaN(idx) && pending_operations[idx]) {
2484
+ // const original_index = pending_operations[idx].original_index;
2485
+ // aggregated_result.upserted_ids[original_index] = value;
2486
+ // succeeded_in_this_attempt.add(original_index);
2487
+ // }
2488
+ // }
2489
+ // }
2490
+ // // Check for write errors
2491
+ // const write_errors = result.hasWriteErrors?.() ? result.getWriteErrors() : [];
2492
+ // if (write_errors.length > 0) {
2493
+ // aggregated_result.ok = false;
2494
+ // // Track failed operations by their indices in current batch
2495
+ // const failed_indices_in_batch = new Set(write_errors.map(err => err.index));
2496
+ // // Update errors for failed operations
2497
+ // for (const error of write_errors) {
2498
+ // if (error.index < pending_operations.length) {
2499
+ // const original_index = pending_operations[error.index].original_index;
2500
+ // latest_errors.set(original_index, {
2501
+ // ...error,
2502
+ // index: original_index,
2503
+ // attempt: attempt_count,
2504
+ // timestamp: new Date().toISOString()
2505
+ // });
2506
+ // }
2507
+ // }
2508
+ // // Mark operations as succeeded if they weren't in the error list
2509
+ // pending_operations.forEach((item, batch_index) => {
2510
+ // if (!failed_indices_in_batch.has(batch_index)) {
2511
+ // const original_index = item.original_index;
2512
+ // operation_status.set(original_index, true);
2513
+ // succeeded_in_this_attempt.add(original_index);
2514
+ // // Clear any previous errors for this operation
2515
+ // latest_errors.delete(original_index);
2516
+ // }
2517
+ // });
2518
+ // // Filter pending operations to only include failed ones
2519
+ // if (retries >= 0 && attempt_count < max_attempts) {
2520
+ // pending_operations = pending_operations.filter((_, index) => failed_indices_in_batch.has(index));
2521
+ // // Add exponential backoff for retries
2522
+ // if (pending_operations.length > 0) {
2523
+ // const delay = Math.min(1000 * Math.pow(2, attempt_count - 1), 5000);
2524
+ // await new Promise(resolve => setTimeout(resolve, delay));
2525
+ // }
2526
+ // } else {
2527
+ // // No more retries, exit
2528
+ // break;
2529
+ // }
2530
+ // } else {
2531
+ // // All operations in this batch succeeded
2532
+ // pending_operations.forEach(item => {
2533
+ // operation_status.set(item.original_index, true);
2534
+ // succeeded_in_this_attempt.add(item.original_index);
2535
+ // // Clear any previous errors for these operations
2536
+ // latest_errors.delete(item.original_index);
2537
+ // });
2538
+ // pending_operations = [];
2539
+ // }
2540
+ // // Log successful recoveries for monitoring
2541
+ // if (attempt_count > 1 && succeeded_in_this_attempt.size > 0) {
2542
+ // console.log(`[BulkOps] Recovered ${succeeded_in_this_attempt.size} operations on attempt ${attempt_count}`);
2543
+ // }
2544
+ // } catch (error: any) {
2545
+ // aggregated_result.ok = false;
2546
+ // // Track error for all pending operations
2547
+ // const affected_indices = pending_operations.map(item => item.original_index);
2548
+ // for (const original_index of affected_indices) {
2549
+ // latest_errors.set(original_index, {
2550
+ // message: error.message || 'Unknown error',
2551
+ // code: error.code,
2552
+ // attempt: attempt_count,
2553
+ // index: original_index,
2554
+ // timestamp: new Date().toISOString(),
2555
+ // type: 'batch_error'
2556
+ // });
2557
+ // }
2558
+ // // If retries are disabled or we've exhausted retries, throw
2559
+ // if (retries < 0 || attempt_count >= max_attempts) {
2560
+ // break;
2561
+ // }
2562
+ // // Add exponential backoff before retry
2563
+ // const delay = Math.min(1000 * Math.pow(2, attempt_count - 1), 5000);
2564
+ // await new Promise(resolve => setTimeout(resolve, delay));
2565
+ // }
2566
+ // }
2567
+ // // Final reconciliation: determine which operations ultimately failed
2568
+ // aggregated_result.failed_operations = [];
2569
+ // aggregated_result.errors = [];
2570
+ // for (const [index, succeeded] of operation_status.entries()) {
2571
+ // if (!succeeded) {
2572
+ // aggregated_result.failed_operations.push(index);
2573
+ // const error = latest_errors.get(index);
2574
+ // if (error) {
2575
+ // aggregated_result.errors.push(error);
2576
+ // }
2577
+ // }
2578
+ // }
2579
+ // // Sort failed operations for consistency
2580
+ // aggregated_result.failed_operations.sort((a, b) => a - b);
2581
+ // // Clean up errors array if empty
2582
+ // if (aggregated_result.errors.length === 0) {
2583
+ // delete (aggregated_result as any).errors;
2584
+ // }
2585
+ // // If we still have failed operations after all retries, include detailed error
2586
+ // if (aggregated_result.failed_operations.length > 0) {
2587
+ // const error = new Error(
2588
+ // `Bulk operations partially failed: ${aggregated_result.failed_operations.length} of ${operations.length} operations could not be completed after ${attempt_count} attempts. ` +
2589
+ // `Successfully processed: ${operations.length - aggregated_result.failed_operations.length} operations.`
2590
+ // );
2591
+ // (error as any).aggregated_result = aggregated_result;
2592
+ // (error as any).retry_attempts = attempt_count;
2593
+ // (error as any).success_rate = ((operations.length - aggregated_result.failed_operations.length) / operations.length * 100).toFixed(2) + '%';
2594
+ // // Only throw if all operations failed
2595
+ // if (aggregated_result.failed_operations.length === operations.length) {
2596
+ // throw error;
2597
+ // }
2598
+ // // Log partial failure for monitoring
2599
+ // console.warn(`[BulkOps] Partial failure:`, (error as any).success_rate, 'success rate');
2600
+ // } else {
2601
+ // // Clean up failed operations array.
2602
+ // delete (aggregated_result as any).failed_operations;
2603
+ // }
2604
+ // return aggregated_result;
2605
+ // }
2606
+ /**
2607
+ * Execute bulk write operations.
2608
+ *
2609
+ * @param operations Array of bulk write operations.
2610
+ * @param opts Additional options, see {@link Collection.BulkOpts}
2611
+ *
2612
+ * @note The `opts.throw` option defaults to `true`.
2613
+ *
2614
+ * @returns
2615
+ * - A {@link Collection.BulkError} if occurred and `opts.throw === false`.
2616
+ * - A {@link mongodb.BulkWriteResult}.
2617
+ *
2618
+ * @throws {Collection.BulkError} When `opts.throw !== false` and if the bulk operation failed, this does not check against the bulk write result (this may change in the future).
2619
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2620
+ *
2621
+ * @docs
2622
+ */
2623
+ async bulk_operations(operations, opts) {
2624
+ if (!this.initialized) {
2625
+ await this.init();
2626
+ }
2627
+ this.assert_init();
2628
+ this.assert_not_finalized();
2629
+ if (!Array.isArray(operations)) {
2630
+ throw new TypeError("Operations must be an array");
2631
+ }
2632
+ if (operations.length > 1e5) {
2633
+ throw new import_errors.InvalidUsageError({
2634
+ message: "Bulk operations exceed MongoDB limit of 100000",
2635
+ reason: "invalid_operations_length"
2636
+ });
2637
+ }
2638
+ const throw_errors = opts?.throw ?? true;
2639
+ if (this.ttl_enabled || this.record_version != null) {
2640
+ const now = /* @__PURE__ */ new Date();
2641
+ for (const op of operations) {
2642
+ if (this.record_version != null) {
2643
+ const rv = this.record_version;
2644
+ if (op.insertOne?.document && typeof op.insertOne.document === "object") {
2645
+ const d = op.insertOne.document;
2646
+ if (d.__record_version == null) {
2647
+ d.__record_version = rv;
2648
+ }
2649
+ } else if (op.replaceOne?.replacement && typeof op.replaceOne.replacement === "object") {
2650
+ if (op.replaceOne.upsert) {
2651
+ const d = op.replaceOne.replacement;
2652
+ if (d.__record_version == null) {
2653
+ d.__record_version = rv;
2654
+ }
2655
+ }
2656
+ } else if (op.updateOne?.update) {
2657
+ if (op.updateOne.upsert)
2658
+ this._apply_record_version_to_operation(op.updateOne.update, true);
2659
+ } else if (op.updateMany?.update) {
2660
+ if (op.updateMany.upsert)
2661
+ this._apply_record_version_to_operation(op.updateMany.update, true);
2662
+ }
2663
+ }
2664
+ if (!this.ttl_enabled)
2665
+ continue;
2666
+ if (op.insertOne?.document && typeof op.insertOne.document === "object") {
2667
+ if (this.sliding_ttl || op.insertOne.document.__ttl_timestamp == null) {
2668
+ op.insertOne.document.__ttl_timestamp = now;
2669
+ }
2670
+ continue;
2671
+ }
2672
+ if (op.replaceOne?.replacement && typeof op.replaceOne.replacement === "object") {
2673
+ if (this.sliding_ttl) {
2674
+ op.replaceOne.replacement.__ttl_timestamp = now;
2675
+ } else if (op.replaceOne.upsert && op.replaceOne.replacement.__ttl_timestamp == null) {
2676
+ op.replaceOne.replacement.__ttl_timestamp = now;
2677
+ }
2678
+ continue;
2679
+ }
2680
+ if (op.updateOne?.update) {
2681
+ this._apply_ttl_to_operation(op.updateOne.update, op.updateOne.upsert);
2682
+ continue;
2683
+ }
2684
+ if (op.updateMany?.update) {
2685
+ this._apply_ttl_to_operation(op.updateMany.update, op.updateMany.upsert);
2686
+ continue;
2687
+ }
2688
+ }
2689
+ }
2690
+ try {
2691
+ return await this._with_retry(() => this._col.bulkWrite(operations, this.get_operation_options({
2692
+ ordered: true,
2693
+ ...typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {}
2694
+ })), opts?.retry);
2695
+ } catch (e) {
2696
+ const err = new Collection.BulkError({
2697
+ message: "Bulk operations failed due to an unexpected error.",
2698
+ query: {},
2699
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2700
+ cause: e
2701
+ });
2702
+ if (throw_errors)
2703
+ throw err;
2704
+ return err;
2705
+ }
2706
+ }
2707
+ /**
2708
+ * Execute an aggregation pipeline.
2709
+ *
2710
+ * @param pipeline MongoDB aggregation pipeline stages.
2711
+ * @param opts Aggregation options, see {@link Collection.AggregateOpts}
2712
+ *
2713
+ * @note The `opts.throw` option defaults to `true`.
2714
+ * @note This method does not execute the {@link Collection.Opts.on_load}
2715
+ * and {@link Collection.Opts.on_transform_version} callbacks.
2716
+ *
2717
+ * @returns
2718
+ * - A {@link Collection.AggregateError} if occurred and `opts.throw === false`.
2719
+ * - An {@link mongodb.AggregationCursor} if `opts.cursor === true`.
2720
+ * - An array of document results.
2721
+ *
2722
+ * @throws {Collection.AggregateError} When `opts.throw !== false` and if the aggregate operation failed, this does not check against the aggregate result (this may change in the future).
2723
+ * @throws {InvalidUsageError} (always) When the provided argument(s) are invalid or if the collection was not used properly.
2724
+ *
2725
+ * @docs
2726
+ */
2727
+ async aggregate(pipeline, opts) {
2728
+ if (!this.initialized) {
2729
+ await this.init();
2730
+ }
2731
+ this.assert_init();
2732
+ this.assert_not_finalized();
2733
+ const throw_errors = opts?.throw ?? true;
2734
+ try {
2735
+ const cursor = await this._with_retry(() => this._col.aggregate(pipeline, this.get_operation_options(typeof opts?.timeout === "number" ? { maxTimeMS: opts.timeout } : {})), opts?.retry);
2736
+ if (typeof opts?.timeout === "number" && typeof cursor.maxTimeMS === "function") {
2737
+ cursor.maxTimeMS(opts.timeout);
2738
+ }
2739
+ if (opts?.cursor)
2740
+ return cursor;
2741
+ const arr = await this._with_retry(() => cursor.toArray(), opts?.retry);
2742
+ return arr;
2743
+ } catch (e) {
2744
+ const err = new Collection.AggregateError({
2745
+ message: "Aggregate operation failed due to an unexpected error.",
2746
+ query: {},
2747
+ reason: this._should_retry_error(e) ? Collection.Retry.get_attempts(opts?.retry) > 1 ? "retries_exhausted" : "retryable" : "unknown",
2748
+ cause: e
2749
+ });
2750
+ if (throw_errors)
2751
+ throw err;
2752
+ return err;
2753
+ }
2754
+ }
2755
+ /**
2756
+ * Clean a document from all default system attributes.
2757
+ * @param doc The document to clean.
2758
+ * @returns The cleaned document without system attributes.
2759
+ *
2760
+ * @docs
2761
+ */
2762
+ clean(doc) {
2763
+ if (doc == null) {
2764
+ return doc;
2765
+ }
2766
+ if (typeof doc === "object") {
2767
+ const out = { ...doc };
2768
+ delete out._id;
2769
+ delete out._path;
2770
+ if (out.__ttl_timestamp != null) {
2771
+ delete out.__ttl_timestamp;
2772
+ }
2773
+ if (out.__record_version != null) {
2774
+ delete out.__record_version;
2775
+ }
2776
+ return out;
2777
+ }
2778
+ return doc;
2779
+ }
2780
+ // ---------------------------------------------------------
2781
+ // Sessions & transactions.
2782
+ // ---------------------------------------------------------
2783
+ /**
2784
+ * Start a new transaction by creating a TransactionCollection instance.
2785
+ * @returns A new TransactionCollection instance with transaction capabilities.
2786
+ *
2787
+ * @docs
2788
+ */
2789
+ async start_transaction() {
2790
+ if (!this.db.client) {
2791
+ throw new import_errors.InvalidUsageError({
2792
+ message: "Database client is not initialized, ensure the parent 'volt.Server' is initialized.",
2793
+ reason: "client_not_connected"
2794
+ });
2795
+ }
2796
+ if (!this.initialized) {
2797
+ await this.init();
2798
+ }
2799
+ this.assert_init();
2800
+ return new TransactionCollection({
2801
+ derived_collection: this,
2802
+ transaction_based: true
2803
+ });
2804
+ }
2805
+ // ------------------- DEPRECATED -------------------------
2806
+ /** Prepare a _path based regex operation. @deprecated */
2807
+ prepare_path_regex_filter(path) {
2808
+ while (path.length > 0 && path.charAt(path.length - 1) === "/") {
2809
+ path = path.substring(0, path.length - 1);
2810
+ }
2811
+ if (path.length == 0) {
2812
+ throw new import_errors.InvalidUsageError({
2813
+ message: `Invalid path '${path}'`,
2814
+ reason: "invalid_path"
2815
+ });
2816
+ }
2817
+ if (path.length > 1e3) {
2818
+ throw new import_errors.InvalidUsageError({
2819
+ message: `Path too long (${path.length})`,
2820
+ reason: "invalid_path"
2821
+ });
2822
+ }
2823
+ const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2824
+ const filter = {
2825
+ _path: {
2826
+ $regex: `^${escapeRegExp(path)}/`
2827
+ // $options: 'i' // Case insensitive for consistency
2828
+ }
2829
+ };
2830
+ return filter;
2831
+ }
2832
+ }
2833
+ (function(Collection2) {
2834
+ let Retry;
2835
+ (function(Retry2) {
2836
+ function get_attempts(retry) {
2837
+ return Math.max(1, Math.min(100, typeof retry === "number" ? retry : !retry ? 1 : retry.attempts));
2838
+ }
2839
+ Retry2.get_attempts = get_attempts;
2840
+ function normalize(retry) {
2841
+ const base = typeof retry === "number" ? { attempts: retry } : typeof retry === "object" ? retry ?? { attempts: 1 } : { attempts: 1 };
2842
+ let attempts = Number(base.attempts);
2843
+ if (!Number.isFinite(attempts))
2844
+ attempts = 1;
2845
+ attempts = Math.max(1, Math.min(100, attempts));
2846
+ const initial_delay = base.initial_delay ?? 100;
2847
+ const max_delay = base.max_delay ?? 1e3;
2848
+ const backoff_factor = base.backoff_factor ?? 2;
2849
+ const jitter_ratio = 0.2;
2850
+ return {
2851
+ attempts,
2852
+ initial_delay,
2853
+ max_delay,
2854
+ backoff_factor,
2855
+ jitter_ratio
2856
+ };
2857
+ }
2858
+ Retry2.normalize = normalize;
2859
+ function compute_backoff_delay(attempt_index, params) {
2860
+ const base = Math.min(params.max_delay, (params.initial_delay <= 0 ? 0 : params.initial_delay) * Math.pow(Math.max(1, params.backoff_factor), attempt_index));
2861
+ if (base <= 0)
2862
+ return 0;
2863
+ const jitter = (Math.random() * 2 - 1) * (params.jitter_ratio * base);
2864
+ const delay = Math.max(0, Math.min(params.max_delay, base + jitter));
2865
+ return Math.floor(delay);
2866
+ }
2867
+ Retry2.compute_backoff_delay = compute_backoff_delay;
2868
+ })(Retry = Collection2.Retry || (Collection2.Retry = {}));
2869
+ class OperationError extends Error {
2870
+ /** The error message. */
2871
+ message;
2872
+ query;
2873
+ reason;
2874
+ /** An optional error that caused this error. */
2875
+ cause;
2876
+ /** Construct a not found error. */
2877
+ constructor(opts) {
2878
+ super(opts.message);
2879
+ this.message = opts.message;
2880
+ this.name = "OperationError";
2881
+ this.query = opts.query;
2882
+ this.reason = opts.reason;
2883
+ this.cause = opts.cause;
2884
+ Object.setPrototypeOf(this, new.target.prototype);
2885
+ }
2886
+ }
2887
+ Collection2.OperationError = OperationError;
2888
+ class NotFoundError extends OperationError {
2889
+ /**
2890
+ * Constructor method.
2891
+ * @param opts The error options, see {@link OperationError.Opts}.
2892
+ */
2893
+ constructor(opts) {
2894
+ super(opts);
2895
+ this.name = "NotFoundError";
2896
+ Object.setPrototypeOf(this, new.target.prototype);
2897
+ }
2898
+ }
2899
+ Collection2.NotFoundError = NotFoundError;
2900
+ class OnTransformError extends OperationError {
2901
+ /**
2902
+ * Constructor method.
2903
+ * @param opts The error options, see {@link OperationError.Opts}.
2904
+ */
2905
+ constructor(opts) {
2906
+ super(opts);
2907
+ this.name = "OnTransformError";
2908
+ Object.setPrototypeOf(this, new.target.prototype);
2909
+ }
2910
+ }
2911
+ Collection2.OnTransformError = OnTransformError;
2912
+ class OnLoadError extends OperationError {
2913
+ /**
2914
+ * Constructor method.
2915
+ * @param opts The error options, see {@link OperationError.Opts}.
2916
+ */
2917
+ constructor(opts) {
2918
+ super(opts);
2919
+ this.name = "OnLoadError";
2920
+ Object.setPrototypeOf(this, new.target.prototype);
2921
+ }
2922
+ }
2923
+ Collection2.OnLoadError = OnLoadError;
2924
+ class CountError extends OperationError {
2925
+ /**
2926
+ * Construct a {@link CountError}.
2927
+ * @param opts The error options, see {@link OperationError.Opts}.
2928
+ */
2929
+ constructor(opts) {
2930
+ super(opts);
2931
+ this.name = "CountError";
2932
+ Object.setPrototypeOf(this, new.target.prototype);
2933
+ }
2934
+ }
2935
+ Collection2.CountError = CountError;
2936
+ class ListError extends OperationError {
2937
+ /**
2938
+ * Constructor method.
2939
+ * @param opts The error options, see {@link OperationError.Opts}.
2940
+ */
2941
+ constructor(opts) {
2942
+ super(opts);
2943
+ this.name = "ListError";
2944
+ Object.setPrototypeOf(this, new.target.prototype);
2945
+ }
2946
+ }
2947
+ Collection2.ListError = ListError;
2948
+ class ExistsError extends OperationError {
2949
+ /**
2950
+ * Constructor method.
2951
+ * @param opts The error options, see {@link OperationError.Opts}.
2952
+ */
2953
+ constructor(opts) {
2954
+ super(opts);
2955
+ this.name = "ExistsError";
2956
+ Object.setPrototypeOf(this, new.target.prototype);
2957
+ }
2958
+ }
2959
+ Collection2.ExistsError = ExistsError;
2960
+ class LoadError extends OperationError {
2961
+ /**
2962
+ * Constructor method.
2963
+ * @param opts The error options, see {@link OperationError.Opts}.
2964
+ */
2965
+ constructor(opts) {
2966
+ super(opts);
2967
+ this.name = "LoadError";
2968
+ Object.setPrototypeOf(this, new.target.prototype);
2969
+ }
2970
+ }
2971
+ Collection2.LoadError = LoadError;
2972
+ class SaveError extends OperationError {
2973
+ /**
2974
+ * Constructor method.
2975
+ * @param opts The error options, see {@link OperationError.Opts}.
2976
+ */
2977
+ constructor(opts) {
2978
+ super(opts);
2979
+ this.name = "SaveError";
2980
+ Object.setPrototypeOf(this, new.target.prototype);
2981
+ }
2982
+ }
2983
+ Collection2.SaveError = SaveError;
2984
+ class DeleteError extends OperationError {
2985
+ /**
2986
+ * Constructor method.
2987
+ * @param opts The error options, see {@link OperationError.Opts}.
2988
+ */
2989
+ constructor(opts) {
2990
+ super(opts);
2991
+ this.name = "DeleteError";
2992
+ Object.setPrototypeOf(this, new.target.prototype);
2993
+ }
2994
+ }
2995
+ Collection2.DeleteError = DeleteError;
2996
+ class BulkError extends OperationError {
2997
+ /**
2998
+ * Constructor method.
2999
+ * @param opts The error options, see {@link OperationError.Opts}.
3000
+ */
3001
+ constructor(opts) {
3002
+ super(opts);
3003
+ this.name = "BulkError";
3004
+ Object.setPrototypeOf(this, new.target.prototype);
3005
+ }
3006
+ }
3007
+ Collection2.BulkError = BulkError;
3008
+ class AggregateError extends OperationError {
3009
+ /**
3010
+ * Constructor method.
3011
+ * @param opts The error options, see {@link OperationError.Opts}.
3012
+ */
3013
+ constructor(opts) {
3014
+ super(opts);
3015
+ this.name = "AggregateError";
3016
+ Object.setPrototypeOf(this, new.target.prototype);
3017
+ }
3018
+ }
3019
+ Collection2.AggregateError = AggregateError;
3020
+ let Projection;
3021
+ (function(Projection2) {
3022
+ function init(projection) {
3023
+ if (Array.isArray(projection)) {
3024
+ const p = {};
3025
+ for (let i = 0; i < projection.length; i++) {
3026
+ p[projection[i]] = 1;
3027
+ }
3028
+ return p;
3029
+ } else {
3030
+ const p = projection;
3031
+ let has_include = false;
3032
+ let has_exclude = false;
3033
+ for (const [k, v] of Object.entries(p)) {
3034
+ if (v === 1 || v === true) {
3035
+ if (k !== "_id")
3036
+ has_include = true;
3037
+ } else if (v === 0 || v === false) {
3038
+ if (k !== "_id")
3039
+ has_exclude = true;
3040
+ } else {
3041
+ throw new import_errors.InvalidUsageError({
3042
+ message: `Invalid projection value for "${k}": expected 0, 1, true or false.`,
3043
+ reason: "invalid_projection"
3044
+ });
3045
+ }
3046
+ if (has_include && has_exclude) {
3047
+ throw new import_errors.InvalidUsageError({
3048
+ message: "Invalid projection: cannot mix inclusion and exclusion (except for _id).",
3049
+ reason: "invalid_projection"
3050
+ });
3051
+ }
3052
+ }
3053
+ return p;
3054
+ }
3055
+ }
3056
+ Projection2.init = init;
3057
+ })(Projection = Collection2.Projection || (Collection2.Projection = {}));
3058
+ {
3059
+ }
3060
+ })(Collection || (Collection = {}));
3061
+ class TransactionCollection extends Collection {
3062
+ /**
3063
+ * Commit the current transaction.
3064
+ * Implements retry logic for transient errors and unknown commit results.
3065
+ * @throws {InvalidUsageError} If there is no active session or if the transaction has already been finalized.
3066
+ * @throws {Error} If the commit fails after retries or encounters a non-retryable error.
3067
+ *
3068
+ * @docs
3069
+ */
3070
+ async commit() {
3071
+ const session = this._session;
3072
+ if (!session) {
3073
+ throw new import_errors.InvalidUsageError({
3074
+ message: "No active session for this transaction.",
3075
+ reason: "no_session"
3076
+ });
3077
+ }
3078
+ if (this.is_finalized_transaction) {
3079
+ throw new import_errors.InvalidUsageError({
3080
+ message: "Transaction has already been finalized.",
3081
+ reason: "transaction_finalized"
3082
+ });
3083
+ }
3084
+ const max_retries_unknown = 10;
3085
+ const base_delay_ms = 20;
3086
+ const max_delay_ms = 1e3;
3087
+ for (let attempt = 0; attempt <= max_retries_unknown; attempt++) {
3088
+ try {
3089
+ await session.commitTransaction();
3090
+ this.is_finalized_transaction = true;
3091
+ try {
3092
+ await session.endSession();
3093
+ } finally {
3094
+ this._session = void 0;
3095
+ }
3096
+ return;
3097
+ } catch (err) {
3098
+ const has_label = (label) => {
3099
+ if (!err || typeof err !== "object") {
3100
+ return false;
3101
+ }
3102
+ if (typeof err?.hasErrorLabel === "function") {
3103
+ try {
3104
+ return !!err.hasErrorLabel(label);
3105
+ } catch {
3106
+ }
3107
+ }
3108
+ return Array.isArray(err?.errorLabels) && err.errorLabels.includes(label);
3109
+ };
3110
+ const unknown_commit = has_label("UnknownTransactionCommitResult");
3111
+ const transient = has_label("TransientTransactionError");
3112
+ const is_networkish = err?.name === "MongoNetworkError" || err?.name === "MongoNetworkTimeoutError";
3113
+ if ((unknown_commit || is_networkish) && attempt < max_retries_unknown) {
3114
+ const delay = Math.min(max_delay_ms, base_delay_ms * Math.pow(2, attempt));
3115
+ await new Promise((res) => setTimeout(res, delay));
3116
+ continue;
3117
+ }
3118
+ if (transient) {
3119
+ try {
3120
+ await session.abortTransaction();
3121
+ } catch {
3122
+ }
3123
+ this.is_finalized_transaction = true;
3124
+ try {
3125
+ await session.endSession();
3126
+ } finally {
3127
+ this._session = void 0;
3128
+ }
3129
+ const e = new Error(`TransientTransactionError during commit; transaction aborted. Retry the entire transaction. ${err?.message ?? ""}`);
3130
+ e.codeName = err?.codeName;
3131
+ e.errorLabels = err?.errorLabels;
3132
+ throw e;
3133
+ }
3134
+ if ((unknown_commit || is_networkish) && attempt >= max_retries_unknown) {
3135
+ this.is_finalized_transaction = true;
3136
+ try {
3137
+ await session.endSession();
3138
+ } finally {
3139
+ this._session = void 0;
3140
+ }
3141
+ const e = new Error(`Commit failed after ${attempt + 1} attempt(s) with unknown outcome; last error: ${err?.message ?? err}`);
3142
+ e.codeName = err?.codeName;
3143
+ e.errorLabels = err?.errorLabels;
3144
+ throw e;
3145
+ }
3146
+ this.is_finalized_transaction = true;
3147
+ try {
3148
+ await session.endSession();
3149
+ } finally {
3150
+ this._session = void 0;
3151
+ }
3152
+ throw err;
3153
+ }
3154
+ }
3155
+ }
3156
+ /**
3157
+ * Abort the current transaction.
3158
+ * Implements retry logic for transient errors.
3159
+ * @throws {InvalidUsageError} If there is no active session or if the transaction has already been finalized.
3160
+ * @throws {Error} If the abort fails after retries or encounters a non-retryable error.
3161
+ *
3162
+ * @docs
3163
+ */
3164
+ async abort() {
3165
+ const session = this._session;
3166
+ if (!session) {
3167
+ throw new import_errors.InvalidUsageError({
3168
+ message: "No active session for this transaction.",
3169
+ reason: "no_session"
3170
+ });
3171
+ }
3172
+ if (this.is_finalized_transaction) {
3173
+ throw new import_errors.InvalidUsageError({
3174
+ message: "Transaction has already been finalized.",
3175
+ reason: "transaction_finalized"
3176
+ });
3177
+ }
3178
+ const max_retries = 5;
3179
+ const base_delay_ms = 20;
3180
+ const max_delay_ms = 500;
3181
+ for (let attempt = 0; attempt <= max_retries; attempt++) {
3182
+ try {
3183
+ await session.abortTransaction();
3184
+ this.is_finalized_transaction = true;
3185
+ try {
3186
+ await session.endSession();
3187
+ } finally {
3188
+ this._session = void 0;
3189
+ }
3190
+ return;
3191
+ } catch (err) {
3192
+ if (err?.codeName === "NoSuchTransaction") {
3193
+ this.is_finalized_transaction = true;
3194
+ try {
3195
+ await session.endSession();
3196
+ } finally {
3197
+ this._session = void 0;
3198
+ }
3199
+ return;
3200
+ }
3201
+ const has_label = (label) => {
3202
+ if (!err || typeof err !== "object") {
3203
+ return false;
3204
+ }
3205
+ if (typeof err?.hasErrorLabel === "function") {
3206
+ try {
3207
+ return !!err.hasErrorLabel(label);
3208
+ } catch {
3209
+ }
3210
+ }
3211
+ return Array.isArray(err?.errorLabels) && err.errorLabels.includes(label);
3212
+ };
3213
+ const transient = has_label("TransientTransactionError");
3214
+ const is_networkish = err?.name === "MongoNetworkError" || err?.name === "MongoNetworkTimeoutError";
3215
+ if ((transient || is_networkish) && attempt < max_retries) {
3216
+ const delay = Math.min(max_delay_ms, base_delay_ms * Math.pow(2, attempt));
3217
+ await new Promise((res) => setTimeout(res, delay));
3218
+ continue;
3219
+ }
3220
+ this.is_finalized_transaction = true;
3221
+ try {
3222
+ await session.endSession();
3223
+ } finally {
3224
+ this._session = void 0;
3225
+ }
3226
+ throw err;
3227
+ }
3228
+ }
3229
+ }
3230
+ /**
3231
+ * Cleanup method for proper resource management
3232
+ * Can be called manually or via async disposal
3233
+ *
3234
+ * @warning This method aborts the transaction if it is still active.
3235
+ * @docs
3236
+ */
3237
+ async cleanup() {
3238
+ if (this._session && !this.is_finalized_transaction) {
3239
+ try {
3240
+ await this.abort();
3241
+ } catch (error) {
3242
+ console.error("Failed to abort transaction during cleanup:", error);
3243
+ if (this._session) {
3244
+ try {
3245
+ await this._session.endSession();
3246
+ } catch (endError) {
3247
+ console.error("Failed to end session during cleanup:", endError);
3248
+ }
3249
+ }
3250
+ } finally {
3251
+ this.is_finalized_transaction = true;
3252
+ }
3253
+ }
3254
+ }
3255
+ // Support for async disposal (TC39 proposal)
3256
+ async [Symbol.asyncDispose]() {
3257
+ await this.cleanup();
3258
+ }
3259
+ /**
3260
+ * Check if the transaction is still active (not finalized).
3261
+ * @returns True if the transaction is active, false otherwise.
3262
+ * @docs
3263
+ */
3264
+ is_active() {
3265
+ return this.is_transaction && !this.is_finalized_transaction && this._session != null;
3266
+ }
3267
+ }
3268
+ async function test_save() {
3269
+ const res = void await test_col.save(
3270
+ { uid: "" },
3271
+ // @ts-ignore
3272
+ { uid: "" },
3273
+ { return: true }
3274
+ );
3275
+ const res_no_throw = await test_col.save(
3276
+ { uid: "" },
3277
+ // @ts-ignore
3278
+ { uid: "" },
3279
+ { return: true, throw: false, bulk: false }
3280
+ );
3281
+ function init_save_opts(opts) {
3282
+ return opts;
3283
+ }
3284
+ const a = init_save_opts({ bulk: true, upsert: true });
3285
+ const b = init_save_opts({ return: false, upsert: true });
3286
+ const b2 = init_save_opts({ return: false, throw: true });
3287
+ const c = init_save_opts({ return: true, upsert: false, throw: false });
3288
+ const e = init_save_opts({ return: true, upsert: true, bulk: true });
3289
+ const res_bulk_op = await test_col.save({ uid: "" }, { uid: "" }, { bulk: true });
3290
+ const res_undef = await test_col.save({ uid: "" }, { uid: "" });
3291
+ const res_doc = await test_col.save({ uid: "" }, { uid: "" }, { return: true });
3292
+ const res_doc_or_undef = await test_col.save({ uid: "" }, { uid: "" }, { return: true, throw: false, upsert: false });
3293
+ async function save_wrapper(doc, bulk) {
3294
+ return await test_col.save({ id: "test" }, { $set: doc }, { bulk });
3295
+ }
3296
+ }
3297
+ // Annotate the CommonJS export names for ESM import in node:
3298
+ 0 && (module.exports = {
3299
+ Collection,
3300
+ TransactionCollection
3301
+ });