spindb 0.37.2 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (811) hide show
  1. package/dist/cli/bin.js +9 -0
  2. package/dist/cli/bin.js.map +1 -0
  3. package/dist/cli/commands/attach.js +102 -0
  4. package/dist/cli/commands/attach.js.map +1 -0
  5. package/dist/cli/commands/backup.js +197 -0
  6. package/dist/cli/commands/backup.js.map +1 -0
  7. package/dist/cli/commands/backups.js +190 -0
  8. package/dist/cli/commands/backups.js.map +1 -0
  9. package/dist/cli/commands/clone.js +119 -0
  10. package/dist/cli/commands/clone.js.map +1 -0
  11. package/dist/cli/commands/config.js +276 -0
  12. package/dist/cli/commands/config.js.map +1 -0
  13. package/dist/cli/commands/connect.js +559 -0
  14. package/dist/cli/commands/connect.js.map +1 -0
  15. package/dist/cli/commands/create.js +952 -0
  16. package/dist/cli/commands/create.js.map +1 -0
  17. package/dist/cli/commands/databases.js +485 -0
  18. package/dist/cli/commands/databases.js.map +1 -0
  19. package/dist/cli/commands/delete.js +106 -0
  20. package/dist/cli/commands/delete.js.map +1 -0
  21. package/dist/cli/commands/deps.js +238 -0
  22. package/dist/cli/commands/deps.js.map +1 -0
  23. package/dist/cli/commands/detach.js +81 -0
  24. package/dist/cli/commands/detach.js.map +1 -0
  25. package/dist/cli/commands/doctor.js +567 -0
  26. package/dist/cli/commands/doctor.js.map +1 -0
  27. package/dist/cli/commands/duckdb.js +207 -0
  28. package/dist/cli/commands/duckdb.js.map +1 -0
  29. package/dist/cli/commands/edit.js +524 -0
  30. package/dist/cli/commands/edit.js.map +1 -0
  31. package/dist/cli/commands/engines.js +1414 -0
  32. package/dist/cli/commands/engines.js.map +1 -0
  33. package/dist/cli/commands/export.js +383 -0
  34. package/dist/cli/commands/export.js.map +1 -0
  35. package/dist/cli/commands/info.js +270 -0
  36. package/dist/cli/commands/info.js.map +1 -0
  37. package/dist/cli/commands/list.js +215 -0
  38. package/dist/cli/commands/list.js.map +1 -0
  39. package/dist/cli/commands/logs.js +81 -0
  40. package/dist/cli/commands/logs.js.map +1 -0
  41. package/dist/cli/commands/menu/backup-handlers.js +1202 -0
  42. package/dist/cli/commands/menu/backup-handlers.js.map +1 -0
  43. package/dist/cli/commands/menu/container-handlers.js +1788 -0
  44. package/dist/cli/commands/menu/container-handlers.js.map +1 -0
  45. package/dist/cli/commands/menu/engine-handlers.js +235 -0
  46. package/dist/cli/commands/menu/engine-handlers.js.map +1 -0
  47. package/dist/cli/commands/menu/index.js +266 -0
  48. package/dist/cli/commands/menu/index.js.map +1 -0
  49. package/dist/cli/commands/menu/settings-handlers.js +320 -0
  50. package/dist/cli/commands/menu/settings-handlers.js.map +1 -0
  51. package/dist/cli/commands/menu/shared.js +13 -0
  52. package/dist/cli/commands/menu/shared.js.map +1 -0
  53. package/dist/cli/commands/menu/shell-handlers.js +1573 -0
  54. package/dist/cli/commands/menu/shell-handlers.js.map +1 -0
  55. package/dist/cli/commands/menu/sql-handlers.js +185 -0
  56. package/dist/cli/commands/menu/sql-handlers.js.map +1 -0
  57. package/dist/cli/commands/menu/update-handlers.js +322 -0
  58. package/dist/cli/commands/menu/update-handlers.js.map +1 -0
  59. package/dist/cli/commands/menu/validators.js +9 -0
  60. package/dist/cli/commands/menu/validators.js.map +1 -0
  61. package/dist/cli/commands/ports.js +166 -0
  62. package/dist/cli/commands/ports.js.map +1 -0
  63. package/dist/cli/commands/pull.js +166 -0
  64. package/dist/cli/commands/pull.js.map +1 -0
  65. package/dist/cli/commands/query.js +180 -0
  66. package/dist/cli/commands/query.js.map +1 -0
  67. package/dist/cli/commands/restore.js +428 -0
  68. package/dist/cli/commands/restore.js.map +1 -0
  69. package/dist/cli/commands/run.js +115 -0
  70. package/dist/cli/commands/run.js.map +1 -0
  71. package/dist/cli/commands/self-update.js +99 -0
  72. package/dist/cli/commands/self-update.js.map +1 -0
  73. package/dist/cli/commands/sqlite.js +207 -0
  74. package/dist/cli/commands/sqlite.js.map +1 -0
  75. package/dist/cli/commands/start.js +196 -0
  76. package/dist/cli/commands/start.js.map +1 -0
  77. package/dist/cli/commands/stop.js +182 -0
  78. package/dist/cli/commands/stop.js.map +1 -0
  79. package/dist/cli/commands/url.js +88 -0
  80. package/dist/cli/commands/url.js.map +1 -0
  81. package/dist/cli/commands/users.js +189 -0
  82. package/dist/cli/commands/users.js.map +1 -0
  83. package/dist/cli/commands/version.js +52 -0
  84. package/dist/cli/commands/version.js.map +1 -0
  85. package/dist/cli/commands/which.js +258 -0
  86. package/dist/cli/commands/which.js.map +1 -0
  87. package/dist/cli/constants.js +212 -0
  88. package/dist/cli/constants.js.map +1 -0
  89. package/dist/cli/helpers.js +1120 -0
  90. package/dist/cli/helpers.js.map +1 -0
  91. package/dist/cli/index.js +146 -0
  92. package/dist/cli/index.js.map +1 -0
  93. package/dist/cli/ui/prompts.js +1002 -0
  94. package/dist/cli/ui/prompts.js.map +1 -0
  95. package/dist/cli/ui/spinner.js +74 -0
  96. package/dist/cli/ui/spinner.js.map +1 -0
  97. package/dist/cli/ui/theme.js +99 -0
  98. package/dist/cli/ui/theme.js.map +1 -0
  99. package/dist/cli/utils/file-follower.js +79 -0
  100. package/dist/cli/utils/file-follower.js.map +1 -0
  101. package/dist/config/backup-formats.js +363 -0
  102. package/dist/config/backup-formats.js.map +1 -0
  103. package/dist/config/defaults.js +25 -0
  104. package/dist/config/defaults.js.map +1 -0
  105. package/dist/config/engine-defaults.js +303 -0
  106. package/dist/config/engine-defaults.js.map +1 -0
  107. package/dist/config/engines-registry.js +103 -0
  108. package/dist/config/engines-registry.js.map +1 -0
  109. package/dist/config/os-dependencies.js +767 -0
  110. package/dist/config/os-dependencies.js.map +1 -0
  111. package/dist/config/paths.js +156 -0
  112. package/dist/config/paths.js.map +1 -0
  113. package/dist/config/version.js +3 -0
  114. package/dist/config/version.js.map +1 -0
  115. package/dist/core/backup-restore.js +219 -0
  116. package/dist/core/backup-restore.js.map +1 -0
  117. package/dist/core/base-binary-manager.js +403 -0
  118. package/dist/core/base-binary-manager.js.map +1 -0
  119. package/dist/core/base-document-binary-manager.js +364 -0
  120. package/dist/core/base-document-binary-manager.js.map +1 -0
  121. package/dist/core/base-embedded-binary-manager.js +364 -0
  122. package/dist/core/base-embedded-binary-manager.js.map +1 -0
  123. package/dist/core/base-server-binary-manager.js +368 -0
  124. package/dist/core/base-server-binary-manager.js.map +1 -0
  125. package/dist/core/config-manager.js +495 -0
  126. package/dist/core/config-manager.js.map +1 -0
  127. package/dist/core/container-manager.js +609 -0
  128. package/dist/core/container-manager.js.map +1 -0
  129. package/dist/core/credential-generator.js +67 -0
  130. package/dist/core/credential-generator.js.map +1 -0
  131. package/dist/core/credential-manager.js +211 -0
  132. package/dist/core/credential-manager.js.map +1 -0
  133. package/dist/core/dblab-utils.js +105 -0
  134. package/dist/core/dblab-utils.js.map +1 -0
  135. package/dist/core/dependency-manager.js +359 -0
  136. package/dist/core/dependency-manager.js.map +1 -0
  137. package/dist/core/docker-exporter.js +1077 -0
  138. package/dist/core/docker-exporter.js.map +1 -0
  139. package/dist/core/error-handler.js +295 -0
  140. package/dist/core/error-handler.js.map +1 -0
  141. package/dist/core/fs-error-utils.js +74 -0
  142. package/dist/core/fs-error-utils.js.map +1 -0
  143. package/dist/core/homebrew-version-manager.js +280 -0
  144. package/dist/core/homebrew-version-manager.js.map +1 -0
  145. package/dist/core/hostdb-client.js +252 -0
  146. package/dist/core/hostdb-client.js.map +1 -0
  147. package/dist/core/hostdb-metadata.js +243 -0
  148. package/dist/core/hostdb-metadata.js.map +1 -0
  149. package/dist/core/hostdb-releases-factory.js +161 -0
  150. package/dist/core/hostdb-releases-factory.js.map +1 -0
  151. package/dist/core/library-env.js +88 -0
  152. package/dist/core/library-env.js.map +1 -0
  153. package/dist/core/pgweb-utils.js +53 -0
  154. package/dist/core/pgweb-utils.js.map +1 -0
  155. package/dist/core/platform-service.js +632 -0
  156. package/dist/core/platform-service.js.map +1 -0
  157. package/dist/core/port-manager.js +136 -0
  158. package/dist/core/port-manager.js.map +1 -0
  159. package/dist/core/process-manager.js +445 -0
  160. package/dist/core/process-manager.js.map +1 -0
  161. package/dist/core/pull-manager.js +418 -0
  162. package/dist/core/pull-manager.js.map +1 -0
  163. package/dist/core/query-parser.js +449 -0
  164. package/dist/core/query-parser.js.map +1 -0
  165. package/dist/core/spawn-utils.js +90 -0
  166. package/dist/core/spawn-utils.js.map +1 -0
  167. package/dist/core/start-with-retry.js +90 -0
  168. package/dist/core/start-with-retry.js.map +1 -0
  169. package/dist/core/test-cleanup.js +85 -0
  170. package/dist/core/test-cleanup.js.map +1 -0
  171. package/dist/core/tls-generator.js +84 -0
  172. package/dist/core/tls-generator.js.map +1 -0
  173. package/dist/core/transaction-manager.js +139 -0
  174. package/dist/core/transaction-manager.js.map +1 -0
  175. package/dist/core/update-manager.js +241 -0
  176. package/dist/core/update-manager.js.map +1 -0
  177. package/dist/core/version-migration.js +260 -0
  178. package/dist/core/version-migration.js.map +1 -0
  179. package/dist/core/version-utils.js +91 -0
  180. package/dist/core/version-utils.js.map +1 -0
  181. package/dist/engines/base-engine.js +179 -0
  182. package/dist/engines/base-engine.js.map +1 -0
  183. package/dist/engines/clickhouse/backup.js +289 -0
  184. package/dist/engines/clickhouse/backup.js.map +1 -0
  185. package/dist/engines/clickhouse/binary-manager.js +145 -0
  186. package/dist/engines/clickhouse/binary-manager.js.map +1 -0
  187. package/dist/engines/clickhouse/binary-urls.js +100 -0
  188. package/dist/engines/clickhouse/binary-urls.js.map +1 -0
  189. package/dist/engines/clickhouse/cli-utils.js +143 -0
  190. package/dist/engines/clickhouse/cli-utils.js.map +1 -0
  191. package/dist/engines/clickhouse/hostdb-releases.js +24 -0
  192. package/dist/engines/clickhouse/hostdb-releases.js.map +1 -0
  193. package/dist/engines/clickhouse/index.js +1077 -0
  194. package/dist/engines/clickhouse/index.js.map +1 -0
  195. package/dist/engines/clickhouse/restore.js +335 -0
  196. package/dist/engines/clickhouse/restore.js.map +1 -0
  197. package/dist/engines/clickhouse/version-maps.js +83 -0
  198. package/dist/engines/clickhouse/version-maps.js.map +1 -0
  199. package/dist/engines/clickhouse/version-validator.js +133 -0
  200. package/dist/engines/clickhouse/version-validator.js.map +1 -0
  201. package/dist/engines/cockroachdb/backup.js +261 -0
  202. package/dist/engines/cockroachdb/backup.js.map +1 -0
  203. package/dist/engines/cockroachdb/binary-manager.js +33 -0
  204. package/dist/engines/cockroachdb/binary-manager.js.map +1 -0
  205. package/dist/engines/cockroachdb/binary-urls.js +33 -0
  206. package/dist/engines/cockroachdb/binary-urls.js.map +1 -0
  207. package/dist/engines/cockroachdb/cli-utils.js +338 -0
  208. package/dist/engines/cockroachdb/cli-utils.js.map +1 -0
  209. package/dist/engines/cockroachdb/hostdb-releases.js +21 -0
  210. package/dist/engines/cockroachdb/hostdb-releases.js.map +1 -0
  211. package/dist/engines/cockroachdb/index.js +1016 -0
  212. package/dist/engines/cockroachdb/index.js.map +1 -0
  213. package/dist/engines/cockroachdb/restore.js +323 -0
  214. package/dist/engines/cockroachdb/restore.js.map +1 -0
  215. package/dist/engines/cockroachdb/version-maps.js +37 -0
  216. package/dist/engines/cockroachdb/version-maps.js.map +1 -0
  217. package/dist/engines/couchdb/api-client.js +64 -0
  218. package/dist/engines/couchdb/api-client.js.map +1 -0
  219. package/dist/engines/couchdb/backup.js +90 -0
  220. package/dist/engines/couchdb/backup.js.map +1 -0
  221. package/dist/engines/couchdb/binary-manager.js +62 -0
  222. package/dist/engines/couchdb/binary-manager.js.map +1 -0
  223. package/dist/engines/couchdb/binary-urls.js +92 -0
  224. package/dist/engines/couchdb/binary-urls.js.map +1 -0
  225. package/dist/engines/couchdb/hostdb-releases.js +21 -0
  226. package/dist/engines/couchdb/hostdb-releases.js.map +1 -0
  227. package/dist/engines/couchdb/index.js +1043 -0
  228. package/dist/engines/couchdb/index.js.map +1 -0
  229. package/dist/engines/couchdb/restore.js +198 -0
  230. package/dist/engines/couchdb/restore.js.map +1 -0
  231. package/dist/engines/couchdb/version-maps.js +67 -0
  232. package/dist/engines/couchdb/version-maps.js.map +1 -0
  233. package/dist/engines/couchdb/version-validator.js +88 -0
  234. package/dist/engines/couchdb/version-validator.js.map +1 -0
  235. package/dist/engines/duckdb/binary-manager.js +33 -0
  236. package/dist/engines/duckdb/binary-manager.js.map +1 -0
  237. package/{engines/duckdb/binary-urls.ts → dist/engines/duckdb/binary-urls.js} +11 -16
  238. package/dist/engines/duckdb/binary-urls.js.map +1 -0
  239. package/dist/engines/duckdb/hostdb-releases.js +21 -0
  240. package/dist/engines/duckdb/hostdb-releases.js.map +1 -0
  241. package/dist/engines/duckdb/index.js +594 -0
  242. package/dist/engines/duckdb/index.js.map +1 -0
  243. package/dist/engines/duckdb/registry.js +265 -0
  244. package/dist/engines/duckdb/registry.js.map +1 -0
  245. package/dist/engines/duckdb/scanner.js +12 -0
  246. package/dist/engines/duckdb/scanner.js.map +1 -0
  247. package/dist/engines/duckdb/version-maps.js +67 -0
  248. package/dist/engines/duckdb/version-maps.js.map +1 -0
  249. package/dist/engines/duckdb/version-validator.js +62 -0
  250. package/dist/engines/duckdb/version-validator.js.map +1 -0
  251. package/dist/engines/ferretdb/backup.js +170 -0
  252. package/dist/engines/ferretdb/backup.js.map +1 -0
  253. package/dist/engines/ferretdb/binary-manager.js +765 -0
  254. package/dist/engines/ferretdb/binary-manager.js.map +1 -0
  255. package/dist/engines/ferretdb/binary-urls.js +135 -0
  256. package/dist/engines/ferretdb/binary-urls.js.map +1 -0
  257. package/dist/engines/ferretdb/index.js +1517 -0
  258. package/dist/engines/ferretdb/index.js.map +1 -0
  259. package/dist/engines/ferretdb/restore.js +310 -0
  260. package/dist/engines/ferretdb/restore.js.map +1 -0
  261. package/{engines/ferretdb/version-maps.ts → dist/engines/ferretdb/version-maps.js} +62 -79
  262. package/dist/engines/ferretdb/version-maps.js.map +1 -0
  263. package/dist/engines/file-based-utils.js +184 -0
  264. package/dist/engines/file-based-utils.js.map +1 -0
  265. package/dist/engines/index.js +124 -0
  266. package/dist/engines/index.js.map +1 -0
  267. package/dist/engines/influxdb/api-client.js +54 -0
  268. package/dist/engines/influxdb/api-client.js.map +1 -0
  269. package/dist/engines/influxdb/backup.js +119 -0
  270. package/dist/engines/influxdb/backup.js.map +1 -0
  271. package/dist/engines/influxdb/binary-manager.js +87 -0
  272. package/dist/engines/influxdb/binary-manager.js.map +1 -0
  273. package/dist/engines/influxdb/binary-urls.js +56 -0
  274. package/dist/engines/influxdb/binary-urls.js.map +1 -0
  275. package/dist/engines/influxdb/hostdb-releases.js +21 -0
  276. package/dist/engines/influxdb/hostdb-releases.js.map +1 -0
  277. package/dist/engines/influxdb/index.js +962 -0
  278. package/dist/engines/influxdb/index.js.map +1 -0
  279. package/dist/engines/influxdb/restore.js +329 -0
  280. package/dist/engines/influxdb/restore.js.map +1 -0
  281. package/dist/engines/influxdb/version-maps.js +64 -0
  282. package/dist/engines/influxdb/version-maps.js.map +1 -0
  283. package/dist/engines/influxdb/version-validator.js +109 -0
  284. package/dist/engines/influxdb/version-validator.js.map +1 -0
  285. package/dist/engines/mariadb/backup.js +178 -0
  286. package/dist/engines/mariadb/backup.js.map +1 -0
  287. package/dist/engines/mariadb/binary-manager.js +33 -0
  288. package/dist/engines/mariadb/binary-manager.js.map +1 -0
  289. package/{engines/mariadb/binary-urls.ts → dist/engines/mariadb/binary-urls.js} +38 -55
  290. package/dist/engines/mariadb/binary-urls.js.map +1 -0
  291. package/dist/engines/mariadb/hostdb-releases.js +21 -0
  292. package/dist/engines/mariadb/hostdb-releases.js.map +1 -0
  293. package/dist/engines/mariadb/index.js +1011 -0
  294. package/dist/engines/mariadb/index.js.map +1 -0
  295. package/dist/engines/mariadb/restore.js +322 -0
  296. package/dist/engines/mariadb/restore.js.map +1 -0
  297. package/dist/engines/mariadb/version-maps.js +63 -0
  298. package/dist/engines/mariadb/version-maps.js.map +1 -0
  299. package/dist/engines/mariadb/version-validator.js +143 -0
  300. package/dist/engines/mariadb/version-validator.js.map +1 -0
  301. package/dist/engines/meilisearch/api-client.js +50 -0
  302. package/dist/engines/meilisearch/api-client.js.map +1 -0
  303. package/dist/engines/meilisearch/backup.js +167 -0
  304. package/dist/engines/meilisearch/backup.js.map +1 -0
  305. package/dist/engines/meilisearch/binary-manager.js +31 -0
  306. package/dist/engines/meilisearch/binary-manager.js.map +1 -0
  307. package/dist/engines/meilisearch/binary-urls.js +56 -0
  308. package/dist/engines/meilisearch/binary-urls.js.map +1 -0
  309. package/dist/engines/meilisearch/hostdb-releases.js +21 -0
  310. package/dist/engines/meilisearch/hostdb-releases.js.map +1 -0
  311. package/dist/engines/meilisearch/index.js +992 -0
  312. package/dist/engines/meilisearch/index.js.map +1 -0
  313. package/dist/engines/meilisearch/restore.js +167 -0
  314. package/dist/engines/meilisearch/restore.js.map +1 -0
  315. package/dist/engines/meilisearch/version-maps.js +67 -0
  316. package/dist/engines/meilisearch/version-maps.js.map +1 -0
  317. package/dist/engines/meilisearch/version-validator.js +109 -0
  318. package/dist/engines/meilisearch/version-validator.js.map +1 -0
  319. package/dist/engines/mongodb/backup.js +109 -0
  320. package/dist/engines/mongodb/backup.js.map +1 -0
  321. package/dist/engines/mongodb/binary-manager.js +36 -0
  322. package/dist/engines/mongodb/binary-manager.js.map +1 -0
  323. package/dist/engines/mongodb/binary-urls.js +46 -0
  324. package/dist/engines/mongodb/binary-urls.js.map +1 -0
  325. package/dist/engines/mongodb/cli-utils.js +131 -0
  326. package/dist/engines/mongodb/cli-utils.js.map +1 -0
  327. package/dist/engines/mongodb/hostdb-releases.js +77 -0
  328. package/dist/engines/mongodb/hostdb-releases.js.map +1 -0
  329. package/dist/engines/mongodb/index.js +873 -0
  330. package/dist/engines/mongodb/index.js.map +1 -0
  331. package/dist/engines/mongodb/restore.js +276 -0
  332. package/dist/engines/mongodb/restore.js.map +1 -0
  333. package/dist/engines/mongodb/version-maps.js +79 -0
  334. package/dist/engines/mongodb/version-maps.js.map +1 -0
  335. package/dist/engines/mongodb/version-validator.js +133 -0
  336. package/dist/engines/mongodb/version-validator.js.map +1 -0
  337. package/dist/engines/mysql/backup.js +210 -0
  338. package/dist/engines/mysql/backup.js.map +1 -0
  339. package/dist/engines/mysql/binary-detection.js +325 -0
  340. package/dist/engines/mysql/binary-detection.js.map +1 -0
  341. package/dist/engines/mysql/binary-manager.js +30 -0
  342. package/dist/engines/mysql/binary-manager.js.map +1 -0
  343. package/dist/engines/mysql/binary-urls.js +87 -0
  344. package/dist/engines/mysql/binary-urls.js.map +1 -0
  345. package/{engines/mysql/hostdb-releases.ts → dist/engines/mysql/hostdb-releases.js} +20 -23
  346. package/dist/engines/mysql/hostdb-releases.js.map +1 -0
  347. package/dist/engines/mysql/index.js +1066 -0
  348. package/dist/engines/mysql/index.js.map +1 -0
  349. package/dist/engines/mysql/restore.js +361 -0
  350. package/dist/engines/mysql/restore.js.map +1 -0
  351. package/dist/engines/mysql/version-maps.js +79 -0
  352. package/dist/engines/mysql/version-maps.js.map +1 -0
  353. package/dist/engines/mysql/version-validator.js +266 -0
  354. package/dist/engines/mysql/version-validator.js.map +1 -0
  355. package/dist/engines/postgresql/backup.js +118 -0
  356. package/dist/engines/postgresql/backup.js.map +1 -0
  357. package/dist/engines/postgresql/binary-manager.js +85 -0
  358. package/dist/engines/postgresql/binary-manager.js.map +1 -0
  359. package/dist/engines/postgresql/binary-urls.js +80 -0
  360. package/dist/engines/postgresql/binary-urls.js.map +1 -0
  361. package/dist/engines/postgresql/hostdb-releases.js +21 -0
  362. package/dist/engines/postgresql/hostdb-releases.js.map +1 -0
  363. package/dist/engines/postgresql/index.js +852 -0
  364. package/dist/engines/postgresql/index.js.map +1 -0
  365. package/dist/engines/postgresql/remote-version.js +109 -0
  366. package/dist/engines/postgresql/remote-version.js.map +1 -0
  367. package/dist/engines/postgresql/restore.js +254 -0
  368. package/dist/engines/postgresql/restore.js.map +1 -0
  369. package/dist/engines/postgresql/version-maps.js +73 -0
  370. package/dist/engines/postgresql/version-maps.js.map +1 -0
  371. package/dist/engines/postgresql/version-validator.js +286 -0
  372. package/dist/engines/postgresql/version-validator.js.map +1 -0
  373. package/dist/engines/qdrant/api-client.js +50 -0
  374. package/dist/engines/qdrant/api-client.js.map +1 -0
  375. package/dist/engines/qdrant/backup.js +115 -0
  376. package/dist/engines/qdrant/backup.js.map +1 -0
  377. package/dist/engines/qdrant/binary-manager.js +31 -0
  378. package/dist/engines/qdrant/binary-manager.js.map +1 -0
  379. package/dist/engines/qdrant/binary-urls.js +92 -0
  380. package/dist/engines/qdrant/binary-urls.js.map +1 -0
  381. package/dist/engines/qdrant/cli-utils.js +39 -0
  382. package/dist/engines/qdrant/cli-utils.js.map +1 -0
  383. package/dist/engines/qdrant/hostdb-releases.js +21 -0
  384. package/dist/engines/qdrant/hostdb-releases.js.map +1 -0
  385. package/dist/engines/qdrant/index.js +1002 -0
  386. package/dist/engines/qdrant/index.js.map +1 -0
  387. package/dist/engines/qdrant/restore.js +154 -0
  388. package/dist/engines/qdrant/restore.js.map +1 -0
  389. package/dist/engines/qdrant/version-maps.js +67 -0
  390. package/dist/engines/qdrant/version-maps.js.map +1 -0
  391. package/dist/engines/qdrant/version-validator.js +109 -0
  392. package/dist/engines/qdrant/version-validator.js.map +1 -0
  393. package/dist/engines/questdb/backup.js +191 -0
  394. package/dist/engines/questdb/backup.js.map +1 -0
  395. package/dist/engines/questdb/binary-manager.js +247 -0
  396. package/dist/engines/questdb/binary-manager.js.map +1 -0
  397. package/dist/engines/questdb/binary-urls.js +27 -0
  398. package/dist/engines/questdb/binary-urls.js.map +1 -0
  399. package/dist/engines/questdb/hostdb-releases.js +21 -0
  400. package/dist/engines/questdb/hostdb-releases.js.map +1 -0
  401. package/dist/engines/questdb/index.js +814 -0
  402. package/dist/engines/questdb/index.js.map +1 -0
  403. package/dist/engines/questdb/restore.js +202 -0
  404. package/dist/engines/questdb/restore.js.map +1 -0
  405. package/dist/engines/questdb/version-maps.js +33 -0
  406. package/dist/engines/questdb/version-maps.js.map +1 -0
  407. package/dist/engines/questdb/version-validator.js +99 -0
  408. package/dist/engines/questdb/version-validator.js.map +1 -0
  409. package/dist/engines/redis/backup.js +292 -0
  410. package/dist/engines/redis/backup.js.map +1 -0
  411. package/dist/engines/redis/binary-manager.js +32 -0
  412. package/dist/engines/redis/binary-manager.js.map +1 -0
  413. package/dist/engines/redis/binary-urls.js +96 -0
  414. package/dist/engines/redis/binary-urls.js.map +1 -0
  415. package/dist/engines/redis/cli-utils.js +38 -0
  416. package/dist/engines/redis/cli-utils.js.map +1 -0
  417. package/dist/engines/redis/hostdb-releases.js +21 -0
  418. package/dist/engines/redis/hostdb-releases.js.map +1 -0
  419. package/dist/engines/redis/index.js +1263 -0
  420. package/dist/engines/redis/index.js.map +1 -0
  421. package/dist/engines/redis/restore.js +338 -0
  422. package/dist/engines/redis/restore.js.map +1 -0
  423. package/dist/engines/redis/version-maps.js +70 -0
  424. package/dist/engines/redis/version-maps.js.map +1 -0
  425. package/dist/engines/redis/version-validator.js +109 -0
  426. package/dist/engines/redis/version-validator.js.map +1 -0
  427. package/dist/engines/sqlite/binary-manager.js +39 -0
  428. package/dist/engines/sqlite/binary-manager.js.map +1 -0
  429. package/{engines/sqlite/binary-urls.ts → dist/engines/sqlite/binary-urls.js} +11 -16
  430. package/dist/engines/sqlite/binary-urls.js.map +1 -0
  431. package/dist/engines/sqlite/hostdb-releases.js +21 -0
  432. package/dist/engines/sqlite/hostdb-releases.js.map +1 -0
  433. package/dist/engines/sqlite/index.js +493 -0
  434. package/dist/engines/sqlite/index.js.map +1 -0
  435. package/dist/engines/sqlite/registry.js +163 -0
  436. package/dist/engines/sqlite/registry.js.map +1 -0
  437. package/dist/engines/sqlite/scanner.js +12 -0
  438. package/dist/engines/sqlite/scanner.js.map +1 -0
  439. package/dist/engines/sqlite/version-maps.js +57 -0
  440. package/dist/engines/sqlite/version-maps.js.map +1 -0
  441. package/dist/engines/surrealdb/backup.js +97 -0
  442. package/dist/engines/surrealdb/backup.js.map +1 -0
  443. package/dist/engines/surrealdb/binary-manager.js +33 -0
  444. package/dist/engines/surrealdb/binary-manager.js.map +1 -0
  445. package/dist/engines/surrealdb/binary-urls.js +33 -0
  446. package/dist/engines/surrealdb/binary-urls.js.map +1 -0
  447. package/dist/engines/surrealdb/cli-utils.js +147 -0
  448. package/dist/engines/surrealdb/cli-utils.js.map +1 -0
  449. package/dist/engines/surrealdb/hostdb-releases.js +21 -0
  450. package/dist/engines/surrealdb/hostdb-releases.js.map +1 -0
  451. package/dist/engines/surrealdb/index.js +1022 -0
  452. package/dist/engines/surrealdb/index.js.map +1 -0
  453. package/dist/engines/surrealdb/restore.js +224 -0
  454. package/dist/engines/surrealdb/restore.js.map +1 -0
  455. package/dist/engines/surrealdb/version-maps.js +36 -0
  456. package/dist/engines/surrealdb/version-maps.js.map +1 -0
  457. package/dist/engines/tigerbeetle/backup.js +36 -0
  458. package/dist/engines/tigerbeetle/backup.js.map +1 -0
  459. package/dist/engines/tigerbeetle/binary-manager.js +72 -0
  460. package/dist/engines/tigerbeetle/binary-manager.js.map +1 -0
  461. package/dist/engines/tigerbeetle/binary-urls.js +49 -0
  462. package/dist/engines/tigerbeetle/binary-urls.js.map +1 -0
  463. package/dist/engines/tigerbeetle/hostdb-releases.js +21 -0
  464. package/dist/engines/tigerbeetle/hostdb-releases.js.map +1 -0
  465. package/dist/engines/tigerbeetle/index.js +559 -0
  466. package/dist/engines/tigerbeetle/index.js.map +1 -0
  467. package/dist/engines/tigerbeetle/restore.js +91 -0
  468. package/dist/engines/tigerbeetle/restore.js.map +1 -0
  469. package/{engines/tigerbeetle/version-maps.ts → dist/engines/tigerbeetle/version-maps.js} +22 -31
  470. package/dist/engines/tigerbeetle/version-maps.js.map +1 -0
  471. package/dist/engines/tigerbeetle/version-validator.js +108 -0
  472. package/dist/engines/tigerbeetle/version-validator.js.map +1 -0
  473. package/dist/engines/typedb/backup.js +129 -0
  474. package/dist/engines/typedb/backup.js.map +1 -0
  475. package/dist/engines/typedb/binary-manager.js +151 -0
  476. package/dist/engines/typedb/binary-manager.js.map +1 -0
  477. package/dist/engines/typedb/binary-urls.js +33 -0
  478. package/dist/engines/typedb/binary-urls.js.map +1 -0
  479. package/dist/engines/typedb/cli-utils.js +163 -0
  480. package/dist/engines/typedb/cli-utils.js.map +1 -0
  481. package/dist/engines/typedb/hostdb-releases.js +21 -0
  482. package/dist/engines/typedb/hostdb-releases.js.map +1 -0
  483. package/dist/engines/typedb/index.js +1003 -0
  484. package/dist/engines/typedb/index.js.map +1 -0
  485. package/dist/engines/typedb/restore.js +279 -0
  486. package/dist/engines/typedb/restore.js.map +1 -0
  487. package/dist/engines/typedb/version-maps.js +40 -0
  488. package/dist/engines/typedb/version-maps.js.map +1 -0
  489. package/dist/engines/typedb/version-validator.js +103 -0
  490. package/dist/engines/typedb/version-validator.js.map +1 -0
  491. package/dist/engines/valkey/backup.js +292 -0
  492. package/dist/engines/valkey/backup.js.map +1 -0
  493. package/dist/engines/valkey/binary-manager.js +33 -0
  494. package/dist/engines/valkey/binary-manager.js.map +1 -0
  495. package/dist/engines/valkey/binary-urls.js +98 -0
  496. package/dist/engines/valkey/binary-urls.js.map +1 -0
  497. package/dist/engines/valkey/cli-utils.js +38 -0
  498. package/dist/engines/valkey/cli-utils.js.map +1 -0
  499. package/dist/engines/valkey/hostdb-releases.js +21 -0
  500. package/dist/engines/valkey/hostdb-releases.js.map +1 -0
  501. package/dist/engines/valkey/index.js +1257 -0
  502. package/dist/engines/valkey/index.js.map +1 -0
  503. package/dist/engines/valkey/restore.js +340 -0
  504. package/dist/engines/valkey/restore.js.map +1 -0
  505. package/dist/engines/valkey/version-maps.js +70 -0
  506. package/dist/engines/valkey/version-maps.js.map +1 -0
  507. package/dist/engines/valkey/version-validator.js +112 -0
  508. package/dist/engines/valkey/version-validator.js.map +1 -0
  509. package/dist/engines/weaviate/api-client.js +50 -0
  510. package/dist/engines/weaviate/api-client.js.map +1 -0
  511. package/dist/engines/weaviate/backup.js +95 -0
  512. package/dist/engines/weaviate/backup.js.map +1 -0
  513. package/dist/engines/weaviate/binary-manager.js +58 -0
  514. package/dist/engines/weaviate/binary-manager.js.map +1 -0
  515. package/dist/engines/weaviate/binary-urls.js +92 -0
  516. package/dist/engines/weaviate/binary-urls.js.map +1 -0
  517. package/dist/engines/weaviate/cli-utils.js +39 -0
  518. package/dist/engines/weaviate/cli-utils.js.map +1 -0
  519. package/dist/engines/weaviate/hostdb-releases.js +21 -0
  520. package/dist/engines/weaviate/hostdb-releases.js.map +1 -0
  521. package/dist/engines/weaviate/index.js +871 -0
  522. package/dist/engines/weaviate/index.js.map +1 -0
  523. package/dist/engines/weaviate/restore.js +185 -0
  524. package/dist/engines/weaviate/restore.js.map +1 -0
  525. package/dist/engines/weaviate/version-maps.js +67 -0
  526. package/dist/engines/weaviate/version-maps.js.map +1 -0
  527. package/dist/engines/weaviate/version-validator.js +109 -0
  528. package/dist/engines/weaviate/version-validator.js.map +1 -0
  529. package/dist/types/index.js +102 -0
  530. package/dist/types/index.js.map +1 -0
  531. package/package.json +12 -9
  532. package/bin/cli.js +0 -68
  533. package/cli/bin.ts +0 -10
  534. package/cli/commands/attach.ts +0 -139
  535. package/cli/commands/backup.ts +0 -290
  536. package/cli/commands/backups.ts +0 -247
  537. package/cli/commands/clone.ts +0 -159
  538. package/cli/commands/config.ts +0 -367
  539. package/cli/commands/connect.ts +0 -684
  540. package/cli/commands/create.ts +0 -1201
  541. package/cli/commands/databases.ts +0 -630
  542. package/cli/commands/delete.ts +0 -133
  543. package/cli/commands/deps.ts +0 -342
  544. package/cli/commands/detach.ts +0 -107
  545. package/cli/commands/doctor.ts +0 -689
  546. package/cli/commands/duckdb.ts +0 -273
  547. package/cli/commands/edit.ts +0 -683
  548. package/cli/commands/engines.ts +0 -1914
  549. package/cli/commands/export.ts +0 -544
  550. package/cli/commands/info.ts +0 -340
  551. package/cli/commands/list.ts +0 -284
  552. package/cli/commands/logs.ts +0 -102
  553. package/cli/commands/menu/backup-handlers.ts +0 -1571
  554. package/cli/commands/menu/container-handlers.ts +0 -2288
  555. package/cli/commands/menu/engine-handlers.ts +0 -355
  556. package/cli/commands/menu/index.ts +0 -342
  557. package/cli/commands/menu/settings-handlers.ts +0 -365
  558. package/cli/commands/menu/shared.ts +0 -23
  559. package/cli/commands/menu/shell-handlers.ts +0 -1811
  560. package/cli/commands/menu/sql-handlers.ts +0 -231
  561. package/cli/commands/menu/update-handlers.ts +0 -378
  562. package/cli/commands/menu/validators.ts +0 -8
  563. package/cli/commands/ports.ts +0 -211
  564. package/cli/commands/pull.ts +0 -223
  565. package/cli/commands/query.ts +0 -241
  566. package/cli/commands/restore.ts +0 -587
  567. package/cli/commands/run.ts +0 -178
  568. package/cli/commands/self-update.ts +0 -121
  569. package/cli/commands/sqlite.ts +0 -273
  570. package/cli/commands/start.ts +0 -218
  571. package/cli/commands/stop.ts +0 -241
  572. package/cli/commands/url.ts +0 -104
  573. package/cli/commands/users.ts +0 -264
  574. package/cli/commands/version.ts +0 -55
  575. package/cli/commands/which.ts +0 -290
  576. package/cli/constants.ts +0 -233
  577. package/cli/helpers.ts +0 -1593
  578. package/cli/index.ts +0 -162
  579. package/cli/ui/prompts.ts +0 -1525
  580. package/cli/ui/spinner.ts +0 -88
  581. package/cli/ui/theme.ts +0 -128
  582. package/cli/utils/file-follower.ts +0 -93
  583. package/config/backup-formats.ts +0 -446
  584. package/config/defaults.ts +0 -56
  585. package/config/engine-defaults.ts +0 -336
  586. package/config/engines-registry.ts +0 -150
  587. package/config/engines.schema.json +0 -135
  588. package/config/os-dependencies.ts +0 -888
  589. package/config/paths.ts +0 -200
  590. package/core/backup-restore.ts +0 -330
  591. package/core/base-binary-manager.ts +0 -562
  592. package/core/base-document-binary-manager.ts +0 -523
  593. package/core/base-embedded-binary-manager.ts +0 -547
  594. package/core/base-server-binary-manager.ts +0 -523
  595. package/core/config-manager.ts +0 -652
  596. package/core/container-manager.ts +0 -787
  597. package/core/credential-generator.ts +0 -93
  598. package/core/credential-manager.ts +0 -259
  599. package/core/dblab-utils.ts +0 -113
  600. package/core/dependency-manager.ts +0 -512
  601. package/core/docker-exporter.ts +0 -1345
  602. package/core/error-handler.ts +0 -419
  603. package/core/fs-error-utils.ts +0 -82
  604. package/core/homebrew-version-manager.ts +0 -352
  605. package/core/hostdb-client.ts +0 -344
  606. package/core/hostdb-metadata.ts +0 -350
  607. package/core/hostdb-releases-factory.ts +0 -237
  608. package/core/library-env.ts +0 -118
  609. package/core/pgweb-utils.ts +0 -62
  610. package/core/platform-service.ts +0 -829
  611. package/core/port-manager.ts +0 -165
  612. package/core/process-manager.ts +0 -576
  613. package/core/pull-manager.ts +0 -511
  614. package/core/query-parser.ts +0 -514
  615. package/core/spawn-utils.ts +0 -122
  616. package/core/start-with-retry.ts +0 -130
  617. package/core/test-cleanup.ts +0 -108
  618. package/core/tls-generator.ts +0 -116
  619. package/core/transaction-manager.ts +0 -158
  620. package/core/update-manager.ts +0 -308
  621. package/core/version-migration.ts +0 -346
  622. package/core/version-utils.ts +0 -104
  623. package/engines/base-engine.ts +0 -340
  624. package/engines/clickhouse/README.md +0 -231
  625. package/engines/clickhouse/backup.ts +0 -398
  626. package/engines/clickhouse/binary-manager.ts +0 -201
  627. package/engines/clickhouse/binary-urls.ts +0 -125
  628. package/engines/clickhouse/cli-utils.ts +0 -176
  629. package/engines/clickhouse/hostdb-releases.ts +0 -30
  630. package/engines/clickhouse/index.ts +0 -1345
  631. package/engines/clickhouse/restore.ts +0 -466
  632. package/engines/clickhouse/version-maps.ts +0 -95
  633. package/engines/clickhouse/version-validator.ts +0 -154
  634. package/engines/cockroachdb/README.md +0 -170
  635. package/engines/cockroachdb/backup.ts +0 -376
  636. package/engines/cockroachdb/binary-manager.ts +0 -45
  637. package/engines/cockroachdb/binary-urls.ts +0 -40
  638. package/engines/cockroachdb/cli-utils.ts +0 -384
  639. package/engines/cockroachdb/hostdb-releases.ts +0 -26
  640. package/engines/cockroachdb/index.ts +0 -1276
  641. package/engines/cockroachdb/restore.ts +0 -455
  642. package/engines/cockroachdb/version-maps.ts +0 -42
  643. package/engines/couchdb/README.md +0 -257
  644. package/engines/couchdb/api-client.ts +0 -81
  645. package/engines/couchdb/backup.ts +0 -137
  646. package/engines/couchdb/binary-manager.ts +0 -86
  647. package/engines/couchdb/binary-urls.ts +0 -115
  648. package/engines/couchdb/hostdb-releases.ts +0 -23
  649. package/engines/couchdb/index.ts +0 -1429
  650. package/engines/couchdb/restore.ts +0 -290
  651. package/engines/couchdb/version-maps.ts +0 -78
  652. package/engines/couchdb/version-validator.ts +0 -111
  653. package/engines/duckdb/README.md +0 -154
  654. package/engines/duckdb/binary-manager.ts +0 -45
  655. package/engines/duckdb/hostdb-releases.ts +0 -23
  656. package/engines/duckdb/index.ts +0 -749
  657. package/engines/duckdb/registry.ts +0 -303
  658. package/engines/duckdb/scanner.ts +0 -22
  659. package/engines/duckdb/version-maps.ts +0 -78
  660. package/engines/duckdb/version-validator.ts +0 -78
  661. package/engines/ferretdb/README.md +0 -262
  662. package/engines/ferretdb/backup.ts +0 -173
  663. package/engines/ferretdb/binary-manager.ts +0 -1095
  664. package/engines/ferretdb/binary-urls.ts +0 -183
  665. package/engines/ferretdb/index.ts +0 -1907
  666. package/engines/ferretdb/restore.ts +0 -357
  667. package/engines/file-based-utils.ts +0 -262
  668. package/engines/index.ts +0 -131
  669. package/engines/influxdb/README.md +0 -180
  670. package/engines/influxdb/api-client.ts +0 -64
  671. package/engines/influxdb/backup.ts +0 -160
  672. package/engines/influxdb/binary-manager.ts +0 -110
  673. package/engines/influxdb/binary-urls.ts +0 -69
  674. package/engines/influxdb/hostdb-releases.ts +0 -23
  675. package/engines/influxdb/index.ts +0 -1272
  676. package/engines/influxdb/restore.ts +0 -417
  677. package/engines/influxdb/version-maps.ts +0 -75
  678. package/engines/influxdb/version-validator.ts +0 -128
  679. package/engines/mariadb/README.md +0 -141
  680. package/engines/mariadb/backup.ts +0 -233
  681. package/engines/mariadb/binary-manager.ts +0 -45
  682. package/engines/mariadb/hostdb-releases.ts +0 -23
  683. package/engines/mariadb/index.ts +0 -1300
  684. package/engines/mariadb/restore.ts +0 -447
  685. package/engines/mariadb/version-maps.ts +0 -72
  686. package/engines/mariadb/version-validator.ts +0 -181
  687. package/engines/meilisearch/README.md +0 -255
  688. package/engines/meilisearch/api-client.ts +0 -61
  689. package/engines/meilisearch/backup.ts +0 -233
  690. package/engines/meilisearch/binary-manager.ts +0 -43
  691. package/engines/meilisearch/binary-urls.ts +0 -69
  692. package/engines/meilisearch/hostdb-releases.ts +0 -26
  693. package/engines/meilisearch/index.ts +0 -1292
  694. package/engines/meilisearch/restore.ts +0 -219
  695. package/engines/meilisearch/version-maps.ts +0 -78
  696. package/engines/meilisearch/version-validator.ts +0 -128
  697. package/engines/mongodb/README.md +0 -162
  698. package/engines/mongodb/backup.ts +0 -127
  699. package/engines/mongodb/binary-manager.ts +0 -48
  700. package/engines/mongodb/binary-urls.ts +0 -63
  701. package/engines/mongodb/cli-utils.ts +0 -171
  702. package/engines/mongodb/hostdb-releases.ts +0 -91
  703. package/engines/mongodb/index.ts +0 -1118
  704. package/engines/mongodb/restore.ts +0 -361
  705. package/engines/mongodb/version-maps.ts +0 -91
  706. package/engines/mongodb/version-validator.ts +0 -160
  707. package/engines/mysql/README.md +0 -142
  708. package/engines/mysql/backup.ts +0 -270
  709. package/engines/mysql/binary-detection.ts +0 -408
  710. package/engines/mysql/binary-manager.ts +0 -42
  711. package/engines/mysql/binary-urls.ts +0 -104
  712. package/engines/mysql/index.ts +0 -1361
  713. package/engines/mysql/restore.ts +0 -500
  714. package/engines/mysql/version-maps.ts +0 -91
  715. package/engines/mysql/version-validator.ts +0 -369
  716. package/engines/postgresql/README.md +0 -158
  717. package/engines/postgresql/backup.ts +0 -151
  718. package/engines/postgresql/binary-manager.ts +0 -114
  719. package/engines/postgresql/binary-urls.ts +0 -99
  720. package/engines/postgresql/hostdb-releases.ts +0 -26
  721. package/engines/postgresql/index.ts +0 -1143
  722. package/engines/postgresql/remote-version.ts +0 -161
  723. package/engines/postgresql/restore.ts +0 -342
  724. package/engines/postgresql/version-maps.ts +0 -83
  725. package/engines/postgresql/version-validator.ts +0 -413
  726. package/engines/qdrant/README.md +0 -222
  727. package/engines/qdrant/api-client.ts +0 -61
  728. package/engines/qdrant/backup.ts +0 -165
  729. package/engines/qdrant/binary-manager.ts +0 -43
  730. package/engines/qdrant/binary-urls.ts +0 -115
  731. package/engines/qdrant/cli-utils.ts +0 -43
  732. package/engines/qdrant/hostdb-releases.ts +0 -23
  733. package/engines/qdrant/index.ts +0 -1312
  734. package/engines/qdrant/restore.ts +0 -203
  735. package/engines/qdrant/version-maps.ts +0 -78
  736. package/engines/qdrant/version-validator.ts +0 -128
  737. package/engines/questdb/README.md +0 -334
  738. package/engines/questdb/backup.ts +0 -220
  739. package/engines/questdb/binary-manager.ts +0 -310
  740. package/engines/questdb/binary-urls.ts +0 -34
  741. package/engines/questdb/hostdb-releases.ts +0 -23
  742. package/engines/questdb/index.ts +0 -1023
  743. package/engines/questdb/restore.ts +0 -260
  744. package/engines/questdb/version-maps.ts +0 -37
  745. package/engines/questdb/version-validator.ts +0 -121
  746. package/engines/redis/README.md +0 -173
  747. package/engines/redis/backup.ts +0 -389
  748. package/engines/redis/binary-manager.ts +0 -44
  749. package/engines/redis/binary-urls.ts +0 -117
  750. package/engines/redis/cli-utils.ts +0 -42
  751. package/engines/redis/hostdb-releases.ts +0 -23
  752. package/engines/redis/index.ts +0 -1583
  753. package/engines/redis/restore.ts +0 -443
  754. package/engines/redis/version-maps.ts +0 -81
  755. package/engines/redis/version-validator.ts +0 -131
  756. package/engines/sqlite/README.md +0 -162
  757. package/engines/sqlite/binary-manager.ts +0 -52
  758. package/engines/sqlite/hostdb-releases.ts +0 -23
  759. package/engines/sqlite/index.ts +0 -641
  760. package/engines/sqlite/registry.ts +0 -198
  761. package/engines/sqlite/scanner.ts +0 -22
  762. package/engines/sqlite/version-maps.ts +0 -64
  763. package/engines/surrealdb/README.md +0 -218
  764. package/engines/surrealdb/backup.ts +0 -131
  765. package/engines/surrealdb/binary-manager.ts +0 -45
  766. package/engines/surrealdb/binary-urls.ts +0 -40
  767. package/engines/surrealdb/cli-utils.ts +0 -173
  768. package/engines/surrealdb/hostdb-releases.ts +0 -23
  769. package/engines/surrealdb/index.ts +0 -1246
  770. package/engines/surrealdb/restore.ts +0 -302
  771. package/engines/surrealdb/version-maps.ts +0 -41
  772. package/engines/tigerbeetle/README.md +0 -61
  773. package/engines/tigerbeetle/backup.ts +0 -49
  774. package/engines/tigerbeetle/binary-manager.ts +0 -95
  775. package/engines/tigerbeetle/binary-urls.ts +0 -62
  776. package/engines/tigerbeetle/hostdb-releases.ts +0 -26
  777. package/engines/tigerbeetle/index.ts +0 -746
  778. package/engines/tigerbeetle/restore.ts +0 -130
  779. package/engines/tigerbeetle/version-validator.ts +0 -126
  780. package/engines/typedb/backup.ts +0 -167
  781. package/engines/typedb/binary-manager.ts +0 -200
  782. package/engines/typedb/binary-urls.ts +0 -40
  783. package/engines/typedb/cli-utils.ts +0 -210
  784. package/engines/typedb/hostdb-releases.ts +0 -23
  785. package/engines/typedb/index.ts +0 -1275
  786. package/engines/typedb/restore.ts +0 -377
  787. package/engines/typedb/version-maps.ts +0 -48
  788. package/engines/typedb/version-validator.ts +0 -127
  789. package/engines/valkey/README.md +0 -219
  790. package/engines/valkey/backup.ts +0 -389
  791. package/engines/valkey/binary-manager.ts +0 -45
  792. package/engines/valkey/binary-urls.ts +0 -122
  793. package/engines/valkey/cli-utils.ts +0 -42
  794. package/engines/valkey/hostdb-releases.ts +0 -23
  795. package/engines/valkey/index.ts +0 -1585
  796. package/engines/valkey/restore.ts +0 -446
  797. package/engines/valkey/version-maps.ts +0 -81
  798. package/engines/valkey/version-validator.ts +0 -131
  799. package/engines/weaviate/README.md +0 -302
  800. package/engines/weaviate/api-client.ts +0 -61
  801. package/engines/weaviate/backup.ts +0 -145
  802. package/engines/weaviate/binary-manager.ts +0 -80
  803. package/engines/weaviate/binary-urls.ts +0 -115
  804. package/engines/weaviate/cli-utils.ts +0 -43
  805. package/engines/weaviate/hostdb-releases.ts +0 -23
  806. package/engines/weaviate/index.ts +0 -1139
  807. package/engines/weaviate/restore.ts +0 -235
  808. package/engines/weaviate/version-maps.ts +0 -78
  809. package/engines/weaviate/version-validator.ts +0 -128
  810. package/types/index.ts +0 -624
  811. /package/{config → dist/config}/engines.json +0 -0
