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