@@ -1,1907 +0,0 @@
1
- /**
2
- * FerretDB Engine implementation
3
- *
4
- * FerretDB is a MongoDB-compatible proxy that stores data in PostgreSQL.
5
- * This is a composite engine that manages two processes:
6
- * 1. PostgreSQL backend (postgresql-documentdb)
7
- * 2. FerretDB proxy
8
- *
9
- * The lifecycle is:
10
- * - Start: Start PostgreSQL → Wait for ready → Start FerretDB
11
- * - Stop: Stop FerretDB → Stop PostgreSQL
12
- */
13
-
14
- import {
15
- spawn,
16
- exec,
17
- type ChildProcess,
18
- type SpawnOptions,
19
- } from 'child_process'
20
- import { promisify } from 'util'
21
- import { existsSync } from 'fs'
22
- import net from 'net'
23
- import {
24
- mkdir,
25
- writeFile,
26
- readFile,
27
- symlink,
28
- unlink,
29
- readdir,
30
- } from 'fs/promises'
31
- import { join, basename, dirname } from 'path'
32
- import { BaseEngine } from '../base-engine'
33
- import { paths } from '../../config/paths'
34
- import { getEngineDefaults } from '../../config/defaults'
35
- import { platformService, isWindows } from '../../core/platform-service'
36
- import { configManager } from '../../core/config-manager'
37
- import { containerManager } from '../../core/container-manager'
38
- import {
39
- logDebug,
40
- logWarning,
41
- assertValidDatabaseName,
42
- assertValidUsername,
43
- } from '../../core/error-handler'
44
- import { processManager } from '../../core/process-manager'
45
- import { spawnAsync } from '../../core/spawn-utils'
46
- import { ferretdbBinaryManager } from './binary-manager'
47
- import {
48
- SUPPORTED_MAJOR_VERSIONS,
49
- FALLBACK_VERSION_MAP,
50
- DEFAULT_DOCUMENTDB_VERSION,
51
- DEFAULT_V1_POSTGRESQL_VERSION,
52
- normalizeVersion,
53
- normalizeDocumentDBVersion,
54
- isV1,
55
- } from './version-maps'
56
- import { getBinaryUrls, isPlatformSupported } from './binary-urls'
57
- import {
58
- detectBackupFormat as detectBackupFormatImpl,
59
- restoreBackup,
60
- } from './restore'
61
- import { createBackup } from './backup'
62
- import {
63
- type Platform,
64
- type Arch,
65
- type ContainerConfig,
66
- type ProgressCallback,
67
- type BackupFormat,
68
- type BackupOptions,
69
- type BackupResult,
70
- type RestoreResult,
71
- type DumpResult,
72
- type StatusResult,
73
- type QueryResult,
74
- type QueryOptions,
75
- type CreateUserOptions,
76
- type UserCredentials,
77
- } from '../../types'
78
- import { parseMongoDBResult } from '../../core/query-parser'
79
-
80
- const execAsync = promisify(exec)
81
-
82
- const ENGINE = 'ferretdb'
83
- const engineDef = getEngineDefaults(ENGINE)
84
-
85
- // Default internal PostgreSQL port range for FerretDB backends
86
- const BACKEND_PORT_START = 54320
87
- const BACKEND_PORT_END = 54400
88
-
89
- /**
90
- * Allocate a port for the PostgreSQL backend
91
- *
92
- * KNOWN LIMITATION (TOCTOU): There is a race condition between checking port
93
- * availability and PostgreSQL actually binding to the port. Another process
94
- * could claim the port in between. This is acceptable for SpinDB's use case:
95
- *
96
- * 1. The backend port range (54320-54400) is unlikely to be used by other apps
97
- * 2. SpinDB is a local development tool, not a production server
98
- * 3. If a collision occurs, PostgreSQL will fail to start with a clear error
99
- * 4. The CLI layer uses startWithRetry() which can re-attempt with a new port
100
- *
101
- * A more robust solution would use SO_REUSEPORT or hold the socket until
102
- * PostgreSQL starts, but this adds complexity for minimal real-world benefit.
103
- */
104
- async function allocateBackendPort(): Promise<number> {
105
- for (let port = BACKEND_PORT_START; port <= BACKEND_PORT_END; port++) {
106
- if (await isPortAvailable(port)) {
107
- return port
108
- }
109
- }
110
- throw new Error(
111
- `No available ports in range ${BACKEND_PORT_START}-${BACKEND_PORT_END} for PostgreSQL backend`,
112
- )
113
- }
114
-
115
- /**
116
- * Check if a port is available
117
- */
118
- function isPortAvailable(port: number): Promise<boolean> {
119
- return new Promise((resolve) => {
120
- const server = net.createServer()
121
- server.once('error', () => resolve(false))
122
- server.once('listening', () => {
123
- server.close()
124
- resolve(true)
125
- })
126
- server.listen(port, '127.0.0.1')
127
- })
128
- }
129
-
130
- /**
131
- * Wait for a TCP port to accept connections
132
- */
133
- function waitForPort(port: number, timeoutMs = 30000): Promise<boolean> {
134
- const startTime = Date.now()
135
- const checkInterval = 500
136
-
137
- return new Promise((resolve) => {
138
- const check = () => {
139
- const socket = new net.Socket()
140
- socket.setTimeout(1000)
141
- socket.once('connect', () => {
142
- socket.destroy()
143
- resolve(true)
144
- })
145
- socket.once('error', () => {
146
- socket.destroy()
147
- if (Date.now() - startTime < timeoutMs) {
148
- setTimeout(check, checkInterval)
149
- } else {
150
- resolve(false)
151
- }
152
- })
153
- socket.once('timeout', () => {
154
- socket.destroy()
155
- if (Date.now() - startTime < timeoutMs) {
156
- setTimeout(check, checkInterval)
157
- } else {
158
- resolve(false)
159
- }
160
- })
161
- socket.connect(port, '127.0.0.1')
162
- }
163
- check()
164
- })
165
- }
166
-
167
- /**
168
- * Spawn a process and pipe input to its stdin.
169
- * Used for `postgres --single` which reads SQL from stdin.
170
- */
171
- function spawnWithInput(
172
- command: string,
173
- args: string[],
174
- input: string,
175
- options?: { env?: Record<string, string>; timeout?: number },
176
- ): Promise<{ stdout: string; stderr: string }> {
177
- return new Promise((resolve, reject) => {
178
- let proc: ChildProcess
179
- try {
180
- proc = spawn(command, args, {
181
- stdio: ['pipe', 'pipe', 'pipe'],
182
- env: options?.env ? { ...process.env, ...options.env } : undefined,
183
- })
184
- } catch (error) {
185
- reject(error)
186
- return
187
- }
188
-
189
- let stdout = ''
190
- let stderr = ''
191
- let timedOut = false
192
-
193
- const timer = options?.timeout
194
- ? setTimeout(() => {
195
- timedOut = true
196
- proc.kill('SIGKILL')
197
- reject(
198
- new Error(
199
- `Command "${command}" timed out after ${options.timeout}ms`,
200
- ),
201
- )
202
- }, options.timeout)
203
- : undefined
204
-
205
- proc.stdout?.on('data', (data: Buffer) => {
206
- stdout += data.toString()
207
- })
208
- proc.stderr?.on('data', (data: Buffer) => {
209
- stderr += data.toString()
210
- })
211
-
212
- proc.on('close', (code) => {
213
- if (timer) clearTimeout(timer)
214
- if (timedOut) return
215
- if (code === 0) {
216
- resolve({ stdout, stderr })
217
- } else {
218
- reject(
219
- new Error(
220
- `Command "${command}" failed with code ${code}: ${stderr || stdout}`,
221
- ),
222
- )
223
- }
224
- })
225
-
226
- proc.on('error', (err) => {
227
- if (timer) clearTimeout(timer)
228
- if (timedOut) return
229
- reject(err)
230
- })
231
-
232
- proc.stdin?.write(input)
233
- proc.stdin?.end()
234
- })
235
- }
236
-
237
- export class FerretDBEngine extends BaseEngine {
238
- name = ENGINE
239
- displayName = 'FerretDB'
240
- defaultPort = engineDef.defaultPort
241
- supportedVersions = SUPPORTED_MAJOR_VERSIONS
242
-
243
- // Get the current platform and architecture
244
- getPlatformInfo(): { platform: Platform; arch: Arch } {
245
- return platformService.getPlatformInfo()
246
- }
247
-
248
- /**
249
- * Check if the current platform supports FerretDB
250
- * @param version - Optional version to check (v1 supports Windows, v2 does not)
251
- */
252
- isPlatformSupported(version?: string): boolean {
253
- const { platform, arch } = this.getPlatformInfo()
254
- return isPlatformSupported(platform, arch, version)
255
- }
256
-
257
- /**
258
- * Returns available FerretDB versions from the fallback version map.
259
- */
260
- async fetchAvailableVersions(): Promise<Record<string, string[]>> {
261
- const versions: Record<string, string[]> = {}
262
-
263
- for (const [major, full] of Object.entries(FALLBACK_VERSION_MAP)) {
264
- if (/^\d+$/.test(major)) {
265
- versions[major] = [full]
266
- }
267
- }
268
-
269
- return versions
270
- }
271
-
272
- // Get binary download URL from hostdb
273
- getBinaryUrl(version: string, platform: Platform, arch: Arch): string {
274
- const backendVersion = isV1(version)
275
- ? DEFAULT_V1_POSTGRESQL_VERSION
276
- : DEFAULT_DOCUMENTDB_VERSION
277
- const urls = getBinaryUrls(version, backendVersion, platform, arch)
278
- return urls.ferretdb
279
- }
280
-
281
- // Resolves version string to full version
282
- resolveFullVersion(version: string): string {
283
- if (/^\d+\.\d+\.\d+$/.test(version)) {
284
- return version
285
- }
286
- return FALLBACK_VERSION_MAP[version] || `${version}.0.0`
287
- }
288
-
289
- // Get the path where binaries for a version would be installed
290
- getBinaryPath(version: string): string {
291
- const fullVersion = this.resolveFullVersion(version)
292
- const { platform: p, arch: a } = this.getPlatformInfo()
293
- return ferretdbBinaryManager.getFerretDBBinaryPath(fullVersion, p, a)
294
- }
295
-
296
- /**
297
- * Verify that FerretDB binaries are available and functional
298
- */
299
- async verifyBinary(binPath: string, version?: string): Promise<boolean> {
300
- const { platform: p, arch: a } = this.getPlatformInfo()
301
-
302
- if (version) {
303
- const backendVersion = isV1(version)
304
- ? DEFAULT_V1_POSTGRESQL_VERSION
305
- : DEFAULT_DOCUMENTDB_VERSION
306
- return ferretdbBinaryManager.isInstalled(version, p, a, backendVersion)
307
- }
308
-
309
- // Fallback: extract version from directory name
310
- const dirName = basename(binPath)
311
- const match = dirName.match(/^ferretdb-([\d.]+)-/)
312
- if (match) {
313
- const extractedVersion = match[1]
314
- const backendVersion = isV1(extractedVersion)
315
- ? DEFAULT_V1_POSTGRESQL_VERSION
316
- : DEFAULT_DOCUMENTDB_VERSION
317
- return ferretdbBinaryManager.isInstalled(
318
- extractedVersion,
319
- p,
320
- a,
321
- backendVersion,
322
- )
323
- }
324
-
325
- // Last resort: check file existence
326
- const ext = platformService.getExecutableExtension()
327
- const ferretdbPath = join(binPath, 'bin', `ferretdb${ext}`)
328
- return existsSync(ferretdbPath)
329
- }
330
-
331
- // Check if a specific FerretDB version is installed
332
- async isBinaryInstalled(version: string): Promise<boolean> {
333
- const { platform, arch } = this.getPlatformInfo()
334
- const backendVersion = isV1(version)
335
- ? DEFAULT_V1_POSTGRESQL_VERSION
336
- : DEFAULT_DOCUMENTDB_VERSION
337
- return ferretdbBinaryManager.isInstalled(
338
- version,
339
- platform,
340
- arch,
341
- backendVersion,
342
- )
343
- }
344
-
345
- /**
346
- * Ensure FerretDB binaries are available for a specific version
347
- */
348
- async ensureBinaries(
349
- version: string,
350
- onProgress?: ProgressCallback,
351
- ): Promise<string> {
352
- const { platform, arch } = this.getPlatformInfo()
353
- const backendVersion = isV1(version)
354
- ? DEFAULT_V1_POSTGRESQL_VERSION
355
- : DEFAULT_DOCUMENTDB_VERSION
356
-
357
- // Download binaries (proxy + backend)
358
- const { ferretdbPath } = await ferretdbBinaryManager.ensureInstalled(
359
- version,
360
- platform,
361
- arch,
362
- onProgress,
363
- backendVersion,
364
- )
365
-
366
- // Register ferretdb binary in config
367
- const ext = platformService.getExecutableExtension()
368
- const ferretdbBinary = join(ferretdbPath, 'bin', `ferretdb${ext}`)
369
- if (existsSync(ferretdbBinary)) {
370
- await configManager.setBinaryPath('ferretdb', ferretdbBinary, 'bundled')
371
- }
372
-
373
- return ferretdbPath
374
- }
375
-
376
- /**
377
- * Get backend binary paths and spawn environment for a container.
378
- * Centralizes the v1/v2 branching logic for backend resolution.
379
- */
380
- private getBackendPaths(
381
- version: string,
382
- backendVersion: string,
383
- platform: Platform,
384
- arch: Arch,
385
- ): { backendPath: string; pgSpawnEnv: Record<string, string> | undefined } {
386
- const fullVersion = normalizeVersion(version)
387
- const backendPath = ferretdbBinaryManager.getBackendBinaryPath(
388
- fullVersion,
389
- backendVersion,
390
- platform,
391
- arch,
392
- )
393
-
394
- const baseSpawnEnv = ferretdbBinaryManager.getBackendSpawnEnv(
395
- fullVersion,
396
- backendVersion,
397
- platform,
398
- arch,
399
- )
400
- const pgSpawnEnv =
401
- platform === 'darwin'
402
- ? {
403
- ...baseSpawnEnv,
404
- DYLD_FALLBACK_LIBRARY_PATH: join(backendPath, 'lib'),
405
- }
406
- : baseSpawnEnv
407
-
408
- return { backendPath, pgSpawnEnv }
409
- }
410
-
411
- /**
412
- * Initialize a new FerretDB container directory
413
- * Creates both the PostgreSQL data directory and FerretDB config
414
- */
415
- async initDataDir(
416
- containerName: string,
417
- _version: string,
418
- options: Record<string, unknown> = {},
419
- ): Promise<string> {
420
- const { platform, arch } = this.getPlatformInfo()
421
- const version = normalizeVersion(_version)
422
- const v1 = isV1(version)
423
-
424
- // Get binary paths - resolve backend based on v1/v2
425
- const backendVersion = v1
426
- ? (options.backendVersion as string) || DEFAULT_V1_POSTGRESQL_VERSION
427
- : (options.backendVersion as string) || DEFAULT_DOCUMENTDB_VERSION
428
-
429
- const { backendPath: documentdbPath, pgSpawnEnv: initSpawnEnv } =
430
- this.getBackendPaths(version, backendVersion, platform, arch)
431
-
432
- // Container directory structure
433
- const containerDir = paths.getContainerPath(containerName, {
434
- engine: ENGINE,
435
- })
436
- const pgDataDir = join(containerDir, 'pg_data')
437
- const logsDir = join(containerDir, 'logs')
438
-
439
- // Create directories
440
- await mkdir(containerDir, { recursive: true })
441
- await mkdir(logsDir, { recursive: true })
442
-
443
- // Initialize PostgreSQL data directory
444
- // Check for PG_VERSION file to determine if already initialized
445
- // (directory may exist but be empty if created by containerManager.create)
446
- const pgVersionFile = join(pgDataDir, 'PG_VERSION')
447
- if (!existsSync(pgVersionFile)) {
448
- const ext = platformService.getExecutableExtension()
449
- const initdb = join(documentdbPath, 'bin', `initdb${ext}`)
450
-
451
- if (!existsSync(initdb)) {
452
- throw new Error(`initdb not found at ${initdb}`)
453
- }
454
-
455
- // Homebrew-derived x64 binaries have compiled-in absolute paths for
456
- // sharedir, pkglibdir ($libdir), and libdir that don't exist when running
457
- // from ~/.spindb/bin/. We fix this by:
458
- // 1. Using initdb's -L flag to explicitly set the share directory
459
- // 2. Creating symlinks at compiled-in paths for pkglibdir and libdir
460
- // (these are needed by the bootstrap postgres subprocess during initdb)
461
- const shareDirBase = join(documentdbPath, 'share')
462
- const actualShareDir = existsSync(join(shareDirBase, 'postgres.bki'))
463
- ? shareDirBase
464
- : existsSync(join(shareDirBase, 'postgresql', 'postgres.bki'))
465
- ? join(shareDirBase, 'postgresql')
466
- : shareDirBase
467
-
468
- // v2 only: Homebrew-derived DocumentDB binaries need compiled-in path fixups
469
- // v1 uses plain PostgreSQL which has correct relative paths
470
- if (!v1 && platform === 'darwin') {
471
- const pgConfigBin = join(documentdbPath, 'bin', `pg_config${ext}`)
472
- if (existsSync(pgConfigBin)) {
473
- // Query all relevant compiled-in paths and create symlinks where needed
474
- const pathFixups: Array<{
475
- flag: string
476
- actualDir: string
477
- label: string
478
- }> = [
479
- { flag: '--sharedir', actualDir: actualShareDir, label: 'share' },
480
- {
481
- flag: '--pkglibdir',
482
- actualDir: existsSync(join(documentdbPath, 'lib', 'postgresql'))
483
- ? join(documentdbPath, 'lib', 'postgresql')
484
- : join(documentdbPath, 'lib'),
485
- label: 'pkglib',
486
- },
487
- {
488
- flag: '--libdir',
489
- actualDir: join(documentdbPath, 'lib'),
490
- label: 'lib',
491
- },
492
- ]
493
-
494
- // Create symlinks at compiled-in paths so PostgreSQL can find its
495
- // libraries. These paths may be in system directories (e.g. /usr/local/),
496
- // which require elevated privileges to write to.
497
- for (const { flag, actualDir, label } of pathFixups) {
498
- try {
499
- const { stdout: out } = await execAsync(
500
- `"${pgConfigBin}" ${flag}`,
501
- { timeout: 5000 },
502
- )
503
- const compiledDir = out.trim()
504
- logDebug(`pg_config ${flag}: ${compiledDir}`)
505
- if (compiledDir && !existsSync(compiledDir)) {
506
- await mkdir(dirname(compiledDir), { recursive: true })
507
- await symlink(actualDir, compiledDir)
508
- logDebug(
509
- `Created ${label} symlink: ${compiledDir} -> ${actualDir}`,
510
- )
511
- }
512
- } catch (error) {
513
- const e = error as NodeJS.ErrnoException
514
- const isPermission =
515
- e.code === 'EACCES' ||
516
- e.code === 'EPERM' ||
517
- (e.message && /permission denied/i.test(e.message))
518
- if (isPermission) {
519
- logWarning(
520
- `Cannot create ${label} symlink (permission denied). ` +
521
- `This can be caused by macOS SIP or container/sudo limitations when compiled-in paths point to system directories. ` +
522
- `Workaround: use a non-system install path, or run with elevated privileges if available (e.g., sudo spindb engines download ferretdb <version>). ` +
523
- `See https://github.com/robertjbass/spindb#ferretdb for details. ` +
524
- `Target: ${flag} -> ${actualDir}`,
525
- )
526
- } else {
527
- logDebug(`Could not fix compiled ${label} path: ${e.message}`)
528
- }
529
- }
530
- }
531
- }
532
- } else if (!v1) {
533
- logDebug(
534
- 'Skipping pg_config symlink fixups (not required on this platform)',
535
- )
536
- }
537
-
538
- // v2 only: Fix hardcoded Homebrew dylib paths in DocumentDB extension libraries
539
- // v1 uses plain PostgreSQL which doesn't have DocumentDB extensions
540
- if (!v1 && platform === 'darwin') {
541
- const dylibMarker = join(documentdbPath, '.dylib_fix_done')
542
- if (!existsSync(dylibMarker)) {
543
- await this.fixDylibDependencies(documentdbPath)
544
- try {
545
- await writeFile(dylibMarker, '', { flag: 'wx' })
546
- } catch {
547
- // Marker may already exist from a parallel init — safe to ignore
548
- }
549
- }
550
- }
551
-
552
- try {
553
- await spawnAsync(
554
- initdb,
555
- [
556
- '-D',
557
- pgDataDir,
558
- '-U',
559
- 'postgres',
560
- '--encoding=UTF8',
561
- '--locale=C',
562
- '-L',
563
- actualShareDir,
564
- ],
565
- { env: initSpawnEnv, timeout: 60000 },
566
- )
567
- logDebug(`Initialized PostgreSQL data directory: ${pgDataDir}`)
568
- } catch (error) {
569
- const err = error as Error
570
- throw new Error(`Failed to initialize PostgreSQL: ${err.message}`)
571
- }
572
-
573
- // v2 only: Copy the bundled postgresql.conf.sample to ensure shared_preload_libraries is set
574
- // This is critical for DocumentDB extension to load properly
575
- // v1 uses initdb defaults (no DocumentDB extensions to preload)
576
- if (!v1) {
577
- const bundledConf = existsSync(
578
- join(shareDirBase, 'postgresql.conf.sample'),
579
- )
580
- ? join(shareDirBase, 'postgresql.conf.sample')
581
- : join(shareDirBase, 'postgresql', 'postgresql.conf.sample')
582
- const pgConf = join(pgDataDir, 'postgresql.conf')
583
-
584
- if (existsSync(bundledConf)) {
585
- try {
586
- // Read the bundled config
587
- let confContent = await readFile(bundledConf, 'utf8')
588
-
589
- // Update cron.database_name to 'ferretdb' (required for pg_cron to work with DocumentDB)
590
- confContent = confContent.replace(
591
- /cron\.database_name\s*=\s*'[^']*'/,
592
- "cron.database_name = 'ferretdb'",
593
- )
594
-
595
- // Write the modified config
596
- await writeFile(pgConf, confContent)
597
- logDebug(`Copied and configured postgresql.conf to ${pgConf}`)
598
- } catch (copyError) {
599
- logDebug(
600
- `Warning: Could not copy postgresql.conf.sample: ${copyError}`,
601
- )
602
- // Continue anyway - initdb creates a default config
603
- }
604
- } else {
605
- logDebug(`Bundled postgresql.conf.sample not found at ${bundledConf}`)
606
- }
607
- }
608
- }
609
-
610
- return pgDataDir
611
- }
612
-
613
- /**
614
- * Start FerretDB (two-process lifecycle)
615
- *
616
- * 1. Allocate/verify backend port
617
- * 2. Start PostgreSQL
618
- * 3. Wait for PostgreSQL ready
619
- * 4. Create ferretdb database + extension (first start)
620
- * 5. Start FerretDB proxy
621
- * 6. Verify FerretDB connectivity
622
- */
623
- async start(
624
- container: ContainerConfig,
625
- onProgress?: ProgressCallback,
626
- ): Promise<{ port: number; connectionString: string }> {
627
- const {
628
- name,
629
- port,
630
- version,
631
- backendVersion,
632
- backendPort: existingBackendPort,
633
- } = container
634
-
635
- // Check if already running
636
- const alreadyRunning = await processManager.isRunning(name, {
637
- engine: ENGINE,
638
- })
639
- if (alreadyRunning) {
640
- return {
641
- port,
642
- connectionString: this.getConnectionString(container),
643
- }
644
- }
645
-
646
- const { platform, arch } = this.getPlatformInfo()
647
- const fullVersion = normalizeVersion(version)
648
- const v1 = isV1(version)
649
- const effectiveBackendVersion = v1
650
- ? backendVersion || DEFAULT_V1_POSTGRESQL_VERSION
651
- : normalizeDocumentDBVersion(backendVersion || DEFAULT_DOCUMENTDB_VERSION)
652
-
653
- // Get binary paths using version-aware helper
654
- const ferretdbPath = ferretdbBinaryManager.getFerretDBBinaryPath(
655
- fullVersion,
656
- platform,
657
- arch,
658
- )
659
- const { backendPath: documentdbPath, pgSpawnEnv } = this.getBackendPaths(
660
- version,
661
- effectiveBackendVersion,
662
- platform,
663
- arch,
664
- )
665
-
666
- const ext = platformService.getExecutableExtension()
667
- const ferretdbBinary = join(ferretdbPath, 'bin', `ferretdb${ext}`)
668
- const pgCtl = join(documentdbPath, 'bin', `pg_ctl${ext}`)
669
- // v1 backend may be a minimal PostgreSQL install (shared with DocumentDB) that
670
- // lacks client tools. Use postgres --single as fallback for database creation.
671
- const psqlCandidate = join(documentdbPath, 'bin', `psql${ext}`)
672
- const psql = existsSync(psqlCandidate) ? psqlCandidate : null
673
- const postgresBinary = join(documentdbPath, 'bin', `postgres${ext}`)
674
-
675
- // Verify binaries exist
676
- if (!existsSync(ferretdbBinary)) {
677
- throw new Error(
678
- `FerretDB binary not found. Run: spindb engines download ferretdb ${version}`,
679
- )
680
- }
681
- if (!existsSync(pgCtl)) {
682
- throw new Error(
683
- `postgresql-documentdb not found. Run: spindb engines download ferretdb ${version}`,
684
- )
685
- }
686
-
687
- // Container paths
688
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
689
- const pgDataDir = join(containerDir, 'pg_data')
690
- const logsDir = join(containerDir, 'logs')
691
- const pgLogFile = join(logsDir, 'postgres.log')
692
- const ferretPidFile = join(containerDir, 'ferretdb.pid')
693
-
694
- // Allocate backend port
695
- const backendPort = existingBackendPort || (await allocateBackendPort())
696
-
697
- // v2 only: Fix hardcoded Homebrew dylib paths (darwin-x64 binaries)
698
- // Skip if already completed (marker written by initDataDir or a previous start)
699
- // v1 uses plain PostgreSQL which doesn't have DocumentDB extensions
700
- if (!v1 && platform === 'darwin') {
701
- const dylibMarker = join(documentdbPath, '.dylib_fix_done')
702
- if (!existsSync(dylibMarker)) {
703
- await this.fixDylibDependencies(documentdbPath)
704
- try {
705
- await writeFile(dylibMarker, '', { flag: 'wx' })
706
- } catch {
707
- // Marker may already exist — safe to ignore
708
- }
709
- }
710
- }
711
-
712
- let pgStarted = false
713
- let ferretStarted = false
714
-
715
- try {
716
- // 1. Start PostgreSQL (skip if already running)
717
- onProgress?.({
718
- stage: 'starting',
719
- message: 'Starting PostgreSQL backend...',
720
- })
721
-
722
- // Check if PostgreSQL backend is already running in this data dir
723
- let pgAlreadyRunning = false
724
- try {
725
- await spawnAsync(pgCtl, ['status', '-D', pgDataDir], {
726
- env: pgSpawnEnv,
727
- timeout: 5000,
728
- })
729
- // pg_ctl status exits 0 if server is running
730
- pgAlreadyRunning = true
731
- logDebug('PostgreSQL backend already running, skipping start')
732
- } catch {
733
- // Exit code != 0 means not running — proceed to start
734
- }
735
-
736
- // v1 pre-start: Create ferretdb database using postgres --single mode
737
- // when psql is unavailable (minimal PG install may lack client tools).
738
- // postgres --single requires exclusive data dir access, so this MUST
739
- // happen before pg_ctl start.
740
- if (v1 && !psql && !pgAlreadyRunning) {
741
- logDebug(
742
- 'psql not found in backend, using postgres --single to pre-create database',
743
- )
744
- try {
745
- await spawnWithInput(
746
- postgresBinary,
747
- ['--single', '-D', pgDataDir, 'postgres'],
748
- "CREATE DATABASE ferretdb ENCODING 'UTF8';\n",
749
- { env: pgSpawnEnv, timeout: 30000 },
750
- )
751
- logDebug('Pre-created ferretdb database via postgres --single')
752
- } catch {
753
- // Database may already exist from a previous start — safe to ignore
754
- logDebug(
755
- 'postgres --single CREATE DATABASE failed (may already exist)',
756
- )
757
- }
758
- }
759
-
760
- if (!pgAlreadyRunning) {
761
- // Use pg_ctl to start PostgreSQL
762
- // On Windows, spawnAsync pipes stdout/stderr which get inherited by the
763
- // PostgreSQL background process, preventing the 'close' event from firing
764
- // until PG itself exits (causing a 60s timeout even though PG is ready).
765
- // Use exec() on Windows (matches process-manager.ts approach) which runs
766
- // through the shell and doesn't hold pipes open. On Unix, use -w (wait mode).
767
- try {
768
- if (isWindows()) {
769
- const cmd = `"${pgCtl}" start -D "${pgDataDir}" -l "${pgLogFile}" -o "-p ${backendPort} -h 127.0.0.1"`
770
- await execAsync(cmd, {
771
- env: { ...process.env, ...pgSpawnEnv },
772
- timeout: 30000,
773
- })
774
- } else {
775
- const pgCtlArgs = [
776
- 'start',
777
- '-D',
778
- pgDataDir,
779
- '-l',
780
- pgLogFile,
781
- '-o',
782
- `-p ${backendPort} -h 127.0.0.1`,
783
- '-w',
784
- ]
785
- await spawnAsync(pgCtl, pgCtlArgs, {
786
- env: pgSpawnEnv,
787
- timeout: 60000,
788
- })
789
- }
790
- } catch (pgError) {
791
- // Read PostgreSQL log for debugging
792
- let pgLog = ''
793
- try {
794
- pgLog = await readFile(pgLogFile, 'utf8')
795
- } catch {
796
- pgLog = '(no log available)'
797
- }
798
- throw new Error(
799
- `PostgreSQL backend failed to start: ${pgError instanceof Error ? pgError.message : pgError}\n` +
800
- `PostgreSQL log:\n${pgLog.slice(-2000)}`, // Last 2KB of log
801
- )
802
- }
803
- }
804
-
805
- pgStarted = true
806
- logDebug(`PostgreSQL started on port ${backendPort}`)
807
-
808
- // 2. Wait for PostgreSQL to be ready
809
- onProgress?.({ stage: 'starting', message: 'Waiting for PostgreSQL...' })
810
- const pgReady = await waitForPort(backendPort, 30000)
811
- if (!pgReady) {
812
- throw new Error('PostgreSQL failed to start within timeout')
813
- }
814
-
815
- // 3. Create ferretdb database and extension (first start)
816
- // For v1 without psql, database was already created pre-start via postgres --single
817
- if (psql) {
818
- onProgress?.({
819
- stage: 'starting',
820
- message: 'Initializing FerretDB database...',
821
- })
822
- try {
823
- // Create ferretdb database if it doesn't exist
824
- await spawnAsync(
825
- psql,
826
- [
827
- '-h',
828
- '127.0.0.1',
829
- '-p',
830
- String(backendPort),
831
- '-U',
832
- 'postgres',
833
- '-c',
834
- "CREATE DATABASE ferretdb WITH ENCODING 'UTF8';",
835
- ],
836
- { env: pgSpawnEnv, timeout: 30000 },
837
- ).catch(() => {
838
- // Ignore error if database already exists (error code 42P04)
839
- })
840
-
841
- // v2 only: Create DocumentDB extension
842
- // v1 uses plain PostgreSQL without DocumentDB
843
- if (!v1) {
844
- await spawnAsync(
845
- psql,
846
- [
847
- '-h',
848
- '127.0.0.1',
849
- '-p',
850
- String(backendPort),
851
- '-U',
852
- 'postgres',
853
- '-d',
854
- 'ferretdb',
855
- '-c',
856
- 'CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;',
857
- ],
858
- { env: pgSpawnEnv, timeout: 30000 },
859
- ).catch((error) => {
860
- logWarning(`Failed to create documentdb extension: ${error}`)
861
- // Continue anyway - extension might already exist
862
- })
863
- }
864
-
865
- logDebug('FerretDB database initialized')
866
- } catch (error) {
867
- logDebug(`Database initialization warning: ${error}`)
868
- // Continue - might already be initialized
869
- }
870
- }
871
-
872
- // 4. Start FerretDB proxy
873
- onProgress?.({ stage: 'starting', message: 'Starting FerretDB proxy...' })
874
-
875
- // Disable authentication for local development (similar to PostgreSQL trust auth)
876
- // FerretDB 2.x has auth enabled by default, but for local dev we disable it
877
- // Use a unique debug port to avoid conflicts when running multiple FerretDB containers
878
- const FERRETDB_DEBUG_PORT_OFFSET = 10000
879
- const FERRETDB_DEBUG_PORT_MAX_ATTEMPTS = 10
880
- let debugPort = port + FERRETDB_DEBUG_PORT_OFFSET
881
-
882
- // Probe for an available debug port, incrementing if the computed one is occupied
883
- let debugPortFound = false
884
- for (
885
- let attempt = 0;
886
- attempt < FERRETDB_DEBUG_PORT_MAX_ATTEMPTS;
887
- attempt++
888
- ) {
889
- const candidatePort = debugPort + attempt
890
- if (await isPortAvailable(candidatePort)) {
891
- debugPort = candidatePort
892
- debugPortFound = true
893
- break
894
- }
895
- logDebug(`Debug port ${candidatePort} is occupied, trying next...`)
896
- }
897
-
898
- if (!debugPortFound) {
899
- logWarning(
900
- `Could not find available debug port in range ${debugPort}-${debugPort + FERRETDB_DEBUG_PORT_MAX_ATTEMPTS - 1}, using computed port ${port + FERRETDB_DEBUG_PORT_OFFSET} anyway`,
901
- )
902
- debugPort = port + FERRETDB_DEBUG_PORT_OFFSET
903
- }
904
-
905
- logDebug(`Using debug port ${debugPort} for FerretDB HTTP debug handler`)
906
-
907
- // v1 uses plain PostgreSQL without TLS configured, so sslmode=disable is required
908
- // v2 uses postgresql-documentdb which handles SSL negotiation internally
909
- const pgUrl = isV1(version)
910
- ? `postgres://postgres@127.0.0.1:${backendPort}/ferretdb?sslmode=disable`
911
- : `postgres://postgres@127.0.0.1:${backendPort}/ferretdb`
912
-
913
- const ferretArgs = [
914
- '--listen-addr',
915
- `127.0.0.1:${port}`,
916
- '--postgresql-url',
917
- pgUrl,
918
- '--state-dir',
919
- containerDir,
920
- // v2 requires --no-auth to disable SCRAM authentication
921
- // v1 has auth disabled by default (flag doesn't exist)
922
- ...(isV1(version) ? [] : ['--no-auth']),
923
- '--debug-addr',
924
- `127.0.0.1:${debugPort}`,
925
- ]
926
-
927
- logDebug(`Starting FerretDB with args: ${ferretArgs.join(' ')}`)
928
-
929
- const spawnOpts: SpawnOptions = {
930
- stdio: ['ignore', 'pipe', 'pipe'],
931
- detached: true,
932
- // Run FerretDB in the container directory so telemetry.json/state.json
933
- // are written there instead of polluting the user's cwd
934
- cwd: containerDir,
935
- }
936
-
937
- const proc = spawn(ferretdbBinary, ferretArgs, spawnOpts)
938
-
939
- // Log output
940
- let stderrOutput = ''
941
- proc.stdout?.on('data', (data: Buffer) => {
942
- logDebug(`ferretdb stdout: ${data.toString()}`)
943
- })
944
- proc.stderr?.on('data', (data: Buffer) => {
945
- stderrOutput += data.toString()
946
- logDebug(`ferretdb stderr: ${data.toString()}`)
947
- })
948
-
949
- proc.unref()
950
-
951
- // Write PID file
952
- if (proc.pid) {
953
- await writeFile(ferretPidFile, String(proc.pid))
954
- ferretStarted = true
955
- }
956
-
957
- // 5. Wait for FerretDB to be ready
958
- const ferretReady = await waitForPort(port, 30000)
959
- if (!ferretReady) {
960
- throw new Error(
961
- `FerretDB failed to start within timeout. Stderr: ${stderrOutput}`,
962
- )
963
- }
964
-
965
- logDebug(`FerretDB started on port ${port}`)
966
-
967
- // Persist the allocated backend port if newly allocated
968
- if (!existingBackendPort && backendPort) {
969
- await containerManager.updateConfig(name, { backendPort })
970
- logDebug(`Persisted backend port ${backendPort} to container config`)
971
- }
972
-
973
- return {
974
- port,
975
- connectionString: this.getConnectionString(container),
976
- }
977
- } catch (error) {
978
- // Rollback: stop any started processes
979
- if (ferretStarted) {
980
- await this.stopFerretDBProcess(containerDir).catch(() => {})
981
- }
982
- if (pgStarted) {
983
- await this.stopPostgreSQLProcess(pgCtl, pgDataDir, pgSpawnEnv).catch(
984
- () => {},
985
- )
986
- }
987
- throw error
988
- }
989
- }
990
-
991
- /**
992
- * Stop FerretDB (reverse order: FerretDB first, then PostgreSQL)
993
- */
994
- async stop(container: ContainerConfig): Promise<void> {
995
- const { name, version, backendVersion } = container
996
- const { platform, arch } = this.getPlatformInfo()
997
- const v1 = isV1(version)
998
-
999
- const effectiveBackendVersion = v1
1000
- ? backendVersion || DEFAULT_V1_POSTGRESQL_VERSION
1001
- : backendVersion || DEFAULT_DOCUMENTDB_VERSION
1002
-
1003
- const { backendPath: documentdbPath, pgSpawnEnv } = this.getBackendPaths(
1004
- version,
1005
- effectiveBackendVersion,
1006
- platform,
1007
- arch,
1008
- )
1009
-
1010
- const ext = platformService.getExecutableExtension()
1011
- const pgCtl = join(documentdbPath, 'bin', `pg_ctl${ext}`)
1012
-
1013
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
1014
- const pgDataDir = join(containerDir, 'pg_data')
1015
-
1016
- logDebug(`Stopping FerretDB container "${name}"`)
1017
-
1018
- // 1. Stop FerretDB proxy
1019
- await this.stopFerretDBProcess(containerDir)
1020
-
1021
- // 2. Stop PostgreSQL
1022
- if (existsSync(pgCtl)) {
1023
- await this.stopPostgreSQLProcess(pgCtl, pgDataDir, pgSpawnEnv)
1024
- }
1025
-
1026
- // Kill pgweb if running for this container
1027
- await this.stopPgweb(name)
1028
-
1029
- logDebug('FerretDB stopped')
1030
- }
1031
-
1032
- /**
1033
- * Fix hardcoded Homebrew dylib paths in extension libraries.
1034
- *
1035
- * The x64 darwin build of postgresql-documentdb has extensions whose dylib
1036
- * load commands reference absolute Homebrew paths (e.g.
1037
- * /usr/local/opt/mongo-c-driver/lib/libbson2.2.dylib). When these paths
1038
- * don't exist on the target machine, the extension fails to load.
1039
- *
1040
- * This method scans extension dylibs with `otool -L`, finds missing
1041
- * dependencies, searches our bundle for matching libraries, and creates
1042
- * symlinks at the expected Homebrew paths.
1043
- */
1044
- private async fixDylibDependencies(documentdbPath: string): Promise<void> {
1045
- const libDir = join(documentdbPath, 'lib')
1046
- const pkgLibDir = join(libDir, 'postgresql')
1047
-
1048
- if (!existsSync(pkgLibDir)) return
1049
-
1050
- // Collect all .dylib files in our bundle's lib/ directory
1051
- const bundledLibNames = new Set<string>()
1052
- const bundledLibPaths = new Map<string, string>()
1053
- try {
1054
- const libFiles = await readdir(libDir)
1055
- for (const f of libFiles) {
1056
- if (f.endsWith('.dylib')) {
1057
- bundledLibNames.add(f)
1058
- bundledLibPaths.set(f, join(libDir, f))
1059
- }
1060
- }
1061
- } catch {
1062
- return
1063
- }
1064
-
1065
- // Scan extension dylibs for missing dependencies
1066
- let extFiles: string[]
1067
- try {
1068
- extFiles = (await readdir(pkgLibDir)).filter((f) => f.endsWith('.dylib'))
1069
- } catch {
1070
- return
1071
- }
1072
-
1073
- for (const extFile of extFiles) {
1074
- const extPath = join(pkgLibDir, extFile)
1075
- try {
1076
- const { stdout } = await execAsync(`otool -L "${extPath}"`, {
1077
- timeout: 5000,
1078
- })
1079
-
1080
- for (const line of stdout.split('\n')) {
1081
- const match = line.trim().match(/^(\/[^\s]+\.dylib)\s/)
1082
- if (!match) continue
1083
- const depPath = match[1]
1084
-
1085
- // Skip system libs, @-prefixed paths, and paths in our bundle
1086
- if (depPath.startsWith('/usr/lib/')) continue
1087
- if (depPath.startsWith('/System/')) continue
1088
- if (depPath.startsWith('@')) continue
1089
- if (depPath.includes(documentdbPath)) continue
1090
-
1091
- if (!existsSync(depPath)) {
1092
- const depName = basename(depPath)
1093
-
1094
- // Check if we have this exact library in our bundle
1095
- if (bundledLibPaths.has(depName)) {
1096
- try {
1097
- await mkdir(dirname(depPath), { recursive: true })
1098
- } catch {
1099
- logDebug(
1100
- `Cannot create directory for dylib dep: ${dirname(depPath)} (skipping)`,
1101
- )
1102
- continue
1103
- }
1104
- try {
1105
- await symlink(bundledLibPaths.get(depName)!, depPath)
1106
- logDebug(
1107
- `Fixed dylib dep: ${depPath} -> ${bundledLibPaths.get(depName)}`,
1108
- )
1109
- } catch {
1110
- // Symlink may already exist from a parallel fix
1111
- }
1112
- } else {
1113
- logDebug(
1114
- `Missing dylib dependency: ${depPath} (not found in bundle)`,
1115
- )
1116
- }
1117
- }
1118
- }
1119
- } catch {
1120
- logDebug(`Could not scan dylib deps for ${extFile}`)
1121
- }
1122
- }
1123
- }
1124
-
1125
- /**
1126
- * Stop FerretDB proxy process
1127
- */
1128
- private async stopFerretDBProcess(containerDir: string): Promise<void> {
1129
- const pidFile = join(containerDir, 'ferretdb.pid')
1130
-
1131
- if (existsSync(pidFile)) {
1132
- let pid = NaN
1133
- try {
1134
- const pidContent = await readFile(pidFile, 'utf8')
1135
- pid = parseInt(pidContent.trim(), 10)
1136
- } catch {
1137
- // PID file unreadable — clean it up below
1138
- }
1139
-
1140
- if (!isNaN(pid) && platformService.isProcessRunning(pid)) {
1141
- logDebug(`Killing FerretDB process ${pid}`)
1142
-
1143
- // On Windows, taskkill without /F sends WM_CLOSE which console/server
1144
- // processes ignore, causing an error. Use force kill directly.
1145
- if (isWindows()) {
1146
- try {
1147
- await platformService.terminateProcess(pid, true)
1148
- } catch {
1149
- logDebug(`Force kill of FerretDB process ${pid} failed`)
1150
- }
1151
- } else {
1152
- // Unix: try graceful SIGTERM first, then SIGKILL
1153
- try {
1154
- await platformService.terminateProcess(pid, false)
1155
- } catch {
1156
- // Graceful termination failed — force kill below
1157
- }
1158
-
1159
- // Poll until process exits or timeout (10 seconds)
1160
- const maxWaitMs = 10000
1161
- const pollIntervalMs = 200
1162
- const startTime = Date.now()
1163
-
1164
- while (Date.now() - startTime < maxWaitMs) {
1165
- if (!platformService.isProcessRunning(pid)) {
1166
- logDebug(`FerretDB process ${pid} terminated gracefully`)
1167
- break
1168
- }
1169
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
1170
- }
1171
-
1172
- // Force kill if still running after timeout
1173
- if (platformService.isProcessRunning(pid)) {
1174
- logWarning(`Graceful termination timed out, force killing ${pid}`)
1175
- try {
1176
- await platformService.terminateProcess(pid, true)
1177
- } catch {
1178
- logDebug(`Force kill of FerretDB process ${pid} failed`)
1179
- }
1180
- }
1181
- }
1182
-
1183
- // Wait briefly for process to fully exit after force kill
1184
- const exitWaitMs = isWindows() ? 3000 : 1000
1185
- const pollMs = 100
1186
- const exitStart = Date.now()
1187
- while (Date.now() - exitStart < exitWaitMs) {
1188
- if (!platformService.isProcessRunning(pid)) break
1189
- await new Promise((resolve) => setTimeout(resolve, pollMs))
1190
- }
1191
- }
1192
-
1193
- // Always clean up PID file
1194
- await unlink(pidFile).catch(() => {})
1195
- }
1196
- }
1197
-
1198
- /**
1199
- * Stop PostgreSQL process
1200
- */
1201
- private async stopPostgreSQLProcess(
1202
- pgCtl: string,
1203
- pgDataDir: string,
1204
- spawnEnv?: Record<string, string>,
1205
- ): Promise<void> {
1206
- if (isWindows()) {
1207
- // On Windows, use exec() instead of spawnAsync() to avoid pipe-related
1208
- // hangs (same issue as pg_ctl start -w). pg_ctl stop -w can block when
1209
- // stdout/stderr pipes prevent the child process from exiting cleanly.
1210
- try {
1211
- await execAsync(`"${pgCtl}" stop -D "${pgDataDir}" -m fast -w`, {
1212
- timeout: 30000,
1213
- env: spawnEnv ? { ...process.env, ...spawnEnv } : undefined,
1214
- })
1215
- logDebug('PostgreSQL stopped')
1216
- } catch (error) {
1217
- logDebug(`pg_ctl stop error: ${error}`)
1218
- try {
1219
- await execAsync(`"${pgCtl}" stop -D "${pgDataDir}" -m immediate -w`, {
1220
- timeout: 15000,
1221
- env: spawnEnv ? { ...process.env, ...spawnEnv } : undefined,
1222
- })
1223
- } catch {
1224
- logWarning('Failed to stop PostgreSQL gracefully')
1225
- }
1226
- }
1227
- } else {
1228
- try {
1229
- await spawnAsync(pgCtl, ['stop', '-D', pgDataDir, '-m', 'fast', '-w'], {
1230
- env: spawnEnv,
1231
- timeout: 30000,
1232
- })
1233
- logDebug('PostgreSQL stopped')
1234
- } catch (error) {
1235
- logDebug(`pg_ctl stop error: ${error}`)
1236
- try {
1237
- await spawnAsync(
1238
- pgCtl,
1239
- ['stop', '-D', pgDataDir, '-m', 'immediate', '-w'],
1240
- { env: spawnEnv, timeout: 15000 },
1241
- )
1242
- } catch {
1243
- logWarning('Failed to stop PostgreSQL gracefully')
1244
- }
1245
- }
1246
- }
1247
- }
1248
-
1249
- // Get FerretDB server status
1250
- async status(container: ContainerConfig): Promise<StatusResult> {
1251
- const { name, port } = container
1252
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
1253
- const pidFile = join(containerDir, 'ferretdb.pid')
1254
-
1255
- // Check if FerretDB is responding
1256
- const isOpen = await new Promise<boolean>((resolve) => {
1257
- const socket = new net.Socket()
1258
- socket.setTimeout(1000)
1259
- socket.once('connect', () => {
1260
- socket.destroy()
1261
- resolve(true)
1262
- })
1263
- socket.once('error', () => {
1264
- socket.destroy()
1265
- resolve(false)
1266
- })
1267
- socket.once('timeout', () => {
1268
- socket.destroy()
1269
- resolve(false)
1270
- })
1271
- socket.connect(port, '127.0.0.1')
1272
- })
1273
-
1274
- if (isOpen) {
1275
- return { running: true, message: 'FerretDB is running' }
1276
- }
1277
-
1278
- // Check PID file
1279
- if (existsSync(pidFile)) {
1280
- try {
1281
- const content = await readFile(pidFile, 'utf8')
1282
- const pid = parseInt(content.trim(), 10)
1283
- if (!isNaN(pid) && pid > 0 && platformService.isProcessRunning(pid)) {
1284
- return {
1285
- running: true,
1286
- message: `FerretDB is running (PID: ${pid})`,
1287
- }
1288
- }
1289
- } catch {
1290
- // Ignore
1291
- }
1292
- }
1293
-
1294
- return { running: false, message: 'FerretDB is not running' }
1295
- }
1296
-
1297
- // Detect backup format
1298
- async detectBackupFormat(filePath: string): Promise<BackupFormat> {
1299
- return detectBackupFormatImpl(filePath)
1300
- }
1301
-
1302
- // Restore a backup
1303
- async restore(
1304
- container: ContainerConfig,
1305
- backupPath: string,
1306
- options: Record<string, unknown> = {},
1307
- ): Promise<RestoreResult> {
1308
- const { backendPort } = container
1309
- const database = (options.database as string) || 'ferretdb'
1310
-
1311
- if (!backendPort) {
1312
- throw new Error('Backend port not set - start the container first')
1313
- }
1314
-
1315
- // Validate database name before restore (defense-in-depth)
1316
- assertValidDatabaseName(database)
1317
-
1318
- const result = await restoreBackup(container, backupPath, {
1319
- database,
1320
- drop: options.drop !== false,
1321
- })
1322
-
1323
- // Restart FerretDB proxy so it picks up the restored data.
1324
- // pg_restore writes directly to PostgreSQL, but FerretDB's proxy
1325
- // caches schema/collection metadata in memory and won't see
1326
- // the restored collections until restarted.
1327
- const containerDir = paths.getContainerPath(container.name, {
1328
- engine: ENGINE,
1329
- })
1330
- try {
1331
- await this.stopFerretDBProcess(containerDir)
1332
- // start() detects PG is already running and only launches the proxy
1333
- await this.start(container)
1334
- } catch (error) {
1335
- const err = error as Error
1336
- logWarning(
1337
- `Failed to restart FerretDB proxy after restore: ${err.message}`,
1338
- )
1339
- // Retry once — transient issues (port race, slow PG) can resolve on second attempt
1340
- try {
1341
- await this.stopFerretDBProcess(containerDir).catch(() => {})
1342
- await this.start(container)
1343
- } catch {
1344
- throw new Error(
1345
- `Restore succeeded but FerretDB proxy failed to restart. ` +
1346
- `Data is safely in PostgreSQL. Run 'spindb start ${container.name}' to restart manually. ` +
1347
- `Original error: ${err.message}`,
1348
- )
1349
- }
1350
- }
1351
-
1352
- return result
1353
- }
1354
-
1355
- // Get connection string (MongoDB-compatible)
1356
- getConnectionString(container: ContainerConfig, database?: string): string {
1357
- const { port } = container
1358
- const db = database || container.database || 'test'
1359
- // No authentication required - FerretDB runs with --no-auth for local dev
1360
- return `mongodb://127.0.0.1:${port}/${db}`
1361
- }
1362
-
1363
- // Get PostgreSQL backend connection string (for debugging)
1364
- getBackendConnectionString(container: ContainerConfig): string {
1365
- const { backendPort } = container
1366
- if (!backendPort) {
1367
- throw new Error(
1368
- 'Backend port not available - start the container first to allocate a port',
1369
- )
1370
- }
1371
- return `postgresql://postgres@127.0.0.1:${backendPort}/ferretdb`
1372
- }
1373
-
1374
- /**
1375
- * Get the path to mongosh (uses MongoDB's mongosh)
1376
- * FerretDB is MongoDB-compatible, so it uses the same shell
1377
- */
1378
- override async getMongoshPath(): Promise<string> {
1379
- const cached = await configManager.getBinaryPath('mongosh')
1380
- if (cached && existsSync(cached)) return cached
1381
-
1382
- // Try to find in PATH as fallback
1383
- const detected = await platformService.findToolPath('mongosh')
1384
- if (detected) {
1385
- await configManager.setBinaryPath('mongosh', detected, 'system')
1386
- return detected
1387
- }
1388
-
1389
- throw new Error(
1390
- 'mongosh not found. To connect to FerretDB, install mongosh:\n' +
1391
- ' Download from: https://www.mongodb.com/try/download/shell\n' +
1392
- ' Or download MongoDB binaries: spindb engines download mongodb <version>',
1393
- )
1394
- }
1395
-
1396
- // Open mongosh interactive shell
1397
- async connect(container: ContainerConfig, database?: string): Promise<void> {
1398
- const { port } = container
1399
- const db = database || 'test'
1400
-
1401
- const mongosh = await this.getMongoshPath()
1402
-
1403
- const spawnOptions: SpawnOptions = {
1404
- stdio: 'inherit',
1405
- }
1406
-
1407
- return new Promise((resolve, reject) => {
1408
- const proc = spawn(
1409
- mongosh,
1410
- ['--host', '127.0.0.1', '--port', String(port), db],
1411
- spawnOptions,
1412
- )
1413
-
1414
- proc.on('error', reject)
1415
- proc.on('close', () => resolve())
1416
- })
1417
- }
1418
-
1419
- /**
1420
- * Create a new database
1421
- * FerretDB/MongoDB creates databases implicitly when you first write to them.
1422
- * To force immediate creation, we create a temporary collection and drop it.
1423
- * This leaves the database visible in tools without any marker clutter.
1424
- */
1425
- async createDatabase(
1426
- container: ContainerConfig,
1427
- database: string,
1428
- ): Promise<void> {
1429
- assertValidDatabaseName(database)
1430
- const { port } = container
1431
-
1432
- try {
1433
- const mongosh = await this.getMongoshPath()
1434
- // Create a temp collection then immediately drop it to force database creation
1435
- // without leaving any visible marker collections.
1436
- // Pre-drop in case a previous run was interrupted and left a stale collection.
1437
- // NOTE: Use db.getCollection() instead of db._spindb_init shorthand because
1438
- // mongosh doesn't support shorthand notation for collection names starting with underscore.
1439
- const script =
1440
- 'try { db.getCollection("_spindb_init").drop(); } catch(e) {} db.createCollection("_spindb_init"); db.getCollection("_spindb_init").drop();'
1441
- const cmd = isWindows()
1442
- ? `"${mongosh}" --host 127.0.0.1 --port ${port} ${database} --eval "${script.replace(/"/g, '\\"')}"`
1443
- : `"${mongosh}" --host 127.0.0.1 --port ${port} ${database} --eval '${script}'`
1444
-
1445
- await execAsync(cmd, { timeout: 10000 })
1446
- logDebug(`Database "${database}" created via temp collection`)
1447
- } catch (error) {
1448
- // Ignore errors - database may already exist or collection cleanup succeeded
1449
- logDebug(`createDatabase result: ${error}`)
1450
- }
1451
- }
1452
-
1453
- // Drop a database
1454
- async dropDatabase(
1455
- container: ContainerConfig,
1456
- database: string,
1457
- ): Promise<void> {
1458
- assertValidDatabaseName(database)
1459
- const { port } = container
1460
-
1461
- try {
1462
- const mongosh = await this.getMongoshPath()
1463
- const cmd = isWindows()
1464
- ? `"${mongosh}" --host 127.0.0.1 --port ${port} ${database} --eval "db.dropDatabase()"`
1465
- : `"${mongosh}" --host 127.0.0.1 --port ${port} ${database} --eval 'db.dropDatabase()'`
1466
-
1467
- await execAsync(cmd, { timeout: 10000 })
1468
- } catch (error) {
1469
- logDebug(`dropDatabase result: ${error}`)
1470
- }
1471
- }
1472
-
1473
- // Get the size of the database in bytes
1474
- async getDatabaseSize(container: ContainerConfig): Promise<number | null> {
1475
- const { port, database } = container
1476
- const db = database || 'test'
1477
- assertValidDatabaseName(db)
1478
-
1479
- try {
1480
- const mongosh = await this.getMongoshPath()
1481
- const script = 'JSON.stringify(db.stats())'
1482
- const cmd = isWindows()
1483
- ? `"${mongosh}" --host 127.0.0.1 --port ${port} ${db} --quiet --eval "${script}"`
1484
- : `"${mongosh}" --host 127.0.0.1 --port ${port} ${db} --quiet --eval '${script}'`
1485
-
1486
- const { stdout } = await execAsync(cmd, { timeout: 10000 })
1487
-
1488
- // Extract JSON from output
1489
- const firstBrace = stdout.indexOf('{')
1490
- const lastBrace = stdout.lastIndexOf('}')
1491
- if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
1492
- const stats = JSON.parse(stdout.substring(firstBrace, lastBrace + 1))
1493
- const dataSize = Number(stats?.dataSize)
1494
- return Number.isFinite(dataSize) && dataSize > 0 ? dataSize : null
1495
- }
1496
- return null
1497
- } catch {
1498
- return null
1499
- }
1500
- }
1501
-
1502
- // Create a dump from a remote database
1503
- async dumpFromConnectionString(
1504
- connectionString: string,
1505
- outputPath: string,
1506
- ): Promise<DumpResult> {
1507
- // Use mongodump if available
1508
- const mongodump = await configManager.getBinaryPath('mongodump')
1509
- if (!mongodump) {
1510
- throw new Error(
1511
- 'mongodump not found. Download MongoDB binaries:\n' +
1512
- ' Run: spindb engines download mongodb <version>',
1513
- )
1514
- }
1515
-
1516
- const args = [
1517
- '--uri',
1518
- connectionString,
1519
- '--archive=' + outputPath,
1520
- '--gzip',
1521
- ]
1522
-
1523
- const spawnOptions: SpawnOptions = {
1524
- stdio: ['pipe', 'pipe', 'pipe'],
1525
- }
1526
-
1527
- return new Promise((resolve, reject) => {
1528
- const proc = spawn(mongodump, args, spawnOptions)
1529
-
1530
- let stdout = ''
1531
- let stderr = ''
1532
-
1533
- proc.stdout?.on('data', (data: Buffer) => {
1534
- stdout += data.toString()
1535
- })
1536
- proc.stderr?.on('data', (data: Buffer) => {
1537
- stderr += data.toString()
1538
- })
1539
-
1540
- proc.on('error', reject)
1541
-
1542
- proc.on('close', (code) => {
1543
- if (code === 0) {
1544
- resolve({
1545
- filePath: outputPath,
1546
- stdout,
1547
- stderr,
1548
- code,
1549
- })
1550
- } else {
1551
- reject(new Error(stderr || `mongodump exited with code ${code}`))
1552
- }
1553
- })
1554
- })
1555
- }
1556
-
1557
- // Create a backup
1558
- async backup(
1559
- container: ContainerConfig,
1560
- outputPath: string,
1561
- options: BackupOptions,
1562
- ): Promise<BackupResult> {
1563
- return createBackup(container, outputPath, options)
1564
- }
1565
-
1566
- // Run a JavaScript file or inline script against the database
1567
- async runScript(
1568
- container: ContainerConfig,
1569
- options: { file?: string; sql?: string; database?: string },
1570
- ): Promise<void> {
1571
- const { port } = container
1572
- const db = options.database || container.database || 'test'
1573
-
1574
- const mongosh = await this.getMongoshPath()
1575
-
1576
- if (options.file) {
1577
- const spawnOptions: SpawnOptions = {
1578
- stdio: 'inherit',
1579
- }
1580
-
1581
- return new Promise((resolve, reject) => {
1582
- const proc = spawn(
1583
- mongosh,
1584
- [
1585
- '--host',
1586
- '127.0.0.1',
1587
- '--port',
1588
- String(port),
1589
- db,
1590
- '--file',
1591
- options.file!,
1592
- ],
1593
- spawnOptions,
1594
- )
1595
-
1596
- proc.on('error', reject)
1597
- proc.on('close', (code) => {
1598
- if (code === 0) {
1599
- resolve()
1600
- } else {
1601
- reject(new Error(`mongosh exited with code ${code}`))
1602
- }
1603
- })
1604
- })
1605
- } else if (options.sql) {
1606
- // sql field is actually JS for MongoDB-compatible databases
1607
- const script = options.sql
1608
-
1609
- return new Promise((resolve, reject) => {
1610
- const proc = spawn(
1611
- mongosh,
1612
- ['--host', '127.0.0.1', '--port', String(port), db, '--eval', script],
1613
- { stdio: ['pipe', 'pipe', 'pipe'] },
1614
- )
1615
-
1616
- let stdout = ''
1617
- let stderr = ''
1618
-
1619
- proc.stdout?.on('data', (data: Buffer) => {
1620
- stdout += data.toString()
1621
- })
1622
- proc.stderr?.on('data', (data: Buffer) => {
1623
- stderr += data.toString()
1624
- })
1625
-
1626
- // 60 second timeout
1627
- const timeout = setTimeout(() => {
1628
- proc.kill('SIGTERM')
1629
- reject(new Error('mongosh timed out after 60 seconds'))
1630
- }, 60000)
1631
-
1632
- proc.on('error', (err) => {
1633
- clearTimeout(timeout)
1634
- reject(err)
1635
- })
1636
-
1637
- proc.on('close', (code) => {
1638
- clearTimeout(timeout)
1639
- if (stdout) process.stdout.write(stdout)
1640
- if (stderr) process.stderr.write(stderr)
1641
- if (code === 0) {
1642
- resolve()
1643
- } else {
1644
- reject(new Error(`mongosh exited with code ${code}`))
1645
- }
1646
- })
1647
- })
1648
- } else {
1649
- throw new Error('Either file or sql option must be provided')
1650
- }
1651
- }
1652
-
1653
- /**
1654
- * Execute a query and return structured results
1655
- * FerretDB uses MongoDB JavaScript syntax
1656
- *
1657
- * Examples:
1658
- * db.users.find({active: true})
1659
- * db.orders.countDocuments()
1660
- */
1661
- async executeQuery(
1662
- container: ContainerConfig,
1663
- query: string,
1664
- options?: QueryOptions,
1665
- ): Promise<QueryResult> {
1666
- const { port } = container
1667
- const db = options?.database || container.database || 'test'
1668
-
1669
- const mongosh = await this.getMongoshPath()
1670
-
1671
- // Normalize query - only prepend "db." for collection operations
1672
- // Collection operations match pattern: identifier.method(...) e.g., "users.find({})"
1673
- // Non-collection queries (show dbs, arbitrary JS) are rejected with clear error
1674
- let normalizedQuery = query.trim()
1675
- if (!normalizedQuery.startsWith('db.')) {
1676
- // Check if it looks like a collection operation: identifier.method(
1677
- const collectionOpPattern =
1678
- /^[a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_][a-zA-Z0-9_]*\s*\(/
1679
- if (collectionOpPattern.test(normalizedQuery)) {
1680
- normalizedQuery = `db.${normalizedQuery}`
1681
- } else {
1682
- throw new Error(
1683
- 'Invalid query format. Expected a collection operation like "users.find({})" or "db.users.find({})"\n' +
1684
- 'Shell commands like "show dbs" and "use dbname" are not supported in executeQuery.',
1685
- )
1686
- }
1687
- }
1688
-
1689
- // Wrap query in async IIFE to properly await cursor.toArray()
1690
- // This prevents JSON.stringify from serializing a Promise
1691
- const script = `(async () => { const res = ${normalizedQuery}; return JSON.stringify(res.toArray ? await res.toArray() : await Promise.resolve(res)); })()`
1692
-
1693
- return new Promise((resolve, reject) => {
1694
- const args = [
1695
- '--host',
1696
- '127.0.0.1',
1697
- '--port',
1698
- String(port),
1699
- db,
1700
- '--quiet',
1701
- '--eval',
1702
- script,
1703
- ]
1704
-
1705
- const proc = spawn(mongosh, args, {
1706
- stdio: ['pipe', 'pipe', 'pipe'],
1707
- })
1708
-
1709
- let stdout = ''
1710
- let stderr = ''
1711
-
1712
- proc.stdout?.on('data', (data: Buffer) => {
1713
- stdout += data.toString()
1714
- })
1715
- proc.stderr?.on('data', (data: Buffer) => {
1716
- stderr += data.toString()
1717
- })
1718
-
1719
- const timeout = setTimeout(() => {
1720
- proc.kill('SIGTERM')
1721
- reject(new Error('Query timed out after 60 seconds'))
1722
- }, 60000)
1723
-
1724
- proc.on('error', (err) => {
1725
- clearTimeout(timeout)
1726
- reject(err)
1727
- })
1728
-
1729
- proc.on('close', (code) => {
1730
- clearTimeout(timeout)
1731
- if (code !== 0) {
1732
- reject(new Error(stderr || `mongosh exited with code ${code}`))
1733
- return
1734
- }
1735
-
1736
- try {
1737
- // Extract JSON from output (mongosh may output extra info)
1738
- const jsonStart = stdout.indexOf('[')
1739
- const jsonEnd = stdout.lastIndexOf(']')
1740
-
1741
- if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) {
1742
- const jsonStr = stdout.substring(jsonStart, jsonEnd + 1)
1743
- resolve(parseMongoDBResult(jsonStr))
1744
- } else {
1745
- // Try parsing as single object or scalar
1746
- const objStart = stdout.indexOf('{')
1747
- const objEnd = stdout.lastIndexOf('}')
1748
- if (objStart !== -1 && objEnd !== -1 && objEnd > objStart) {
1749
- const jsonStr = stdout.substring(objStart, objEnd + 1)
1750
- resolve(parseMongoDBResult(jsonStr))
1751
- } else {
1752
- // Return as scalar result
1753
- resolve({
1754
- columns: ['result'],
1755
- rows: [{ result: stdout.trim() }],
1756
- rowCount: 1,
1757
- })
1758
- }
1759
- }
1760
- } catch (error) {
1761
- reject(
1762
- new Error(
1763
- `Failed to parse query result: ${error instanceof Error ? error.message : error}`,
1764
- ),
1765
- )
1766
- }
1767
- })
1768
- })
1769
- }
1770
-
1771
- /**
1772
- * List all user databases, excluding system databases (admin, config, local).
1773
- * FerretDB uses MongoDB protocol, so same approach as MongoDB.
1774
- */
1775
- async listDatabases(container: ContainerConfig): Promise<string[]> {
1776
- const { port } = container
1777
- const mongosh = await this.getMongoshPath()
1778
-
1779
- return new Promise((resolve, reject) => {
1780
- // Use JSON output for reliable parsing
1781
- const script = `JSON.stringify(db.adminCommand({listDatabases: 1}).databases.map(d => d.name))`
1782
- const args = [
1783
- '--quiet',
1784
- '--host',
1785
- '127.0.0.1',
1786
- '--port',
1787
- String(port),
1788
- '--eval',
1789
- script,
1790
- ]
1791
-
1792
- const proc = spawn(mongosh, args, {
1793
- stdio: ['ignore', 'pipe', 'pipe'],
1794
- })
1795
-
1796
- let stdout = ''
1797
- let stderr = ''
1798
-
1799
- proc.stdout?.on('data', (data: Buffer) => {
1800
- stdout += data.toString()
1801
- })
1802
- proc.stderr?.on('data', (data: Buffer) => {
1803
- stderr += data.toString()
1804
- })
1805
-
1806
- proc.on('error', reject)
1807
-
1808
- proc.on('close', (code) => {
1809
- if (code !== 0) {
1810
- reject(new Error(stderr || `mongosh exited with code ${code}`))
1811
- return
1812
- }
1813
-
1814
- try {
1815
- const allDatabases = JSON.parse(stdout.trim()) as string[]
1816
- const systemDatabases = ['admin', 'config', 'local']
1817
- const databases = allDatabases.filter(
1818
- (db) => !systemDatabases.includes(db),
1819
- )
1820
- resolve(databases)
1821
- } catch (error) {
1822
- reject(new Error(`Failed to parse database list: ${error}`))
1823
- }
1824
- })
1825
- })
1826
- }
1827
-
1828
- async createUser(
1829
- container: ContainerConfig,
1830
- options: CreateUserOptions,
1831
- ): Promise<UserCredentials> {
1832
- const { username, password, database } = options
1833
- assertValidUsername(username)
1834
- const { port } = container
1835
- const db = database ?? container.database ?? 'admin'
1836
- assertValidDatabaseName(db)
1837
- const mongosh = await this.getMongoshPath()
1838
-
1839
- // Same as MongoDB - auth disabled with --no-auth but user is still created
1840
- // Use JSON.stringify for password to safely escape all special characters in JS context
1841
- // Pass script via stdin to avoid exposing passwords in process listings
1842
- const jsonPwd = JSON.stringify(password)
1843
- const script = `db.getSiblingDB('${db}').createUser({user:'${username}',pwd:${jsonPwd},roles:[{role:'readWrite',db:'${db}'}]})`
1844
-
1845
- const mongoshArgs = ['--host', '127.0.0.1', '--port', String(port), 'admin']
1846
-
1847
- const runMongoshViaStdin = (js: string): Promise<void> =>
1848
- new Promise((resolve, reject) => {
1849
- const proc = spawn(mongosh, mongoshArgs, {
1850
- stdio: ['pipe', 'pipe', 'pipe'],
1851
- })
1852
-
1853
- let stderr = ''
1854
- proc.stderr?.on('data', (data: Buffer) => {
1855
- stderr += data.toString()
1856
- })
1857
-
1858
- const timeout = setTimeout(() => {
1859
- proc.kill('SIGTERM')
1860
- reject(new Error('mongosh timed out after 10 seconds'))
1861
- }, 10000)
1862
-
1863
- proc.on('error', (err) => {
1864
- clearTimeout(timeout)
1865
- reject(err)
1866
- })
1867
-
1868
- proc.on('close', (code) => {
1869
- clearTimeout(timeout)
1870
- if (code === 0) resolve()
1871
- else reject(new Error(stderr || `mongosh exited with code ${code}`))
1872
- })
1873
-
1874
- proc.stdin?.write(js)
1875
- proc.stdin?.end()
1876
- })
1877
-
1878
- try {
1879
- await runMongoshViaStdin(script)
1880
- } catch (error) {
1881
- const err = error as Error
1882
- if (
1883
- err.message.includes('51003') ||
1884
- err.message.includes('already exists')
1885
- ) {
1886
- // User exists — update password instead
1887
- const updateScript = `db.getSiblingDB('${db}').updateUser('${username}',{pwd:${jsonPwd}})`
1888
- await runMongoshViaStdin(updateScript)
1889
- } else {
1890
- throw error
1891
- }
1892
- }
1893
-
1894
- const connectionString = `mongodb://${encodeURIComponent(username)}:${encodeURIComponent(password)}@127.0.0.1:${port}/${db}`
1895
-
1896
- return {
1897
- username,
1898
- password,
1899
- connectionString,
1900
- engine: container.engine,
1901
- container: container.name,
1902
- database: db,
1903
- }
1904
- }
1905
- }
1906
-
1907
- export const ferretdbEngine = new FerretDBEngine()