spindb 0.37.2 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (811) hide show
  1. package/dist/cli/bin.js +9 -0
  2. package/dist/cli/bin.js.map +1 -0
  3. package/dist/cli/commands/attach.js +102 -0
  4. package/dist/cli/commands/attach.js.map +1 -0
  5. package/dist/cli/commands/backup.js +197 -0
  6. package/dist/cli/commands/backup.js.map +1 -0
  7. package/dist/cli/commands/backups.js +190 -0
  8. package/dist/cli/commands/backups.js.map +1 -0
  9. package/dist/cli/commands/clone.js +119 -0
  10. package/dist/cli/commands/clone.js.map +1 -0
  11. package/dist/cli/commands/config.js +276 -0
  12. package/dist/cli/commands/config.js.map +1 -0
  13. package/dist/cli/commands/connect.js +559 -0
  14. package/dist/cli/commands/connect.js.map +1 -0
  15. package/dist/cli/commands/create.js +952 -0
  16. package/dist/cli/commands/create.js.map +1 -0
  17. package/dist/cli/commands/databases.js +485 -0
  18. package/dist/cli/commands/databases.js.map +1 -0
  19. package/dist/cli/commands/delete.js +106 -0
  20. package/dist/cli/commands/delete.js.map +1 -0
  21. package/dist/cli/commands/deps.js +238 -0
  22. package/dist/cli/commands/deps.js.map +1 -0
  23. package/dist/cli/commands/detach.js +81 -0
  24. package/dist/cli/commands/detach.js.map +1 -0
  25. package/dist/cli/commands/doctor.js +567 -0
  26. package/dist/cli/commands/doctor.js.map +1 -0
  27. package/dist/cli/commands/duckdb.js +207 -0
  28. package/dist/cli/commands/duckdb.js.map +1 -0
  29. package/dist/cli/commands/edit.js +524 -0
  30. package/dist/cli/commands/edit.js.map +1 -0
  31. package/dist/cli/commands/engines.js +1414 -0
  32. package/dist/cli/commands/engines.js.map +1 -0
  33. package/dist/cli/commands/export.js +383 -0
  34. package/dist/cli/commands/export.js.map +1 -0
  35. package/dist/cli/commands/info.js +270 -0
  36. package/dist/cli/commands/info.js.map +1 -0
  37. package/dist/cli/commands/list.js +215 -0
  38. package/dist/cli/commands/list.js.map +1 -0
  39. package/dist/cli/commands/logs.js +81 -0
  40. package/dist/cli/commands/logs.js.map +1 -0
  41. package/dist/cli/commands/menu/backup-handlers.js +1202 -0
  42. package/dist/cli/commands/menu/backup-handlers.js.map +1 -0
  43. package/dist/cli/commands/menu/container-handlers.js +1788 -0
  44. package/dist/cli/commands/menu/container-handlers.js.map +1 -0
  45. package/dist/cli/commands/menu/engine-handlers.js +235 -0
  46. package/dist/cli/commands/menu/engine-handlers.js.map +1 -0
  47. package/dist/cli/commands/menu/index.js +266 -0
  48. package/dist/cli/commands/menu/index.js.map +1 -0
  49. package/dist/cli/commands/menu/settings-handlers.js +320 -0
  50. package/dist/cli/commands/menu/settings-handlers.js.map +1 -0
  51. package/dist/cli/commands/menu/shared.js +13 -0
  52. package/dist/cli/commands/menu/shared.js.map +1 -0
  53. package/dist/cli/commands/menu/shell-handlers.js +1573 -0
  54. package/dist/cli/commands/menu/shell-handlers.js.map +1 -0
  55. package/dist/cli/commands/menu/sql-handlers.js +185 -0
  56. package/dist/cli/commands/menu/sql-handlers.js.map +1 -0
  57. package/dist/cli/commands/menu/update-handlers.js +322 -0
  58. package/dist/cli/commands/menu/update-handlers.js.map +1 -0
  59. package/dist/cli/commands/menu/validators.js +9 -0
  60. package/dist/cli/commands/menu/validators.js.map +1 -0
  61. package/dist/cli/commands/ports.js +166 -0
  62. package/dist/cli/commands/ports.js.map +1 -0
  63. package/dist/cli/commands/pull.js +166 -0
  64. package/dist/cli/commands/pull.js.map +1 -0
  65. package/dist/cli/commands/query.js +180 -0
  66. package/dist/cli/commands/query.js.map +1 -0
  67. package/dist/cli/commands/restore.js +428 -0
  68. package/dist/cli/commands/restore.js.map +1 -0
  69. package/dist/cli/commands/run.js +115 -0
  70. package/dist/cli/commands/run.js.map +1 -0
  71. package/dist/cli/commands/self-update.js +99 -0
  72. package/dist/cli/commands/self-update.js.map +1 -0
  73. package/dist/cli/commands/sqlite.js +207 -0
  74. package/dist/cli/commands/sqlite.js.map +1 -0
  75. package/dist/cli/commands/start.js +196 -0
  76. package/dist/cli/commands/start.js.map +1 -0
  77. package/dist/cli/commands/stop.js +182 -0
  78. package/dist/cli/commands/stop.js.map +1 -0
  79. package/dist/cli/commands/url.js +88 -0
  80. package/dist/cli/commands/url.js.map +1 -0
  81. package/dist/cli/commands/users.js +189 -0
  82. package/dist/cli/commands/users.js.map +1 -0
  83. package/dist/cli/commands/version.js +52 -0
  84. package/dist/cli/commands/version.js.map +1 -0
  85. package/dist/cli/commands/which.js +258 -0
  86. package/dist/cli/commands/which.js.map +1 -0
  87. package/dist/cli/constants.js +212 -0
  88. package/dist/cli/constants.js.map +1 -0
  89. package/dist/cli/helpers.js +1120 -0
  90. package/dist/cli/helpers.js.map +1 -0
  91. package/dist/cli/index.js +146 -0
  92. package/dist/cli/index.js.map +1 -0
  93. package/dist/cli/ui/prompts.js +1002 -0
  94. package/dist/cli/ui/prompts.js.map +1 -0
  95. package/dist/cli/ui/spinner.js +74 -0
  96. package/dist/cli/ui/spinner.js.map +1 -0
  97. package/dist/cli/ui/theme.js +99 -0
  98. package/dist/cli/ui/theme.js.map +1 -0
  99. package/dist/cli/utils/file-follower.js +79 -0
  100. package/dist/cli/utils/file-follower.js.map +1 -0
  101. package/dist/config/backup-formats.js +363 -0
  102. package/dist/config/backup-formats.js.map +1 -0
  103. package/dist/config/defaults.js +25 -0
  104. package/dist/config/defaults.js.map +1 -0
  105. package/dist/config/engine-defaults.js +303 -0
  106. package/dist/config/engine-defaults.js.map +1 -0
  107. package/dist/config/engines-registry.js +103 -0
  108. package/dist/config/engines-registry.js.map +1 -0
  109. package/dist/config/os-dependencies.js +767 -0
  110. package/dist/config/os-dependencies.js.map +1 -0
  111. package/dist/config/paths.js +156 -0
  112. package/dist/config/paths.js.map +1 -0
  113. package/dist/config/version.js +3 -0
  114. package/dist/config/version.js.map +1 -0
  115. package/dist/core/backup-restore.js +219 -0
  116. package/dist/core/backup-restore.js.map +1 -0
  117. package/dist/core/base-binary-manager.js +403 -0
  118. package/dist/core/base-binary-manager.js.map +1 -0
  119. package/dist/core/base-document-binary-manager.js +364 -0
  120. package/dist/core/base-document-binary-manager.js.map +1 -0
  121. package/dist/core/base-embedded-binary-manager.js +364 -0
  122. package/dist/core/base-embedded-binary-manager.js.map +1 -0
  123. package/dist/core/base-server-binary-manager.js +368 -0
  124. package/dist/core/base-server-binary-manager.js.map +1 -0
  125. package/dist/core/config-manager.js +495 -0
  126. package/dist/core/config-manager.js.map +1 -0
  127. package/dist/core/container-manager.js +609 -0
  128. package/dist/core/container-manager.js.map +1 -0
  129. package/dist/core/credential-generator.js +67 -0
  130. package/dist/core/credential-generator.js.map +1 -0
  131. package/dist/core/credential-manager.js +211 -0
  132. package/dist/core/credential-manager.js.map +1 -0
  133. package/dist/core/dblab-utils.js +105 -0
  134. package/dist/core/dblab-utils.js.map +1 -0
  135. package/dist/core/dependency-manager.js +359 -0
  136. package/dist/core/dependency-manager.js.map +1 -0
  137. package/dist/core/docker-exporter.js +1077 -0
  138. package/dist/core/docker-exporter.js.map +1 -0
  139. package/dist/core/error-handler.js +295 -0
  140. package/dist/core/error-handler.js.map +1 -0
  141. package/dist/core/fs-error-utils.js +74 -0
  142. package/dist/core/fs-error-utils.js.map +1 -0
  143. package/dist/core/homebrew-version-manager.js +280 -0
  144. package/dist/core/homebrew-version-manager.js.map +1 -0
  145. package/dist/core/hostdb-client.js +252 -0
  146. package/dist/core/hostdb-client.js.map +1 -0
  147. package/dist/core/hostdb-metadata.js +243 -0
  148. package/dist/core/hostdb-metadata.js.map +1 -0
  149. package/dist/core/hostdb-releases-factory.js +161 -0
  150. package/dist/core/hostdb-releases-factory.js.map +1 -0
  151. package/dist/core/library-env.js +88 -0
  152. package/dist/core/library-env.js.map +1 -0
  153. package/dist/core/pgweb-utils.js +53 -0
  154. package/dist/core/pgweb-utils.js.map +1 -0
  155. package/dist/core/platform-service.js +632 -0
  156. package/dist/core/platform-service.js.map +1 -0
  157. package/dist/core/port-manager.js +136 -0
  158. package/dist/core/port-manager.js.map +1 -0
  159. package/dist/core/process-manager.js +445 -0
  160. package/dist/core/process-manager.js.map +1 -0
  161. package/dist/core/pull-manager.js +418 -0
  162. package/dist/core/pull-manager.js.map +1 -0
  163. package/dist/core/query-parser.js +449 -0
  164. package/dist/core/query-parser.js.map +1 -0
  165. package/dist/core/spawn-utils.js +90 -0
  166. package/dist/core/spawn-utils.js.map +1 -0
  167. package/dist/core/start-with-retry.js +90 -0
  168. package/dist/core/start-with-retry.js.map +1 -0
  169. package/dist/core/test-cleanup.js +85 -0
  170. package/dist/core/test-cleanup.js.map +1 -0
  171. package/dist/core/tls-generator.js +84 -0
  172. package/dist/core/tls-generator.js.map +1 -0
  173. package/dist/core/transaction-manager.js +139 -0
  174. package/dist/core/transaction-manager.js.map +1 -0
  175. package/dist/core/update-manager.js +241 -0
  176. package/dist/core/update-manager.js.map +1 -0
  177. package/dist/core/version-migration.js +260 -0
  178. package/dist/core/version-migration.js.map +1 -0
  179. package/dist/core/version-utils.js +91 -0
  180. package/dist/core/version-utils.js.map +1 -0
  181. package/dist/engines/base-engine.js +179 -0
  182. package/dist/engines/base-engine.js.map +1 -0
  183. package/dist/engines/clickhouse/backup.js +289 -0
  184. package/dist/engines/clickhouse/backup.js.map +1 -0
  185. package/dist/engines/clickhouse/binary-manager.js +145 -0
  186. package/dist/engines/clickhouse/binary-manager.js.map +1 -0
  187. package/dist/engines/clickhouse/binary-urls.js +100 -0
  188. package/dist/engines/clickhouse/binary-urls.js.map +1 -0
  189. package/dist/engines/clickhouse/cli-utils.js +143 -0
  190. package/dist/engines/clickhouse/cli-utils.js.map +1 -0
  191. package/dist/engines/clickhouse/hostdb-releases.js +24 -0
  192. package/dist/engines/clickhouse/hostdb-releases.js.map +1 -0
  193. package/dist/engines/clickhouse/index.js +1077 -0
  194. package/dist/engines/clickhouse/index.js.map +1 -0
  195. package/dist/engines/clickhouse/restore.js +335 -0
  196. package/dist/engines/clickhouse/restore.js.map +1 -0
  197. package/dist/engines/clickhouse/version-maps.js +83 -0
  198. package/dist/engines/clickhouse/version-maps.js.map +1 -0
  199. package/dist/engines/clickhouse/version-validator.js +133 -0
  200. package/dist/engines/clickhouse/version-validator.js.map +1 -0
  201. package/dist/engines/cockroachdb/backup.js +261 -0
  202. package/dist/engines/cockroachdb/backup.js.map +1 -0
  203. package/dist/engines/cockroachdb/binary-manager.js +33 -0
  204. package/dist/engines/cockroachdb/binary-manager.js.map +1 -0
  205. package/dist/engines/cockroachdb/binary-urls.js +33 -0
  206. package/dist/engines/cockroachdb/binary-urls.js.map +1 -0
  207. package/dist/engines/cockroachdb/cli-utils.js +338 -0
  208. package/dist/engines/cockroachdb/cli-utils.js.map +1 -0
  209. package/dist/engines/cockroachdb/hostdb-releases.js +21 -0
  210. package/dist/engines/cockroachdb/hostdb-releases.js.map +1 -0
  211. package/dist/engines/cockroachdb/index.js +1016 -0
  212. package/dist/engines/cockroachdb/index.js.map +1 -0
  213. package/dist/engines/cockroachdb/restore.js +323 -0
  214. package/dist/engines/cockroachdb/restore.js.map +1 -0
  215. package/dist/engines/cockroachdb/version-maps.js +37 -0
  216. package/dist/engines/cockroachdb/version-maps.js.map +1 -0
  217. package/dist/engines/couchdb/api-client.js +64 -0
  218. package/dist/engines/couchdb/api-client.js.map +1 -0
  219. package/dist/engines/couchdb/backup.js +90 -0
  220. package/dist/engines/couchdb/backup.js.map +1 -0
  221. package/dist/engines/couchdb/binary-manager.js +62 -0
  222. package/dist/engines/couchdb/binary-manager.js.map +1 -0
  223. package/dist/engines/couchdb/binary-urls.js +92 -0
  224. package/dist/engines/couchdb/binary-urls.js.map +1 -0
  225. package/dist/engines/couchdb/hostdb-releases.js +21 -0
  226. package/dist/engines/couchdb/hostdb-releases.js.map +1 -0
  227. package/dist/engines/couchdb/index.js +1043 -0
  228. package/dist/engines/couchdb/index.js.map +1 -0
  229. package/dist/engines/couchdb/restore.js +198 -0
  230. package/dist/engines/couchdb/restore.js.map +1 -0
  231. package/dist/engines/couchdb/version-maps.js +67 -0
  232. package/dist/engines/couchdb/version-maps.js.map +1 -0
  233. package/dist/engines/couchdb/version-validator.js +88 -0
  234. package/dist/engines/couchdb/version-validator.js.map +1 -0
  235. package/dist/engines/duckdb/binary-manager.js +33 -0
  236. package/dist/engines/duckdb/binary-manager.js.map +1 -0
  237. package/{engines/duckdb/binary-urls.ts → dist/engines/duckdb/binary-urls.js} +11 -16
  238. package/dist/engines/duckdb/binary-urls.js.map +1 -0
  239. package/dist/engines/duckdb/hostdb-releases.js +21 -0
  240. package/dist/engines/duckdb/hostdb-releases.js.map +1 -0
  241. package/dist/engines/duckdb/index.js +594 -0
  242. package/dist/engines/duckdb/index.js.map +1 -0
  243. package/dist/engines/duckdb/registry.js +265 -0
  244. package/dist/engines/duckdb/registry.js.map +1 -0
  245. package/dist/engines/duckdb/scanner.js +12 -0
  246. package/dist/engines/duckdb/scanner.js.map +1 -0
  247. package/dist/engines/duckdb/version-maps.js +67 -0
  248. package/dist/engines/duckdb/version-maps.js.map +1 -0
  249. package/dist/engines/duckdb/version-validator.js +62 -0
  250. package/dist/engines/duckdb/version-validator.js.map +1 -0
  251. package/dist/engines/ferretdb/backup.js +170 -0
  252. package/dist/engines/ferretdb/backup.js.map +1 -0
  253. package/dist/engines/ferretdb/binary-manager.js +765 -0
  254. package/dist/engines/ferretdb/binary-manager.js.map +1 -0
  255. package/dist/engines/ferretdb/binary-urls.js +135 -0
  256. package/dist/engines/ferretdb/binary-urls.js.map +1 -0
  257. package/dist/engines/ferretdb/index.js +1517 -0
  258. package/dist/engines/ferretdb/index.js.map +1 -0
  259. package/dist/engines/ferretdb/restore.js +310 -0
  260. package/dist/engines/ferretdb/restore.js.map +1 -0
  261. package/{engines/ferretdb/version-maps.ts → dist/engines/ferretdb/version-maps.js} +62 -79
  262. package/dist/engines/ferretdb/version-maps.js.map +1 -0
  263. package/dist/engines/file-based-utils.js +184 -0
  264. package/dist/engines/file-based-utils.js.map +1 -0
  265. package/dist/engines/index.js +124 -0
  266. package/dist/engines/index.js.map +1 -0
  267. package/dist/engines/influxdb/api-client.js +54 -0
  268. package/dist/engines/influxdb/api-client.js.map +1 -0
  269. package/dist/engines/influxdb/backup.js +119 -0
  270. package/dist/engines/influxdb/backup.js.map +1 -0
  271. package/dist/engines/influxdb/binary-manager.js +87 -0
  272. package/dist/engines/influxdb/binary-manager.js.map +1 -0
  273. package/dist/engines/influxdb/binary-urls.js +56 -0
  274. package/dist/engines/influxdb/binary-urls.js.map +1 -0
  275. package/dist/engines/influxdb/hostdb-releases.js +21 -0
  276. package/dist/engines/influxdb/hostdb-releases.js.map +1 -0
  277. package/dist/engines/influxdb/index.js +962 -0
  278. package/dist/engines/influxdb/index.js.map +1 -0
  279. package/dist/engines/influxdb/restore.js +329 -0
  280. package/dist/engines/influxdb/restore.js.map +1 -0
  281. package/dist/engines/influxdb/version-maps.js +64 -0
  282. package/dist/engines/influxdb/version-maps.js.map +1 -0
  283. package/dist/engines/influxdb/version-validator.js +109 -0
  284. package/dist/engines/influxdb/version-validator.js.map +1 -0
  285. package/dist/engines/mariadb/backup.js +178 -0
  286. package/dist/engines/mariadb/backup.js.map +1 -0
  287. package/dist/engines/mariadb/binary-manager.js +33 -0
  288. package/dist/engines/mariadb/binary-manager.js.map +1 -0
  289. package/{engines/mariadb/binary-urls.ts → dist/engines/mariadb/binary-urls.js} +38 -55
  290. package/dist/engines/mariadb/binary-urls.js.map +1 -0
  291. package/dist/engines/mariadb/hostdb-releases.js +21 -0
  292. package/dist/engines/mariadb/hostdb-releases.js.map +1 -0
  293. package/dist/engines/mariadb/index.js +1011 -0
  294. package/dist/engines/mariadb/index.js.map +1 -0
  295. package/dist/engines/mariadb/restore.js +322 -0
  296. package/dist/engines/mariadb/restore.js.map +1 -0
  297. package/dist/engines/mariadb/version-maps.js +63 -0
  298. package/dist/engines/mariadb/version-maps.js.map +1 -0
  299. package/dist/engines/mariadb/version-validator.js +143 -0
  300. package/dist/engines/mariadb/version-validator.js.map +1 -0
  301. package/dist/engines/meilisearch/api-client.js +50 -0
  302. package/dist/engines/meilisearch/api-client.js.map +1 -0
  303. package/dist/engines/meilisearch/backup.js +167 -0
  304. package/dist/engines/meilisearch/backup.js.map +1 -0
  305. package/dist/engines/meilisearch/binary-manager.js +31 -0
  306. package/dist/engines/meilisearch/binary-manager.js.map +1 -0
  307. package/dist/engines/meilisearch/binary-urls.js +56 -0
  308. package/dist/engines/meilisearch/binary-urls.js.map +1 -0
  309. package/dist/engines/meilisearch/hostdb-releases.js +21 -0
  310. package/dist/engines/meilisearch/hostdb-releases.js.map +1 -0
  311. package/dist/engines/meilisearch/index.js +992 -0
  312. package/dist/engines/meilisearch/index.js.map +1 -0
  313. package/dist/engines/meilisearch/restore.js +167 -0
  314. package/dist/engines/meilisearch/restore.js.map +1 -0
  315. package/dist/engines/meilisearch/version-maps.js +67 -0
  316. package/dist/engines/meilisearch/version-maps.js.map +1 -0
  317. package/dist/engines/meilisearch/version-validator.js +109 -0
  318. package/dist/engines/meilisearch/version-validator.js.map +1 -0
  319. package/dist/engines/mongodb/backup.js +109 -0
  320. package/dist/engines/mongodb/backup.js.map +1 -0
  321. package/dist/engines/mongodb/binary-manager.js +36 -0
  322. package/dist/engines/mongodb/binary-manager.js.map +1 -0
  323. package/dist/engines/mongodb/binary-urls.js +46 -0
  324. package/dist/engines/mongodb/binary-urls.js.map +1 -0
  325. package/dist/engines/mongodb/cli-utils.js +131 -0
  326. package/dist/engines/mongodb/cli-utils.js.map +1 -0
  327. package/dist/engines/mongodb/hostdb-releases.js +77 -0
  328. package/dist/engines/mongodb/hostdb-releases.js.map +1 -0
  329. package/dist/engines/mongodb/index.js +873 -0
  330. package/dist/engines/mongodb/index.js.map +1 -0
  331. package/dist/engines/mongodb/restore.js +276 -0
  332. package/dist/engines/mongodb/restore.js.map +1 -0
  333. package/dist/engines/mongodb/version-maps.js +79 -0
  334. package/dist/engines/mongodb/version-maps.js.map +1 -0
  335. package/dist/engines/mongodb/version-validator.js +133 -0
  336. package/dist/engines/mongodb/version-validator.js.map +1 -0
  337. package/dist/engines/mysql/backup.js +210 -0
  338. package/dist/engines/mysql/backup.js.map +1 -0
  339. package/dist/engines/mysql/binary-detection.js +325 -0
  340. package/dist/engines/mysql/binary-detection.js.map +1 -0
  341. package/dist/engines/mysql/binary-manager.js +30 -0
  342. package/dist/engines/mysql/binary-manager.js.map +1 -0
  343. package/dist/engines/mysql/binary-urls.js +87 -0
  344. package/dist/engines/mysql/binary-urls.js.map +1 -0
  345. package/{engines/mysql/hostdb-releases.ts → dist/engines/mysql/hostdb-releases.js} +20 -23
  346. package/dist/engines/mysql/hostdb-releases.js.map +1 -0
  347. package/dist/engines/mysql/index.js +1066 -0
  348. package/dist/engines/mysql/index.js.map +1 -0
  349. package/dist/engines/mysql/restore.js +361 -0
  350. package/dist/engines/mysql/restore.js.map +1 -0
  351. package/dist/engines/mysql/version-maps.js +79 -0
  352. package/dist/engines/mysql/version-maps.js.map +1 -0
  353. package/dist/engines/mysql/version-validator.js +266 -0
  354. package/dist/engines/mysql/version-validator.js.map +1 -0
  355. package/dist/engines/postgresql/backup.js +118 -0
  356. package/dist/engines/postgresql/backup.js.map +1 -0
  357. package/dist/engines/postgresql/binary-manager.js +85 -0
  358. package/dist/engines/postgresql/binary-manager.js.map +1 -0
  359. package/dist/engines/postgresql/binary-urls.js +80 -0
  360. package/dist/engines/postgresql/binary-urls.js.map +1 -0
  361. package/dist/engines/postgresql/hostdb-releases.js +21 -0
  362. package/dist/engines/postgresql/hostdb-releases.js.map +1 -0
  363. package/dist/engines/postgresql/index.js +852 -0
  364. package/dist/engines/postgresql/index.js.map +1 -0
  365. package/dist/engines/postgresql/remote-version.js +109 -0
  366. package/dist/engines/postgresql/remote-version.js.map +1 -0
  367. package/dist/engines/postgresql/restore.js +254 -0
  368. package/dist/engines/postgresql/restore.js.map +1 -0
  369. package/dist/engines/postgresql/version-maps.js +73 -0
  370. package/dist/engines/postgresql/version-maps.js.map +1 -0
  371. package/dist/engines/postgresql/version-validator.js +286 -0
  372. package/dist/engines/postgresql/version-validator.js.map +1 -0
  373. package/dist/engines/qdrant/api-client.js +50 -0
  374. package/dist/engines/qdrant/api-client.js.map +1 -0
  375. package/dist/engines/qdrant/backup.js +115 -0
  376. package/dist/engines/qdrant/backup.js.map +1 -0
  377. package/dist/engines/qdrant/binary-manager.js +31 -0
  378. package/dist/engines/qdrant/binary-manager.js.map +1 -0
  379. package/dist/engines/qdrant/binary-urls.js +92 -0
  380. package/dist/engines/qdrant/binary-urls.js.map +1 -0
  381. package/dist/engines/qdrant/cli-utils.js +39 -0
  382. package/dist/engines/qdrant/cli-utils.js.map +1 -0
  383. package/dist/engines/qdrant/hostdb-releases.js +21 -0
  384. package/dist/engines/qdrant/hostdb-releases.js.map +1 -0
  385. package/dist/engines/qdrant/index.js +1002 -0
  386. package/dist/engines/qdrant/index.js.map +1 -0
  387. package/dist/engines/qdrant/restore.js +154 -0
  388. package/dist/engines/qdrant/restore.js.map +1 -0
  389. package/dist/engines/qdrant/version-maps.js +67 -0
  390. package/dist/engines/qdrant/version-maps.js.map +1 -0
  391. package/dist/engines/qdrant/version-validator.js +109 -0
  392. package/dist/engines/qdrant/version-validator.js.map +1 -0
  393. package/dist/engines/questdb/backup.js +191 -0
  394. package/dist/engines/questdb/backup.js.map +1 -0
  395. package/dist/engines/questdb/binary-manager.js +247 -0
  396. package/dist/engines/questdb/binary-manager.js.map +1 -0
  397. package/dist/engines/questdb/binary-urls.js +27 -0
  398. package/dist/engines/questdb/binary-urls.js.map +1 -0
  399. package/dist/engines/questdb/hostdb-releases.js +21 -0
  400. package/dist/engines/questdb/hostdb-releases.js.map +1 -0
  401. package/dist/engines/questdb/index.js +814 -0
  402. package/dist/engines/questdb/index.js.map +1 -0
  403. package/dist/engines/questdb/restore.js +202 -0
  404. package/dist/engines/questdb/restore.js.map +1 -0
  405. package/dist/engines/questdb/version-maps.js +33 -0
  406. package/dist/engines/questdb/version-maps.js.map +1 -0
  407. package/dist/engines/questdb/version-validator.js +99 -0
  408. package/dist/engines/questdb/version-validator.js.map +1 -0
  409. package/dist/engines/redis/backup.js +292 -0
  410. package/dist/engines/redis/backup.js.map +1 -0
  411. package/dist/engines/redis/binary-manager.js +32 -0
  412. package/dist/engines/redis/binary-manager.js.map +1 -0
  413. package/dist/engines/redis/binary-urls.js +96 -0
  414. package/dist/engines/redis/binary-urls.js.map +1 -0
  415. package/dist/engines/redis/cli-utils.js +38 -0
  416. package/dist/engines/redis/cli-utils.js.map +1 -0
  417. package/dist/engines/redis/hostdb-releases.js +21 -0
  418. package/dist/engines/redis/hostdb-releases.js.map +1 -0
  419. package/dist/engines/redis/index.js +1263 -0
  420. package/dist/engines/redis/index.js.map +1 -0
  421. package/dist/engines/redis/restore.js +338 -0
  422. package/dist/engines/redis/restore.js.map +1 -0
  423. package/dist/engines/redis/version-maps.js +70 -0
  424. package/dist/engines/redis/version-maps.js.map +1 -0
  425. package/dist/engines/redis/version-validator.js +109 -0
  426. package/dist/engines/redis/version-validator.js.map +1 -0
  427. package/dist/engines/sqlite/binary-manager.js +39 -0
  428. package/dist/engines/sqlite/binary-manager.js.map +1 -0
  429. package/{engines/sqlite/binary-urls.ts → dist/engines/sqlite/binary-urls.js} +11 -16
  430. package/dist/engines/sqlite/binary-urls.js.map +1 -0
  431. package/dist/engines/sqlite/hostdb-releases.js +21 -0
  432. package/dist/engines/sqlite/hostdb-releases.js.map +1 -0
  433. package/dist/engines/sqlite/index.js +493 -0
  434. package/dist/engines/sqlite/index.js.map +1 -0
  435. package/dist/engines/sqlite/registry.js +163 -0
  436. package/dist/engines/sqlite/registry.js.map +1 -0
  437. package/dist/engines/sqlite/scanner.js +12 -0
  438. package/dist/engines/sqlite/scanner.js.map +1 -0
  439. package/dist/engines/sqlite/version-maps.js +57 -0
  440. package/dist/engines/sqlite/version-maps.js.map +1 -0
  441. package/dist/engines/surrealdb/backup.js +97 -0
  442. package/dist/engines/surrealdb/backup.js.map +1 -0
  443. package/dist/engines/surrealdb/binary-manager.js +33 -0
  444. package/dist/engines/surrealdb/binary-manager.js.map +1 -0
  445. package/dist/engines/surrealdb/binary-urls.js +33 -0
  446. package/dist/engines/surrealdb/binary-urls.js.map +1 -0
  447. package/dist/engines/surrealdb/cli-utils.js +147 -0
  448. package/dist/engines/surrealdb/cli-utils.js.map +1 -0
  449. package/dist/engines/surrealdb/hostdb-releases.js +21 -0
  450. package/dist/engines/surrealdb/hostdb-releases.js.map +1 -0
  451. package/dist/engines/surrealdb/index.js +1022 -0
  452. package/dist/engines/surrealdb/index.js.map +1 -0
  453. package/dist/engines/surrealdb/restore.js +224 -0
  454. package/dist/engines/surrealdb/restore.js.map +1 -0
  455. package/dist/engines/surrealdb/version-maps.js +36 -0
  456. package/dist/engines/surrealdb/version-maps.js.map +1 -0
  457. package/dist/engines/tigerbeetle/backup.js +36 -0
  458. package/dist/engines/tigerbeetle/backup.js.map +1 -0
  459. package/dist/engines/tigerbeetle/binary-manager.js +72 -0
  460. package/dist/engines/tigerbeetle/binary-manager.js.map +1 -0
  461. package/dist/engines/tigerbeetle/binary-urls.js +49 -0
  462. package/dist/engines/tigerbeetle/binary-urls.js.map +1 -0
  463. package/dist/engines/tigerbeetle/hostdb-releases.js +21 -0
  464. package/dist/engines/tigerbeetle/hostdb-releases.js.map +1 -0
  465. package/dist/engines/tigerbeetle/index.js +559 -0
  466. package/dist/engines/tigerbeetle/index.js.map +1 -0
  467. package/dist/engines/tigerbeetle/restore.js +91 -0
  468. package/dist/engines/tigerbeetle/restore.js.map +1 -0
  469. package/{engines/tigerbeetle/version-maps.ts → dist/engines/tigerbeetle/version-maps.js} +22 -31
  470. package/dist/engines/tigerbeetle/version-maps.js.map +1 -0
  471. package/dist/engines/tigerbeetle/version-validator.js +108 -0
  472. package/dist/engines/tigerbeetle/version-validator.js.map +1 -0
  473. package/dist/engines/typedb/backup.js +129 -0
  474. package/dist/engines/typedb/backup.js.map +1 -0
  475. package/dist/engines/typedb/binary-manager.js +151 -0
  476. package/dist/engines/typedb/binary-manager.js.map +1 -0
  477. package/dist/engines/typedb/binary-urls.js +33 -0
  478. package/dist/engines/typedb/binary-urls.js.map +1 -0
  479. package/dist/engines/typedb/cli-utils.js +163 -0
  480. package/dist/engines/typedb/cli-utils.js.map +1 -0
  481. package/dist/engines/typedb/hostdb-releases.js +21 -0
  482. package/dist/engines/typedb/hostdb-releases.js.map +1 -0
  483. package/dist/engines/typedb/index.js +1003 -0
  484. package/dist/engines/typedb/index.js.map +1 -0
  485. package/dist/engines/typedb/restore.js +279 -0
  486. package/dist/engines/typedb/restore.js.map +1 -0
  487. package/dist/engines/typedb/version-maps.js +40 -0
  488. package/dist/engines/typedb/version-maps.js.map +1 -0
  489. package/dist/engines/typedb/version-validator.js +103 -0
  490. package/dist/engines/typedb/version-validator.js.map +1 -0
  491. package/dist/engines/valkey/backup.js +292 -0
  492. package/dist/engines/valkey/backup.js.map +1 -0
  493. package/dist/engines/valkey/binary-manager.js +33 -0
  494. package/dist/engines/valkey/binary-manager.js.map +1 -0
  495. package/dist/engines/valkey/binary-urls.js +98 -0
  496. package/dist/engines/valkey/binary-urls.js.map +1 -0
  497. package/dist/engines/valkey/cli-utils.js +38 -0
  498. package/dist/engines/valkey/cli-utils.js.map +1 -0
  499. package/dist/engines/valkey/hostdb-releases.js +21 -0
  500. package/dist/engines/valkey/hostdb-releases.js.map +1 -0
  501. package/dist/engines/valkey/index.js +1257 -0
  502. package/dist/engines/valkey/index.js.map +1 -0
  503. package/dist/engines/valkey/restore.js +340 -0
  504. package/dist/engines/valkey/restore.js.map +1 -0
  505. package/dist/engines/valkey/version-maps.js +70 -0
  506. package/dist/engines/valkey/version-maps.js.map +1 -0
  507. package/dist/engines/valkey/version-validator.js +112 -0
  508. package/dist/engines/valkey/version-validator.js.map +1 -0
  509. package/dist/engines/weaviate/api-client.js +50 -0
  510. package/dist/engines/weaviate/api-client.js.map +1 -0
  511. package/dist/engines/weaviate/backup.js +95 -0
  512. package/dist/engines/weaviate/backup.js.map +1 -0
  513. package/dist/engines/weaviate/binary-manager.js +58 -0
  514. package/dist/engines/weaviate/binary-manager.js.map +1 -0
  515. package/dist/engines/weaviate/binary-urls.js +92 -0
  516. package/dist/engines/weaviate/binary-urls.js.map +1 -0
  517. package/dist/engines/weaviate/cli-utils.js +39 -0
  518. package/dist/engines/weaviate/cli-utils.js.map +1 -0
  519. package/dist/engines/weaviate/hostdb-releases.js +21 -0
  520. package/dist/engines/weaviate/hostdb-releases.js.map +1 -0
  521. package/dist/engines/weaviate/index.js +871 -0
  522. package/dist/engines/weaviate/index.js.map +1 -0
  523. package/dist/engines/weaviate/restore.js +185 -0
  524. package/dist/engines/weaviate/restore.js.map +1 -0
  525. package/dist/engines/weaviate/version-maps.js +67 -0
  526. package/dist/engines/weaviate/version-maps.js.map +1 -0
  527. package/dist/engines/weaviate/version-validator.js +109 -0
  528. package/dist/engines/weaviate/version-validator.js.map +1 -0
  529. package/dist/types/index.js +102 -0
  530. package/dist/types/index.js.map +1 -0
  531. package/package.json +12 -9
  532. package/bin/cli.js +0 -68
  533. package/cli/bin.ts +0 -10
  534. package/cli/commands/attach.ts +0 -139
  535. package/cli/commands/backup.ts +0 -290
  536. package/cli/commands/backups.ts +0 -247
  537. package/cli/commands/clone.ts +0 -159
  538. package/cli/commands/config.ts +0 -367
  539. package/cli/commands/connect.ts +0 -684
  540. package/cli/commands/create.ts +0 -1201
  541. package/cli/commands/databases.ts +0 -630
  542. package/cli/commands/delete.ts +0 -133
  543. package/cli/commands/deps.ts +0 -342
  544. package/cli/commands/detach.ts +0 -107
  545. package/cli/commands/doctor.ts +0 -689
  546. package/cli/commands/duckdb.ts +0 -273
  547. package/cli/commands/edit.ts +0 -683
  548. package/cli/commands/engines.ts +0 -1914
  549. package/cli/commands/export.ts +0 -544
  550. package/cli/commands/info.ts +0 -340
  551. package/cli/commands/list.ts +0 -284
  552. package/cli/commands/logs.ts +0 -102
  553. package/cli/commands/menu/backup-handlers.ts +0 -1571
  554. package/cli/commands/menu/container-handlers.ts +0 -2288
  555. package/cli/commands/menu/engine-handlers.ts +0 -355
  556. package/cli/commands/menu/index.ts +0 -342
  557. package/cli/commands/menu/settings-handlers.ts +0 -365
  558. package/cli/commands/menu/shared.ts +0 -23
  559. package/cli/commands/menu/shell-handlers.ts +0 -1811
  560. package/cli/commands/menu/sql-handlers.ts +0 -231
  561. package/cli/commands/menu/update-handlers.ts +0 -378
  562. package/cli/commands/menu/validators.ts +0 -8
  563. package/cli/commands/ports.ts +0 -211
  564. package/cli/commands/pull.ts +0 -223
  565. package/cli/commands/query.ts +0 -241
  566. package/cli/commands/restore.ts +0 -587
  567. package/cli/commands/run.ts +0 -178
  568. package/cli/commands/self-update.ts +0 -121
  569. package/cli/commands/sqlite.ts +0 -273
  570. package/cli/commands/start.ts +0 -218
  571. package/cli/commands/stop.ts +0 -241
  572. package/cli/commands/url.ts +0 -104
  573. package/cli/commands/users.ts +0 -264
  574. package/cli/commands/version.ts +0 -55
  575. package/cli/commands/which.ts +0 -290
  576. package/cli/constants.ts +0 -233
  577. package/cli/helpers.ts +0 -1593
  578. package/cli/index.ts +0 -162
  579. package/cli/ui/prompts.ts +0 -1525
  580. package/cli/ui/spinner.ts +0 -88
  581. package/cli/ui/theme.ts +0 -128
  582. package/cli/utils/file-follower.ts +0 -93
  583. package/config/backup-formats.ts +0 -446
  584. package/config/defaults.ts +0 -56
  585. package/config/engine-defaults.ts +0 -336
  586. package/config/engines-registry.ts +0 -150
  587. package/config/engines.schema.json +0 -135
  588. package/config/os-dependencies.ts +0 -888
  589. package/config/paths.ts +0 -200
  590. package/core/backup-restore.ts +0 -330
  591. package/core/base-binary-manager.ts +0 -562
  592. package/core/base-document-binary-manager.ts +0 -523
  593. package/core/base-embedded-binary-manager.ts +0 -547
  594. package/core/base-server-binary-manager.ts +0 -523
  595. package/core/config-manager.ts +0 -652
  596. package/core/container-manager.ts +0 -787
  597. package/core/credential-generator.ts +0 -93
  598. package/core/credential-manager.ts +0 -259
  599. package/core/dblab-utils.ts +0 -113
  600. package/core/dependency-manager.ts +0 -512
  601. package/core/docker-exporter.ts +0 -1345
  602. package/core/error-handler.ts +0 -419
  603. package/core/fs-error-utils.ts +0 -82
  604. package/core/homebrew-version-manager.ts +0 -352
  605. package/core/hostdb-client.ts +0 -344
  606. package/core/hostdb-metadata.ts +0 -350
  607. package/core/hostdb-releases-factory.ts +0 -237
  608. package/core/library-env.ts +0 -118
  609. package/core/pgweb-utils.ts +0 -62
  610. package/core/platform-service.ts +0 -829
  611. package/core/port-manager.ts +0 -165
  612. package/core/process-manager.ts +0 -576
  613. package/core/pull-manager.ts +0 -511
  614. package/core/query-parser.ts +0 -514
  615. package/core/spawn-utils.ts +0 -122
  616. package/core/start-with-retry.ts +0 -130
  617. package/core/test-cleanup.ts +0 -108
  618. package/core/tls-generator.ts +0 -116
  619. package/core/transaction-manager.ts +0 -158
  620. package/core/update-manager.ts +0 -308
  621. package/core/version-migration.ts +0 -346
  622. package/core/version-utils.ts +0 -104
  623. package/engines/base-engine.ts +0 -340
  624. package/engines/clickhouse/README.md +0 -231
  625. package/engines/clickhouse/backup.ts +0 -398
  626. package/engines/clickhouse/binary-manager.ts +0 -201
  627. package/engines/clickhouse/binary-urls.ts +0 -125
  628. package/engines/clickhouse/cli-utils.ts +0 -176
  629. package/engines/clickhouse/hostdb-releases.ts +0 -30
  630. package/engines/clickhouse/index.ts +0 -1345
  631. package/engines/clickhouse/restore.ts +0 -466
  632. package/engines/clickhouse/version-maps.ts +0 -95
  633. package/engines/clickhouse/version-validator.ts +0 -154
  634. package/engines/cockroachdb/README.md +0 -170
  635. package/engines/cockroachdb/backup.ts +0 -376
  636. package/engines/cockroachdb/binary-manager.ts +0 -45
  637. package/engines/cockroachdb/binary-urls.ts +0 -40
  638. package/engines/cockroachdb/cli-utils.ts +0 -384
  639. package/engines/cockroachdb/hostdb-releases.ts +0 -26
  640. package/engines/cockroachdb/index.ts +0 -1276
  641. package/engines/cockroachdb/restore.ts +0 -455
  642. package/engines/cockroachdb/version-maps.ts +0 -42
  643. package/engines/couchdb/README.md +0 -257
  644. package/engines/couchdb/api-client.ts +0 -81
  645. package/engines/couchdb/backup.ts +0 -137
  646. package/engines/couchdb/binary-manager.ts +0 -86
  647. package/engines/couchdb/binary-urls.ts +0 -115
  648. package/engines/couchdb/hostdb-releases.ts +0 -23
  649. package/engines/couchdb/index.ts +0 -1429
  650. package/engines/couchdb/restore.ts +0 -290
  651. package/engines/couchdb/version-maps.ts +0 -78
  652. package/engines/couchdb/version-validator.ts +0 -111
  653. package/engines/duckdb/README.md +0 -154
  654. package/engines/duckdb/binary-manager.ts +0 -45
  655. package/engines/duckdb/hostdb-releases.ts +0 -23
  656. package/engines/duckdb/index.ts +0 -749
  657. package/engines/duckdb/registry.ts +0 -303
  658. package/engines/duckdb/scanner.ts +0 -22
  659. package/engines/duckdb/version-maps.ts +0 -78
  660. package/engines/duckdb/version-validator.ts +0 -78
  661. package/engines/ferretdb/README.md +0 -262
  662. package/engines/ferretdb/backup.ts +0 -173
  663. package/engines/ferretdb/binary-manager.ts +0 -1095
  664. package/engines/ferretdb/binary-urls.ts +0 -183
  665. package/engines/ferretdb/index.ts +0 -1907
  666. package/engines/ferretdb/restore.ts +0 -357
  667. package/engines/file-based-utils.ts +0 -262
  668. package/engines/index.ts +0 -131
  669. package/engines/influxdb/README.md +0 -180
  670. package/engines/influxdb/api-client.ts +0 -64
  671. package/engines/influxdb/backup.ts +0 -160
  672. package/engines/influxdb/binary-manager.ts +0 -110
  673. package/engines/influxdb/binary-urls.ts +0 -69
  674. package/engines/influxdb/hostdb-releases.ts +0 -23
  675. package/engines/influxdb/index.ts +0 -1272
  676. package/engines/influxdb/restore.ts +0 -417
  677. package/engines/influxdb/version-maps.ts +0 -75
  678. package/engines/influxdb/version-validator.ts +0 -128
  679. package/engines/mariadb/README.md +0 -141
  680. package/engines/mariadb/backup.ts +0 -233
  681. package/engines/mariadb/binary-manager.ts +0 -45
  682. package/engines/mariadb/hostdb-releases.ts +0 -23
  683. package/engines/mariadb/index.ts +0 -1300
  684. package/engines/mariadb/restore.ts +0 -447
  685. package/engines/mariadb/version-maps.ts +0 -72
  686. package/engines/mariadb/version-validator.ts +0 -181
  687. package/engines/meilisearch/README.md +0 -255
  688. package/engines/meilisearch/api-client.ts +0 -61
  689. package/engines/meilisearch/backup.ts +0 -233
  690. package/engines/meilisearch/binary-manager.ts +0 -43
  691. package/engines/meilisearch/binary-urls.ts +0 -69
  692. package/engines/meilisearch/hostdb-releases.ts +0 -26
  693. package/engines/meilisearch/index.ts +0 -1292
  694. package/engines/meilisearch/restore.ts +0 -219
  695. package/engines/meilisearch/version-maps.ts +0 -78
  696. package/engines/meilisearch/version-validator.ts +0 -128
  697. package/engines/mongodb/README.md +0 -162
  698. package/engines/mongodb/backup.ts +0 -127
  699. package/engines/mongodb/binary-manager.ts +0 -48
  700. package/engines/mongodb/binary-urls.ts +0 -63
  701. package/engines/mongodb/cli-utils.ts +0 -171
  702. package/engines/mongodb/hostdb-releases.ts +0 -91
  703. package/engines/mongodb/index.ts +0 -1118
  704. package/engines/mongodb/restore.ts +0 -361
  705. package/engines/mongodb/version-maps.ts +0 -91
  706. package/engines/mongodb/version-validator.ts +0 -160
  707. package/engines/mysql/README.md +0 -142
  708. package/engines/mysql/backup.ts +0 -270
  709. package/engines/mysql/binary-detection.ts +0 -408
  710. package/engines/mysql/binary-manager.ts +0 -42
  711. package/engines/mysql/binary-urls.ts +0 -104
  712. package/engines/mysql/index.ts +0 -1361
  713. package/engines/mysql/restore.ts +0 -500
  714. package/engines/mysql/version-maps.ts +0 -91
  715. package/engines/mysql/version-validator.ts +0 -369
  716. package/engines/postgresql/README.md +0 -158
  717. package/engines/postgresql/backup.ts +0 -151
  718. package/engines/postgresql/binary-manager.ts +0 -114
  719. package/engines/postgresql/binary-urls.ts +0 -99
  720. package/engines/postgresql/hostdb-releases.ts +0 -26
  721. package/engines/postgresql/index.ts +0 -1143
  722. package/engines/postgresql/remote-version.ts +0 -161
  723. package/engines/postgresql/restore.ts +0 -342
  724. package/engines/postgresql/version-maps.ts +0 -83
  725. package/engines/postgresql/version-validator.ts +0 -413
  726. package/engines/qdrant/README.md +0 -222
  727. package/engines/qdrant/api-client.ts +0 -61
  728. package/engines/qdrant/backup.ts +0 -165
  729. package/engines/qdrant/binary-manager.ts +0 -43
  730. package/engines/qdrant/binary-urls.ts +0 -115
  731. package/engines/qdrant/cli-utils.ts +0 -43
  732. package/engines/qdrant/hostdb-releases.ts +0 -23
  733. package/engines/qdrant/index.ts +0 -1312
  734. package/engines/qdrant/restore.ts +0 -203
  735. package/engines/qdrant/version-maps.ts +0 -78
  736. package/engines/qdrant/version-validator.ts +0 -128
  737. package/engines/questdb/README.md +0 -334
  738. package/engines/questdb/backup.ts +0 -220
  739. package/engines/questdb/binary-manager.ts +0 -310
  740. package/engines/questdb/binary-urls.ts +0 -34
  741. package/engines/questdb/hostdb-releases.ts +0 -23
  742. package/engines/questdb/index.ts +0 -1023
  743. package/engines/questdb/restore.ts +0 -260
  744. package/engines/questdb/version-maps.ts +0 -37
  745. package/engines/questdb/version-validator.ts +0 -121
  746. package/engines/redis/README.md +0 -173
  747. package/engines/redis/backup.ts +0 -389
  748. package/engines/redis/binary-manager.ts +0 -44
  749. package/engines/redis/binary-urls.ts +0 -117
  750. package/engines/redis/cli-utils.ts +0 -42
  751. package/engines/redis/hostdb-releases.ts +0 -23
  752. package/engines/redis/index.ts +0 -1583
  753. package/engines/redis/restore.ts +0 -443
  754. package/engines/redis/version-maps.ts +0 -81
  755. package/engines/redis/version-validator.ts +0 -131
  756. package/engines/sqlite/README.md +0 -162
  757. package/engines/sqlite/binary-manager.ts +0 -52
  758. package/engines/sqlite/hostdb-releases.ts +0 -23
  759. package/engines/sqlite/index.ts +0 -641
  760. package/engines/sqlite/registry.ts +0 -198
  761. package/engines/sqlite/scanner.ts +0 -22
  762. package/engines/sqlite/version-maps.ts +0 -64
  763. package/engines/surrealdb/README.md +0 -218
  764. package/engines/surrealdb/backup.ts +0 -131
  765. package/engines/surrealdb/binary-manager.ts +0 -45
  766. package/engines/surrealdb/binary-urls.ts +0 -40
  767. package/engines/surrealdb/cli-utils.ts +0 -173
  768. package/engines/surrealdb/hostdb-releases.ts +0 -23
  769. package/engines/surrealdb/index.ts +0 -1246
  770. package/engines/surrealdb/restore.ts +0 -302
  771. package/engines/surrealdb/version-maps.ts +0 -41
  772. package/engines/tigerbeetle/README.md +0 -61
  773. package/engines/tigerbeetle/backup.ts +0 -49
  774. package/engines/tigerbeetle/binary-manager.ts +0 -95
  775. package/engines/tigerbeetle/binary-urls.ts +0 -62
  776. package/engines/tigerbeetle/hostdb-releases.ts +0 -26
  777. package/engines/tigerbeetle/index.ts +0 -746
  778. package/engines/tigerbeetle/restore.ts +0 -130
  779. package/engines/tigerbeetle/version-validator.ts +0 -126
  780. package/engines/typedb/backup.ts +0 -167
  781. package/engines/typedb/binary-manager.ts +0 -200
  782. package/engines/typedb/binary-urls.ts +0 -40
  783. package/engines/typedb/cli-utils.ts +0 -210
  784. package/engines/typedb/hostdb-releases.ts +0 -23
  785. package/engines/typedb/index.ts +0 -1275
  786. package/engines/typedb/restore.ts +0 -377
  787. package/engines/typedb/version-maps.ts +0 -48
  788. package/engines/typedb/version-validator.ts +0 -127
  789. package/engines/valkey/README.md +0 -219
  790. package/engines/valkey/backup.ts +0 -389
  791. package/engines/valkey/binary-manager.ts +0 -45
  792. package/engines/valkey/binary-urls.ts +0 -122
  793. package/engines/valkey/cli-utils.ts +0 -42
  794. package/engines/valkey/hostdb-releases.ts +0 -23
  795. package/engines/valkey/index.ts +0 -1585
  796. package/engines/valkey/restore.ts +0 -446
  797. package/engines/valkey/version-maps.ts +0 -81
  798. package/engines/valkey/version-validator.ts +0 -131
  799. package/engines/weaviate/README.md +0 -302
  800. package/engines/weaviate/api-client.ts +0 -61
  801. package/engines/weaviate/backup.ts +0 -145
  802. package/engines/weaviate/binary-manager.ts +0 -80
  803. package/engines/weaviate/binary-urls.ts +0 -115
  804. package/engines/weaviate/cli-utils.ts +0 -43
  805. package/engines/weaviate/hostdb-releases.ts +0 -23
  806. package/engines/weaviate/index.ts +0 -1139
  807. package/engines/weaviate/restore.ts +0 -235
  808. package/engines/weaviate/version-maps.ts +0 -78
  809. package/engines/weaviate/version-validator.ts +0 -128
  810. package/types/index.ts +0 -624
  811. /package/{config → dist/config}/engines.json +0 -0
@@ -1,1585 +0,0 @@
1
- import { spawn, exec, type SpawnOptions } from 'child_process'
2
- import { promisify } from 'util'
3
- import { existsSync } from 'fs'
4
- import { mkdir, writeFile, readFile, unlink } from 'fs/promises'
5
- import { join } from 'path'
6
- import { BaseEngine } from '../base-engine'
7
- import { paths } from '../../config/paths'
8
- import { getEngineDefaults } from '../../config/defaults'
9
- import { platformService, isWindows } from '../../core/platform-service'
10
- import { configManager } from '../../core/config-manager'
11
- import {
12
- logDebug,
13
- logWarning,
14
- assertValidUsername,
15
- } from '../../core/error-handler'
16
- import { processManager } from '../../core/process-manager'
17
- import { valkeyBinaryManager } from './binary-manager'
18
- import { getBinaryUrl } from './binary-urls'
19
- import {
20
- normalizeVersion,
21
- SUPPORTED_MAJOR_VERSIONS,
22
- VALKEY_VERSION_MAP,
23
- } from './version-maps'
24
- import { fetchAvailableVersions as fetchHostdbVersions } from './hostdb-releases'
25
- import {
26
- detectBackupFormat as detectBackupFormatImpl,
27
- restoreBackup,
28
- } from './restore'
29
- import { createBackup } from './backup'
30
- import { getValkeyCliPath, VALKEY_CLI_NOT_FOUND_ERROR } from './cli-utils'
31
- import {
32
- type Platform,
33
- type Arch,
34
- type ContainerConfig,
35
- type ProgressCallback,
36
- type BackupFormat,
37
- type BackupOptions,
38
- type BackupResult,
39
- type RestoreResult,
40
- type DumpResult,
41
- type StatusResult,
42
- type QueryResult,
43
- type QueryOptions,
44
- type CreateUserOptions,
45
- type UserCredentials,
46
- } from '../../types'
47
- import { parseRedisResult } from '../../core/query-parser'
48
- import { getLibraryEnv, detectLibraryError } from '../../core/library-env'
49
-
50
- const execAsync = promisify(exec)
51
-
52
- const ENGINE = 'valkey'
53
-
54
- /**
55
- * Escape a Valkey key for use in CLI commands.
56
- * Escapes backslashes, double quotes, and control characters to prevent
57
- * command injection and ensure keys are parsed correctly by the CLI.
58
- */
59
- function escapeKeyForCommand(key: string): string {
60
- return key
61
- .replace(/\\/g, '\\\\') // Backslashes first to prevent double-escaping
62
- .replace(/"/g, '\\"') // Double quotes
63
- .replace(/\n/g, '\\n') // Newline
64
- .replace(/\r/g, '\\r') // Carriage return
65
- .replace(/\t/g, '\\t') // Tab
66
- }
67
- const engineDef = getEngineDefaults(ENGINE)
68
-
69
- /**
70
- * Shell metacharacters that indicate potential command injection
71
- * These patterns shouldn't appear in valid Valkey commands
72
- */
73
- const SHELL_INJECTION_PATTERNS = [
74
- /;\s*\S/, // Command chaining: ; followed by another command
75
- /\$\(/, // Command substitution: $(...)
76
- /\$\{/, // Variable substitution: ${...}
77
- /`/, // Backtick command substitution
78
- /&&/, // Logical AND chaining
79
- /\|\|/, // Logical OR chaining
80
- /\|\s*\S/, // Pipe to another command
81
- ]
82
-
83
- // Validate that a command doesn't contain shell injection patterns
84
- function validateCommand(command: string): void {
85
- for (const pattern of SHELL_INJECTION_PATTERNS) {
86
- if (pattern.test(command)) {
87
- throw new Error(
88
- `Command contains shell metacharacters that are not valid in Valkey commands. ` +
89
- `If you need complex commands, use a script file instead.`,
90
- )
91
- }
92
- }
93
- }
94
-
95
- /**
96
- * Convert a Windows path to Cygwin path format.
97
- * Valkey Windows binaries are built with Cygwin runtime and expect paths
98
- * in /cygdrive/c/... format when passed as command-line arguments.
99
- *
100
- * Example: C:\Users\foo\config.conf -> /cygdrive/c/Users/foo/config.conf
101
- */
102
- function toCygwinPath(windowsPath: string): string {
103
- // Match drive letter at start (e.g., C:\ or D:/)
104
- const driveMatch = windowsPath.match(/^([A-Za-z]):[/\\]/)
105
- if (!driveMatch) {
106
- // Not a Windows absolute path, return as-is with forward slashes
107
- return windowsPath.replace(/\\/g, '/')
108
- }
109
-
110
- const driveLetter = driveMatch[1].toLowerCase()
111
- const restOfPath = windowsPath.slice(3).replace(/\\/g, '/')
112
- return `/cygdrive/${driveLetter}/${restOfPath}`
113
- }
114
-
115
- /**
116
- * Parse a Valkey connection string
117
- * Supported schemes:
118
- * - redis:// (plain, no TLS)
119
- * - rediss:// (TLS enabled)
120
- * - valkey:// (plain, no TLS)
121
- * - valkeys:// (TLS enabled)
122
- *
123
- * Format: scheme://[user:password@]host[:port][/database]
124
- *
125
- * Examples:
126
- * - redis://localhost:6379
127
- * - rediss://secure.host:6379/0 (TLS)
128
- * - valkey://localhost:6379
129
- * - valkeys://secure.host:6379 (TLS)
130
- */
131
- function parseValkeyConnectionString(connectionString: string): {
132
- host: string
133
- port: number
134
- username: string | undefined
135
- password: string | undefined
136
- database: number
137
- tls: boolean
138
- } {
139
- let url: URL
140
-
141
- const normalized = connectionString.trim()
142
-
143
- // Check for valid schemes
144
- const validSchemes = ['redis://', 'rediss://', 'valkey://', 'valkeys://']
145
- const hasValidScheme = validSchemes.some((scheme) =>
146
- normalized.startsWith(scheme),
147
- )
148
-
149
- if (!hasValidScheme) {
150
- throw new Error(
151
- `Invalid Valkey connection string: ${connectionString}\n` +
152
- 'Expected format: scheme://[user:password@]host:port[/database]\n' +
153
- 'Supported schemes: redis://, rediss://, valkey://, valkeys://\n' +
154
- '(Use rediss:// or valkeys:// for TLS connections)',
155
- )
156
- }
157
-
158
- // Normalize valkey(s):// to redis(s):// for URL parsing
159
- let urlString = normalized
160
- if (normalized.startsWith('valkeys://')) {
161
- urlString = normalized.replace('valkeys://', 'rediss://')
162
- } else if (normalized.startsWith('valkey://')) {
163
- urlString = normalized.replace('valkey://', 'redis://')
164
- }
165
-
166
- try {
167
- url = new URL(urlString)
168
- } catch {
169
- throw new Error(
170
- `Invalid Valkey connection string: ${connectionString}\n` +
171
- 'Expected format: scheme://[user:password@]host:port[/database]',
172
- )
173
- }
174
-
175
- // Determine TLS based on original scheme
176
- const tls =
177
- normalized.startsWith('rediss://') || normalized.startsWith('valkeys://')
178
-
179
- const host = url.hostname || 'localhost'
180
- const port = parseInt(url.port, 10) || 6379
181
-
182
- // Valkey supports ACL with usernames (inherited from Redis 6.0+)
183
- // Format: redis://username:password@host:port/db
184
- const username = url.username || undefined
185
- const password = url.password || undefined
186
-
187
- // Database is in the path (e.g., /5 means database 5)
188
- let database = 0
189
- if (url.pathname && url.pathname !== '/') {
190
- const dbNum = parseInt(url.pathname.replace('/', ''), 10)
191
- if (!isNaN(dbNum)) {
192
- if (dbNum < 0 || dbNum > 15) {
193
- throw new RangeError(
194
- `Invalid Valkey database number: ${dbNum} (from path "${url.pathname}").\n` +
195
- 'Valkey databases must be 0-15.',
196
- )
197
- }
198
- database = dbNum
199
- }
200
- }
201
-
202
- return { host, port, username, password, database, tls }
203
- }
204
-
205
- // Build a valkey-cli command for inline command execution
206
- export function buildValkeyCliCommand(
207
- valkeyCliPath: string,
208
- port: number,
209
- command: string,
210
- options?: { database?: string },
211
- ): string {
212
- // Validate command doesn't contain shell injection patterns
213
- validateCommand(command)
214
-
215
- const db = options?.database || '0'
216
- // Escape double quotes consistently on all platforms to prevent shell interpretation issues
217
- const escaped = command.replace(/"/g, '\\"')
218
- return `"${valkeyCliPath}" -h 127.0.0.1 -p ${port} -n ${db} ${escaped}`
219
- }
220
-
221
- // Generate Valkey configuration file content
222
- function generateValkeyConfig(options: {
223
- port: number
224
- dataDir: string
225
- logFile: string
226
- pidFile: string
227
- daemonize?: boolean
228
- }): string {
229
- // Windows Valkey doesn't support daemonize natively, use detached spawn instead
230
- const daemonizeValue = options.daemonize ?? true
231
-
232
- // Valkey config requires forward slashes even on Windows
233
- const normalizePathForValkey = (p: string) => p.replace(/\\/g, '/')
234
-
235
- return `# SpinDB generated Valkey configuration
236
- port ${options.port}
237
- bind 127.0.0.1
238
- dir ${normalizePathForValkey(options.dataDir)}
239
- daemonize ${daemonizeValue ? 'yes' : 'no'}
240
- logfile ${normalizePathForValkey(options.logFile)}
241
- pidfile ${normalizePathForValkey(options.pidFile)}
242
-
243
- # Persistence - RDB snapshots
244
- save 900 1
245
- save 300 10
246
- save 60 10000
247
- dbfilename dump.rdb
248
-
249
- # Append Only File (disabled for local dev)
250
- appendonly no
251
-
252
- # Suppress ARM64 copy-on-write warning with Transparent Huge Pages.
253
- # Redis/Valkey refuses to start on ARM64 with THP enabled unless this is set.
254
- # Safe for local development (SpinDB's use case).
255
- ignore-warnings ARM64-COW-BUG
256
- `
257
- }
258
-
259
- export class ValkeyEngine extends BaseEngine {
260
- name = ENGINE
261
- displayName = 'Valkey'
262
- defaultPort = engineDef.defaultPort
263
- supportedVersions = SUPPORTED_MAJOR_VERSIONS
264
-
265
- // Get platform info for binary operations
266
- getPlatformInfo(): { platform: Platform; arch: Arch } {
267
- return platformService.getPlatformInfo()
268
- }
269
-
270
- // Fetch available versions from hostdb (dynamically or from cache/fallback)
271
- async fetchAvailableVersions(): Promise<Record<string, string[]>> {
272
- return fetchHostdbVersions()
273
- }
274
-
275
- // Get binary download URL from hostdb
276
- getBinaryUrl(version: string, platform: Platform, arch: Arch): string {
277
- return getBinaryUrl(version, platform, arch)
278
- }
279
-
280
- // Resolves version string to full version (e.g., '8' -> '8.0.6')
281
- resolveFullVersion(version: string): string {
282
- // Check if already a full version (has at least two dots)
283
- if (/^\d+\.\d+\.\d+$/.test(version)) {
284
- return version
285
- }
286
- // It's a major version, resolve using version map
287
- return VALKEY_VERSION_MAP[version] || `${version}.0.0`
288
- }
289
-
290
- // Get the path where binaries for a version would be installed
291
- getBinaryPath(version: string): string {
292
- const fullVersion = this.resolveFullVersion(version)
293
- const { platform: p, arch: a } = this.getPlatformInfo()
294
- return paths.getBinaryPath({
295
- engine: 'valkey',
296
- version: fullVersion,
297
- platform: p,
298
- arch: a,
299
- })
300
- }
301
-
302
- // Verify that Valkey binaries are available
303
- async verifyBinary(binPath: string): Promise<boolean> {
304
- const ext = platformService.getExecutableExtension()
305
- const serverPath = join(binPath, 'bin', `valkey-server${ext}`)
306
- return existsSync(serverPath)
307
- }
308
-
309
- //Check if a specific Valkey version is installed (downloaded)
310
- async isBinaryInstalled(version: string): Promise<boolean> {
311
- const { platform, arch } = this.getPlatformInfo()
312
- return valkeyBinaryManager.isInstalled(version, platform, arch)
313
- }
314
-
315
- /**
316
- * Ensure Valkey binaries are available for a specific version
317
- * Downloads from hostdb if not already installed
318
- * Returns the path to the bin directory
319
- */
320
- async ensureBinaries(
321
- version: string,
322
- onProgress?: ProgressCallback,
323
- ): Promise<string> {
324
- const { platform, arch } = this.getPlatformInfo()
325
-
326
- const binPath = await valkeyBinaryManager.ensureInstalled(
327
- version,
328
- platform,
329
- arch,
330
- onProgress,
331
- )
332
-
333
- // Register binaries in config
334
- const ext = platformService.getExecutableExtension()
335
- const tools = ['valkey-server', 'valkey-cli'] as const
336
-
337
- for (const tool of tools) {
338
- const toolPath = join(binPath, 'bin', `${tool}${ext}`)
339
- if (existsSync(toolPath)) {
340
- await configManager.setBinaryPath(tool, toolPath, 'bundled')
341
- }
342
- }
343
-
344
- return binPath
345
- }
346
-
347
- /**
348
- * Initialize a new Valkey data directory
349
- * Creates the directory and generates valkey.conf
350
- */
351
- async initDataDir(
352
- containerName: string,
353
- _version: string,
354
- options: Record<string, unknown> = {},
355
- ): Promise<string> {
356
- const dataDir = paths.getContainerDataPath(containerName, {
357
- engine: ENGINE,
358
- })
359
- const containerDir = paths.getContainerPath(containerName, {
360
- engine: ENGINE,
361
- })
362
- const logFile = paths.getContainerLogPath(containerName, { engine: ENGINE })
363
- const pidFile = join(containerDir, 'valkey.pid')
364
- const port = (options.port as number) || engineDef.defaultPort
365
-
366
- // Create data directory if it doesn't exist
367
- if (!existsSync(dataDir)) {
368
- await mkdir(dataDir, { recursive: true })
369
- logDebug(`Created Valkey data directory: ${dataDir}`)
370
- }
371
-
372
- // Generate valkey.conf
373
- const configPath = join(containerDir, 'valkey.conf')
374
- const configContent = generateValkeyConfig({
375
- port,
376
- dataDir,
377
- logFile,
378
- pidFile,
379
- })
380
- await writeFile(configPath, configContent)
381
- logDebug(`Generated Valkey config: ${configPath}`)
382
-
383
- return dataDir
384
- }
385
-
386
- // Get the path to valkey-server for a version
387
- async getValkeyServerPath(version: string): Promise<string> {
388
- const { platform, arch } = this.getPlatformInfo()
389
- const fullVersion = normalizeVersion(version)
390
- const binPath = paths.getBinaryPath({
391
- engine: 'valkey',
392
- version: fullVersion,
393
- platform,
394
- arch,
395
- })
396
- const ext = platformService.getExecutableExtension()
397
- const serverPath = join(binPath, 'bin', `valkey-server${ext}`)
398
- if (existsSync(serverPath)) {
399
- return serverPath
400
- }
401
- throw new Error(
402
- `Valkey ${version} is not installed. Run: spindb engines download valkey ${version}`,
403
- )
404
- }
405
-
406
- // Get the path to valkey-cli for a version
407
- override async getValkeyCliPath(version?: string): Promise<string> {
408
- // Check config cache first
409
- const cached = await configManager.getBinaryPath('valkey-cli')
410
- if (cached && existsSync(cached)) {
411
- return cached
412
- }
413
-
414
- // If version provided, use downloaded binary
415
- if (version) {
416
- const { platform, arch } = this.getPlatformInfo()
417
- const fullVersion = normalizeVersion(version)
418
- const binPath = paths.getBinaryPath({
419
- engine: 'valkey',
420
- version: fullVersion,
421
- platform,
422
- arch,
423
- })
424
- const ext = platformService.getExecutableExtension()
425
- const cliPath = join(binPath, 'bin', `valkey-cli${ext}`)
426
- if (existsSync(cliPath)) {
427
- return cliPath
428
- }
429
- }
430
-
431
- throw new Error(
432
- 'valkey-cli not found. Run: spindb engines download valkey <version>',
433
- )
434
- }
435
-
436
- /**
437
- * Start Valkey server
438
- * CLI wrapper: valkey-server /path/to/valkey.conf
439
- */
440
- async start(
441
- container: ContainerConfig,
442
- onProgress?: ProgressCallback,
443
- ): Promise<{ port: number; connectionString: string }> {
444
- const { name, port, version, binaryPath } = container
445
-
446
- // Check if already running (idempotent behavior)
447
- const alreadyRunning = await processManager.isRunning(name, {
448
- engine: ENGINE,
449
- })
450
- if (alreadyRunning) {
451
- return {
452
- port,
453
- connectionString: this.getConnectionString(container),
454
- }
455
- }
456
-
457
- // Use stored binary path if available (from container creation)
458
- // This ensures version consistency - the container uses the same binary it was created with
459
- let valkeyServer: string | null = null
460
-
461
- if (binaryPath && existsSync(binaryPath)) {
462
- // binaryPath is the directory (e.g., ~/.spindb/bin/valkey-8.0.6-linux-arm64)
463
- // We need to construct the full path to valkey-server
464
- const ext = platformService.getExecutableExtension()
465
- const serverPath = join(binaryPath, 'bin', `valkey-server${ext}`)
466
- if (existsSync(serverPath)) {
467
- valkeyServer = serverPath
468
- logDebug(`Using stored binary path: ${valkeyServer}`)
469
- }
470
- }
471
-
472
- // If we didn't find the binary above, fall back to normal path
473
- if (!valkeyServer) {
474
- // Get binary from downloaded hostdb binaries
475
- try {
476
- valkeyServer = await this.getValkeyServerPath(version)
477
- } catch (error) {
478
- // Binary not downloaded yet - this is an orphaned container situation
479
- const originalMessage =
480
- error instanceof Error ? error.message : String(error)
481
- throw new Error(
482
- `Valkey ${version} is not installed. Run: spindb engines download valkey ${version}\n` +
483
- ` Original error: ${originalMessage}`,
484
- )
485
- }
486
- }
487
-
488
- logDebug(`Using valkey-server for version ${version}: ${valkeyServer}`)
489
-
490
- // Compute library fallback paths from the binary directory
491
- const binBaseDir = binaryPath || this.getBinaryPath(version)
492
- const libraryEnv = getLibraryEnv(binBaseDir)
493
-
494
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
495
- const configPath = join(containerDir, 'valkey.conf')
496
- const dataDir = paths.getContainerDataPath(name, { engine: ENGINE })
497
- const logFile = paths.getContainerLogPath(name, { engine: ENGINE })
498
- const pidFile = join(containerDir, 'valkey.pid')
499
-
500
- // Windows Valkey doesn't support daemonize natively
501
- // Use detached spawn on Windows instead, similar to MongoDB
502
- const useDetachedSpawn = isWindows()
503
-
504
- // Regenerate config with current port (in case it changed)
505
- const configContent = generateValkeyConfig({
506
- port,
507
- dataDir,
508
- logFile,
509
- pidFile,
510
- daemonize: !useDetachedSpawn, // Disable daemonize on Windows
511
- })
512
- await writeFile(configPath, configContent)
513
-
514
- onProgress?.({ stage: 'starting', message: 'Starting Valkey...' })
515
-
516
- logDebug(`Starting valkey-server with config: ${configPath}`)
517
-
518
- /**
519
- * Check log file for port binding errors
520
- * Returns error message if found, null otherwise
521
- */
522
- const checkLogForPortError = async (): Promise<string | null> => {
523
- try {
524
- const logContent = await readFile(logFile, 'utf-8')
525
- const recentLog = logContent.slice(-2000) // Last 2KB
526
-
527
- if (
528
- recentLog.includes('Address already in use') ||
529
- recentLog.includes('bind: Address already in use')
530
- ) {
531
- return `Port ${port} is already in use (address already in use)`
532
- }
533
- if (recentLog.includes('Failed listening on port')) {
534
- return `Port ${port} is already in use`
535
- }
536
- } catch {
537
- // Log file might not exist yet
538
- }
539
- return null
540
- }
541
-
542
- if (useDetachedSpawn) {
543
- // Windows: spawn detached process with proper error handling
544
- // This follows the pattern used by MySQL which works on Windows
545
- return new Promise((resolve, reject) => {
546
- const spawnOpts: SpawnOptions = {
547
- stdio: ['ignore', 'pipe', 'pipe'],
548
- detached: true,
549
- windowsHide: true,
550
- env: { ...process.env, ...libraryEnv },
551
- }
552
-
553
- // Convert Windows path to Cygwin format for Cygwin-built binaries
554
- const cygwinConfigPath = toCygwinPath(configPath)
555
- const proc = spawn(valkeyServer, [cygwinConfigPath], spawnOpts)
556
- let settled = false
557
- let stderrOutput = ''
558
- let stdoutOutput = ''
559
-
560
- // Handle spawn errors (binary not found, DLL issues, etc.)
561
- proc.on('error', (err) => {
562
- if (settled) return
563
- settled = true
564
- reject(new Error(`Failed to spawn Valkey server: ${err.message}`))
565
- })
566
-
567
- proc.stdout?.on('data', (data: Buffer) => {
568
- const str = data.toString()
569
- stdoutOutput += str
570
- logDebug(`valkey-server stdout: ${str}`)
571
- })
572
- proc.stderr?.on('data', (data: Buffer) => {
573
- const str = data.toString()
574
- stderrOutput += str
575
- logDebug(`valkey-server stderr: ${str}`)
576
- })
577
-
578
- // Detach the process so it continues running after parent exits
579
- proc.unref()
580
-
581
- // Give spawn a moment to fail if it's going to, then check readiness
582
- setTimeout(async () => {
583
- if (settled) return
584
-
585
- // Verify process actually started
586
- if (!proc.pid) {
587
- settled = true
588
- reject(new Error('Valkey server process failed to start (no PID)'))
589
- return
590
- }
591
-
592
- // Write PID file for consistency with other engines
593
- try {
594
- await writeFile(pidFile, String(proc.pid))
595
- } catch {
596
- // Non-fatal - process is running, PID file is for convenience
597
- }
598
-
599
- // Wait for Valkey to be ready
600
- const ready = await this.waitForReady(port, version)
601
- if (settled) return
602
-
603
- if (ready) {
604
- // On Windows, Cygwin binaries may fork internally, making proc.pid stale.
605
- // Find the actual PID by port and update the PID file (same pattern as QuestDB).
606
- try {
607
- const pids = await platformService.findProcessByPort(port)
608
- if (pids.length > 0 && pids[0] !== proc.pid) {
609
- logDebug(
610
- `Valkey actual PID ${pids[0]} differs from spawn PID ${proc.pid}, updating PID file`,
611
- )
612
- await writeFile(pidFile, String(pids[0]))
613
- }
614
- } catch {
615
- // Non-fatal - PID file already has proc.pid from earlier write
616
- }
617
-
618
- settled = true
619
- resolve({
620
- port,
621
- connectionString: this.getConnectionString(container),
622
- })
623
- } else {
624
- settled = true
625
- const portError = await checkLogForPortError()
626
-
627
- // Read log file content for better error diagnostics
628
- let logContent = ''
629
- try {
630
- logContent = await readFile(logFile, 'utf-8')
631
- } catch {
632
- logContent = '(log file not found or empty)'
633
- }
634
-
635
- // Check for library loading errors first
636
- const libError = detectLibraryError(
637
- stderrOutput + logContent,
638
- 'Valkey',
639
- )
640
- if (libError) {
641
- reject(new Error(libError))
642
- return
643
- }
644
-
645
- const errorDetails = [
646
- portError || 'Valkey failed to start within timeout.',
647
- `Binary: ${valkeyServer}`,
648
- `Config: ${configPath}`,
649
- `Log file: ${logFile}`,
650
- `Log content:\n${logContent || '(empty)'}`,
651
- stderrOutput ? `Stderr:\n${stderrOutput}` : '',
652
- stdoutOutput ? `Stdout:\n${stdoutOutput}` : '',
653
- ]
654
- .filter(Boolean)
655
- .join('\n')
656
-
657
- reject(new Error(errorDetails))
658
- }
659
- }, 500)
660
- })
661
- }
662
-
663
- // Unix: Valkey with daemonize: yes handles its own forking
664
- return new Promise((resolve, reject) => {
665
- const proc = spawn(valkeyServer, [configPath], {
666
- stdio: ['ignore', 'pipe', 'pipe'],
667
- env: { ...process.env, ...libraryEnv },
668
- })
669
-
670
- let stdout = ''
671
- let stderr = ''
672
-
673
- proc.stdout?.on('data', (data: Buffer) => {
674
- stdout += data.toString()
675
- logDebug(`valkey-server stdout: ${data.toString()}`)
676
- })
677
- proc.stderr?.on('data', (data: Buffer) => {
678
- stderr += data.toString()
679
- logDebug(`valkey-server stderr: ${data.toString()}`)
680
- })
681
-
682
- proc.on('error', reject)
683
-
684
- proc.on('close', async (code) => {
685
- // Valkey with daemonize: yes exits immediately after forking
686
- // Exit code 0 means the parent forked successfully, but the child may still fail
687
- if (code === 0 || code === null) {
688
- // Give the child process a moment to start (or fail)
689
- await new Promise((r) => setTimeout(r, 500))
690
-
691
- // Check log for early startup failures (like port conflicts)
692
- const earlyError = await checkLogForPortError()
693
- if (earlyError) {
694
- reject(new Error(earlyError))
695
- return
696
- }
697
-
698
- // Wait for Valkey to be ready
699
- const ready = await this.waitForReady(port, version)
700
- if (ready) {
701
- resolve({
702
- port,
703
- connectionString: this.getConnectionString(container),
704
- })
705
- } else {
706
- // Check log again for errors if not ready
707
- const portError = await checkLogForPortError()
708
- if (portError) {
709
- reject(new Error(portError))
710
- return
711
- }
712
-
713
- // Check for library loading errors
714
- let logContent = ''
715
- try {
716
- logContent = await readFile(logFile, 'utf-8')
717
- } catch {
718
- logContent = ''
719
- }
720
- const libError = detectLibraryError(stderr + logContent, 'Valkey')
721
- if (libError) {
722
- reject(new Error(libError))
723
- return
724
- }
725
-
726
- reject(
727
- new Error(
728
- `Valkey failed to start within timeout. Check logs at: ${logFile}`,
729
- ),
730
- )
731
- }
732
- } else {
733
- // Check for library loading errors on non-zero exit
734
- const libError = detectLibraryError(stderr || stdout, 'Valkey')
735
- if (libError) {
736
- reject(new Error(libError))
737
- return
738
- }
739
- reject(
740
- new Error(
741
- stderr || stdout || `valkey-server exited with code ${code}`,
742
- ),
743
- )
744
- }
745
- })
746
- })
747
- }
748
-
749
- // Wait for Valkey to be ready to accept connections
750
- // TODO - consider copying the mongodb logic for this
751
- private async waitForReady(
752
- port: number,
753
- version: string,
754
- timeoutMs = 60000,
755
- ): Promise<boolean> {
756
- const startTime = Date.now()
757
- const checkInterval = 500
758
-
759
- let valkeyCli: string
760
- try {
761
- valkeyCli = await this.getValkeyCliPathForVersion(version)
762
- } catch {
763
- logWarning(
764
- 'valkey-cli not found, cannot verify Valkey is ready. Assuming ready after brief delay.',
765
- )
766
- // Give Valkey a moment to start, then assume success
767
- await new Promise((resolve) => setTimeout(resolve, 2000))
768
- return true
769
- }
770
-
771
- while (Date.now() - startTime < timeoutMs) {
772
- try {
773
- const cmd = `"${valkeyCli}" -h 127.0.0.1 -p ${port} PING`
774
- const { stdout } = await execAsync(cmd, { timeout: 5000 })
775
- if (stdout.trim() === 'PONG') {
776
- logDebug(`Valkey ready on port ${port}`)
777
- return true
778
- }
779
- } catch {
780
- await new Promise((resolve) => setTimeout(resolve, checkInterval))
781
- }
782
- }
783
-
784
- logWarning(`Valkey did not become ready within ${timeoutMs}ms`)
785
- return false
786
- }
787
-
788
- /**
789
- * Stop Valkey server
790
- * Uses SHUTDOWN SAVE via valkey-cli to persist data before stopping
791
- */
792
- async stop(container: ContainerConfig): Promise<void> {
793
- const { name, port, version } = container
794
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
795
- const pidFile = join(containerDir, 'valkey.pid')
796
-
797
- logDebug(`Stopping Valkey container "${name}" on port ${port}`)
798
-
799
- // Try graceful shutdown via valkey-cli
800
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
801
- if (valkeyCli) {
802
- try {
803
- const cmd = `"${valkeyCli}" -h 127.0.0.1 -p ${port} SHUTDOWN SAVE`
804
- await execAsync(cmd, { timeout: 10000 })
805
- logDebug('Valkey shutdown command sent')
806
- // Wait a bit for process to exit
807
- await new Promise((resolve) => setTimeout(resolve, 2000))
808
- } catch (error) {
809
- logDebug(`valkey-cli shutdown failed: ${error}`)
810
- // Continue to PID-based shutdown
811
- }
812
- }
813
-
814
- // Get PID and force kill if needed
815
- let pid: number | null = null
816
-
817
- if (existsSync(pidFile)) {
818
- try {
819
- const content = await readFile(pidFile, 'utf8')
820
- pid = parseInt(content.trim(), 10)
821
- } catch {
822
- // Ignore
823
- }
824
- }
825
-
826
- // Kill process if still running
827
- if (pid && platformService.isProcessRunning(pid)) {
828
- logDebug(`Killing Valkey process ${pid}`)
829
- try {
830
- await platformService.terminateProcess(pid, false)
831
- await new Promise((resolve) => setTimeout(resolve, 2000))
832
-
833
- if (platformService.isProcessRunning(pid)) {
834
- logWarning(`Graceful termination failed, force killing ${pid}`)
835
- await platformService.terminateProcess(pid, true)
836
- }
837
- } catch (error) {
838
- logDebug(`Process termination error: ${error}`)
839
- }
840
- }
841
-
842
- // Cleanup PID file
843
- if (existsSync(pidFile)) {
844
- try {
845
- await unlink(pidFile)
846
- } catch {
847
- // Ignore
848
- }
849
- }
850
-
851
- logDebug('Valkey stopped')
852
- }
853
-
854
- // Get Valkey server status
855
- async status(container: ContainerConfig): Promise<StatusResult> {
856
- const { name, port, version } = container
857
- const containerDir = paths.getContainerPath(name, { engine: ENGINE })
858
- const pidFile = join(containerDir, 'valkey.pid')
859
-
860
- // Try pinging with valkey-cli
861
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
862
- if (valkeyCli) {
863
- try {
864
- const cmd = `"${valkeyCli}" -h 127.0.0.1 -p ${port} PING`
865
- const { stdout } = await execAsync(cmd, { timeout: 5000 })
866
- if (stdout.trim() === 'PONG') {
867
- return { running: true, message: 'Valkey is running' }
868
- }
869
- } catch {
870
- // Not responding, check PID
871
- }
872
- }
873
-
874
- // Check PID file
875
- if (existsSync(pidFile)) {
876
- try {
877
- const content = await readFile(pidFile, 'utf8')
878
- const pid = parseInt(content.trim(), 10)
879
- if (!isNaN(pid) && pid > 0 && platformService.isProcessRunning(pid)) {
880
- return {
881
- running: true,
882
- message: `Valkey is running (PID: ${pid})`,
883
- }
884
- }
885
- } catch {
886
- // Ignore
887
- }
888
- }
889
-
890
- return { running: false, message: 'Valkey is not running' }
891
- }
892
-
893
- // Detect backup format
894
- async detectBackupFormat(filePath: string): Promise<BackupFormat> {
895
- return detectBackupFormatImpl(filePath)
896
- }
897
-
898
- /**
899
- * Restore a backup
900
- * IMPORTANT: Valkey must be stopped before restore
901
- */
902
- async restore(
903
- container: ContainerConfig,
904
- backupPath: string,
905
- options: { database?: string; flush?: boolean } = {},
906
- ): Promise<RestoreResult> {
907
- const { name, port } = container
908
- const dataDir = paths.getContainerDataPath(name, { engine: ENGINE })
909
-
910
- return restoreBackup(backupPath, {
911
- containerName: name,
912
- dataDir,
913
- port,
914
- database: options.database || container.database || '0',
915
- flush: options.flush,
916
- })
917
- }
918
-
919
- /**
920
- * Get connection string
921
- * Format: redis://127.0.0.1:PORT/DATABASE
922
- * (Uses redis:// scheme for client compatibility)
923
- */
924
- getConnectionString(container: ContainerConfig, database?: string): string {
925
- const { port } = container
926
- const db = database || container.database || '0'
927
- return `redis://127.0.0.1:${port}/${db}`
928
- }
929
-
930
- /**
931
- * Get path to valkey-cli for a specific version
932
- * @param version - Optional version (e.g., "8", "9"). If not provided, uses cached path.
933
- * @deprecated Use getValkeyCliPath() instead
934
- */
935
- async getValkeyCliPathForVersion(version?: string): Promise<string> {
936
- return this.getValkeyCliPath(version)
937
- }
938
-
939
- // Open valkey-cli interactive shell
940
- async connect(container: ContainerConfig, database?: string): Promise<void> {
941
- const { port, version } = container
942
- const db = database || container.database || '0'
943
-
944
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
945
-
946
- const spawnOptions: SpawnOptions = {
947
- stdio: 'inherit',
948
- }
949
-
950
- return new Promise((resolve, reject) => {
951
- const proc = spawn(
952
- valkeyCli,
953
- ['-h', '127.0.0.1', '-p', String(port), '-n', db],
954
- spawnOptions,
955
- )
956
-
957
- proc.on('error', reject)
958
- proc.on('close', () => resolve())
959
- })
960
- }
961
-
962
- // Get path to iredis (enhanced CLI) if installed
963
- // Note: iredis works with Valkey since it's protocol-compatible
964
- private async getIredisPath(): Promise<string | null> {
965
- // Check config cache first
966
- const cached = await configManager.getBinaryPath('iredis')
967
- if (cached && existsSync(cached)) {
968
- return cached
969
- }
970
-
971
- // Check system PATH
972
- const systemPath = await platformService.findToolPath('iredis')
973
- if (systemPath) {
974
- return systemPath
975
- }
976
-
977
- return null
978
- }
979
-
980
- // Connect with iredis (enhanced CLI)
981
- async connectWithIredis(
982
- container: ContainerConfig,
983
- database?: string,
984
- ): Promise<void> {
985
- const { port } = container
986
- const db = database || container.database || '0'
987
-
988
- const iredis = await this.getIredisPath()
989
- if (!iredis) {
990
- throw new Error(
991
- 'iredis not found. Install it with:\n' +
992
- ' macOS: brew install iredis\n' +
993
- ' pip: pip install iredis',
994
- )
995
- }
996
-
997
- const spawnOptions: SpawnOptions = {
998
- stdio: 'inherit',
999
- }
1000
-
1001
- return new Promise((resolve, reject) => {
1002
- const proc = spawn(
1003
- iredis,
1004
- ['-h', '127.0.0.1', '-p', String(port), '-n', db],
1005
- spawnOptions,
1006
- )
1007
-
1008
- proc.on('error', reject)
1009
- proc.on('close', () => resolve())
1010
- })
1011
- }
1012
-
1013
- /**
1014
- * Create a new database
1015
- * Valkey uses numbered databases (0-15), they always exist
1016
- * This is effectively a no-op
1017
- */
1018
- async createDatabase(
1019
- _container: ContainerConfig,
1020
- database: string,
1021
- ): Promise<void> {
1022
- const dbNum = parseInt(database, 10)
1023
- if (isNaN(dbNum) || dbNum < 0 || dbNum > 15) {
1024
- throw new Error(
1025
- `Invalid Valkey database number: ${database}. Must be 0-15.`,
1026
- )
1027
- }
1028
- // No-op - Valkey databases always exist
1029
- logDebug(
1030
- `Valkey database ${database} is available (databases 0-15 always exist)`,
1031
- )
1032
- }
1033
-
1034
- /**
1035
- * Drop a database
1036
- * Uses FLUSHDB to clear all keys in the specified database
1037
- */
1038
- async dropDatabase(
1039
- container: ContainerConfig,
1040
- database: string,
1041
- ): Promise<void> {
1042
- const { port, version } = container
1043
- const dbNum = parseInt(database, 10)
1044
- if (isNaN(dbNum) || dbNum < 0 || dbNum > 15) {
1045
- throw new Error(
1046
- `Invalid Valkey database number: ${database}. Must be 0-15.`,
1047
- )
1048
- }
1049
-
1050
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
1051
-
1052
- // SELECT the database and FLUSHDB
1053
- const cmd = `"${valkeyCli}" -h 127.0.0.1 -p ${port} -n ${database} FLUSHDB`
1054
-
1055
- try {
1056
- await execAsync(cmd, { timeout: 10000 })
1057
- logDebug(`Flushed Valkey database ${database}`)
1058
- } catch (error) {
1059
- const err = error as Error
1060
- logDebug(`FLUSHDB failed: ${err.message}`)
1061
- throw new Error(
1062
- `Failed to flush Valkey database ${database}: ${err.message}`,
1063
- )
1064
- }
1065
- }
1066
-
1067
- /**
1068
- * Get the memory usage of the Valkey server in bytes
1069
- *
1070
- * NOTE: Valkey does not provide per-database memory statistics.
1071
- * This returns the total server memory (used_memory from INFO memory),
1072
- * not the size of a specific numbered database (0-15).
1073
- * This is acceptable for SpinDB since each container runs one Valkey server.
1074
- */
1075
- async getDatabaseSize(container: ContainerConfig): Promise<number | null> {
1076
- const { port, version } = container
1077
-
1078
- try {
1079
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
1080
- // INFO memory returns server-wide stats (database selection has no effect)
1081
- const cmd = `"${valkeyCli}" -h 127.0.0.1 -p ${port} INFO memory`
1082
-
1083
- const { stdout } = await execAsync(cmd, { timeout: 10000 })
1084
-
1085
- // Parse used_memory (total server memory) from INFO output
1086
- const match = stdout.match(/used_memory:(\d+)/)
1087
- if (match) {
1088
- return parseInt(match[1], 10)
1089
- }
1090
- return null
1091
- } catch {
1092
- return null
1093
- }
1094
- }
1095
-
1096
- /**
1097
- * Dump from a remote Valkey connection
1098
- * Creates a text-format backup by scanning all keys from the remote server
1099
- *
1100
- * Connection string format: redis://[user:password@]host:port[/db]
1101
- * Note: Uses redis:// scheme for compatibility (Valkey is Redis-compatible)
1102
- */
1103
- async dumpFromConnectionString(
1104
- connectionString: string,
1105
- outputPath: string,
1106
- ): Promise<DumpResult> {
1107
- const valkeyCli = await getValkeyCliPath()
1108
- if (!valkeyCli) {
1109
- throw new Error(VALKEY_CLI_NOT_FOUND_ERROR)
1110
- }
1111
-
1112
- // Parse connection string (uses redis:// for compatibility)
1113
- const { host, port, username, password, database, tls } =
1114
- parseValkeyConnectionString(connectionString)
1115
-
1116
- logDebug(
1117
- `Connecting to remote Valkey at ${host}:${port} (db: ${database}, tls: ${tls})`,
1118
- )
1119
-
1120
- // Build CLI args for remote connection (password passed via env var for security)
1121
- const buildArgs = (): string[] => {
1122
- const args = ['-h', host, '-p', String(port)]
1123
- // ACL: pass username via --user flag (inherited from Redis 6.0+)
1124
- if (username) {
1125
- args.push('--user', username)
1126
- }
1127
- // Enable TLS for rediss:// or valkeys:// schemes
1128
- if (tls) {
1129
- args.push('--tls')
1130
- }
1131
- // Note: password is passed via REDISCLI_AUTH env var, not command line
1132
- args.push('-n', String(database))
1133
- return args
1134
- }
1135
-
1136
- // Execute a Valkey command on the remote server with timeout
1137
- const execRemote = async (
1138
- command: string,
1139
- timeoutMs = 30000,
1140
- ): Promise<string> => {
1141
- return new Promise((resolve, reject) => {
1142
- const args = buildArgs()
1143
- // Pass password via REDISCLI_AUTH env var to avoid exposing it in process listings
1144
- const env = password
1145
- ? { ...process.env, REDISCLI_AUTH: password }
1146
- : process.env
1147
- const proc = spawn(valkeyCli, args, {
1148
- stdio: ['pipe', 'pipe', 'pipe'],
1149
- env,
1150
- })
1151
-
1152
- let stdout = ''
1153
- let stderr = ''
1154
- let settled = false
1155
-
1156
- // Timeout handler to prevent hanging
1157
- const timeoutId = setTimeout(() => {
1158
- if (settled) return
1159
- settled = true
1160
- proc.kill()
1161
- reject(
1162
- new Error(
1163
- `Command timed out after ${timeoutMs}ms: ${command.slice(0, 50)}...`,
1164
- ),
1165
- )
1166
- }, timeoutMs)
1167
-
1168
- proc.stdout.on('data', (data: Buffer) => {
1169
- stdout += data.toString()
1170
- })
1171
- proc.stderr.on('data', (data: Buffer) => {
1172
- stderr += data.toString()
1173
- })
1174
-
1175
- proc.on('error', (err) => {
1176
- if (settled) return
1177
- settled = true
1178
- clearTimeout(timeoutId)
1179
- reject(err)
1180
- })
1181
-
1182
- proc.on('close', (code) => {
1183
- if (settled) return
1184
- settled = true
1185
- clearTimeout(timeoutId)
1186
- // Ignore auth-related warnings in stderr (password provided via REDISCLI_AUTH)
1187
- if (code === 0 || code === null) {
1188
- resolve(stdout)
1189
- } else {
1190
- reject(new Error(stderr || `valkey-cli exited with code ${code}`))
1191
- }
1192
- })
1193
-
1194
- proc.stdin.write(command + '\n')
1195
- proc.stdin.end()
1196
- })
1197
- }
1198
-
1199
- // Test connectivity
1200
- try {
1201
- const pingResult = await execRemote('PING')
1202
- if (!pingResult.trim().includes('PONG')) {
1203
- throw new Error(`Unexpected PING response: ${pingResult.trim()}`)
1204
- }
1205
- } catch (error) {
1206
- throw new Error(
1207
- `Failed to connect to Valkey at ${host}:${port}: ${(error as Error).message}`,
1208
- )
1209
- }
1210
-
1211
- // Build text backup from remote keys
1212
- const commands: string[] = []
1213
- commands.push('# Valkey backup generated by SpinDB')
1214
- commands.push(`# Source: ${host}:${port}`)
1215
- commands.push(`# Date: ${new Date().toISOString()}`)
1216
- commands.push('')
1217
-
1218
- // WARNING: KEYS * blocks the Valkey server during execution.
1219
- // This is acceptable for small datasets but will cause performance issues
1220
- // on large databases. For production use with large datasets, consider
1221
- // implementing SCAN-based iteration instead.
1222
- // TODO: Replace with SCAN iterator for large dataset support
1223
- const keysOutput = await execRemote('KEYS *')
1224
- const keys = keysOutput
1225
- .trim()
1226
- .split(/\r?\n/)
1227
- .map((k) => k.trim())
1228
- .filter((k) => k)
1229
-
1230
- logDebug(`Found ${keys.length} keys on remote Valkey`)
1231
-
1232
- // Warn about large key counts that may cause performance issues
1233
- if (keys.length > 10000) {
1234
- logWarning(
1235
- `Large key count detected: ${keys.length} keys. ` +
1236
- 'This operation may be slow. Consider using SCAN-based iteration for production workloads.',
1237
- )
1238
- }
1239
-
1240
- // TODO: Optimize with pipelining or Lua script to batch TYPE/TTL/value fetches
1241
- // Currently makes O(3N) round trips which is slow for large datasets.
1242
- // A pipelined approach could fetch all data in fewer round trips.
1243
-
1244
- for (const key of keys) {
1245
- // Get key type
1246
- const typeOutput = await execRemote(`TYPE "${escapeKeyForCommand(key)}"`)
1247
- const keyType = typeOutput.trim()
1248
-
1249
- // Get TTL
1250
- const ttlOutput = await execRemote(`TTL "${escapeKeyForCommand(key)}"`)
1251
- const ttl = parseInt(ttlOutput.trim(), 10)
1252
-
1253
- // Quote the key for output commands if it contains special chars
1254
- const quotedKey =
1255
- key.includes(' ') || /[*?[\]{}$`"'\\!<>|;&()]/.test(key)
1256
- ? `"${key.replace(/"/g, '\\"')}"`
1257
- : key
1258
-
1259
- // Redis-cli compatible double-quote escaping for values
1260
- // Escapes backslashes and double quotes, converts newlines to \n sequences
1261
- // Note: This approach doesn't handle binary data.
1262
- // For binary-safe backups, consider using DUMP/RESTORE commands instead.
1263
- const escapeValue = (value: string): string => {
1264
- const escaped = value
1265
- .replace(/\\/g, '\\\\')
1266
- .replace(/"/g, '\\"')
1267
- .replace(/\n/g, '\\n')
1268
- .replace(/\r/g, '\\r')
1269
- return `"${escaped}"`
1270
- }
1271
-
1272
- // Strip only trailing newline from execRemote output, preserving intentional whitespace
1273
- const stripTrailingNewline = (s: string): string =>
1274
- s.replace(/\r?\n$/, '')
1275
-
1276
- switch (keyType) {
1277
- case 'string': {
1278
- const value = await execRemote(`GET "${escapeKeyForCommand(key)}"`)
1279
- commands.push(
1280
- `SET ${quotedKey} ${escapeValue(stripTrailingNewline(value))}`,
1281
- )
1282
- break
1283
- }
1284
- case 'hash': {
1285
- const hashData = await execRemote(
1286
- `HGETALL "${escapeKeyForCommand(key)}"`,
1287
- )
1288
- const lines = stripTrailingNewline(hashData)
1289
- .split(/\r?\n/)
1290
- .filter((l) => l)
1291
- if (lines.length >= 2) {
1292
- const pairs: string[] = []
1293
- // Handle odd number of lines (incomplete field/value pair)
1294
- const completeCount = lines.length - (lines.length % 2)
1295
- if (lines.length % 2 !== 0) {
1296
- logWarning(
1297
- `Hash ${quotedKey} has incomplete field/value pair, skipping last field`,
1298
- )
1299
- }
1300
- for (let i = 0; i < completeCount; i += 2) {
1301
- const field = lines[i]
1302
- const value = lines[i + 1]
1303
- const quotedField =
1304
- field.includes(' ') || /[*?[\]{}$`"'\\!<>|;&()]/.test(field)
1305
- ? `"${field.replace(/"/g, '\\"')}"`
1306
- : field
1307
- pairs.push(`${quotedField} ${escapeValue(value)}`)
1308
- }
1309
- if (pairs.length > 0) {
1310
- commands.push(`HSET ${quotedKey} ${pairs.join(' ')}`)
1311
- }
1312
- }
1313
- break
1314
- }
1315
- case 'list': {
1316
- const listData = await execRemote(
1317
- `LRANGE "${escapeKeyForCommand(key)}" 0 -1`,
1318
- )
1319
- const items = stripTrailingNewline(listData)
1320
- .split(/\r?\n/)
1321
- .filter((l) => l)
1322
- if (items.length > 0) {
1323
- const escapedItems = items.map((item) => escapeValue(item))
1324
- commands.push(`RPUSH ${quotedKey} ${escapedItems.join(' ')}`)
1325
- }
1326
- break
1327
- }
1328
- case 'set': {
1329
- const setData = await execRemote(
1330
- `SMEMBERS "${escapeKeyForCommand(key)}"`,
1331
- )
1332
- const members = stripTrailingNewline(setData)
1333
- .split(/\r?\n/)
1334
- .filter((l) => l)
1335
- if (members.length > 0) {
1336
- const escapedMembers = members.map((m) => escapeValue(m))
1337
- commands.push(`SADD ${quotedKey} ${escapedMembers.join(' ')}`)
1338
- }
1339
- break
1340
- }
1341
- case 'zset': {
1342
- const zsetData = await execRemote(
1343
- `ZRANGE "${escapeKeyForCommand(key)}" 0 -1 WITHSCORES`,
1344
- )
1345
- const lines = stripTrailingNewline(zsetData)
1346
- .split(/\r?\n/)
1347
- .filter((l) => l)
1348
- if (lines.length >= 2) {
1349
- const pairs: string[] = []
1350
- // Handle odd number of lines (incomplete member/score pair)
1351
- const completeCount = lines.length - (lines.length % 2)
1352
- if (lines.length % 2 !== 0) {
1353
- logWarning(
1354
- `ZSet ${quotedKey} has odd line count, skipping incomplete entry: ${lines[lines.length - 1]}`,
1355
- )
1356
- }
1357
- for (let i = 0; i < completeCount; i += 2) {
1358
- const member = lines[i]
1359
- const score = lines[i + 1]
1360
- pairs.push(`${score} ${escapeValue(member)}`)
1361
- }
1362
- if (pairs.length > 0) {
1363
- commands.push(`ZADD ${quotedKey} ${pairs.join(' ')}`)
1364
- }
1365
- }
1366
- break
1367
- }
1368
- default:
1369
- logWarning(`Skipping key ${key} with unsupported type: ${keyType}`)
1370
- }
1371
-
1372
- // Add EXPIRE if key has TTL
1373
- if (ttl > 0) {
1374
- commands.push(`EXPIRE ${quotedKey} ${ttl}`)
1375
- }
1376
- }
1377
-
1378
- // Write commands to file
1379
- const content = commands.join('\n') + '\n'
1380
- await writeFile(outputPath, content, 'utf-8')
1381
-
1382
- return {
1383
- filePath: outputPath,
1384
- warnings:
1385
- keys.length === 0 ? ['Remote Valkey database is empty'] : undefined,
1386
- }
1387
- }
1388
-
1389
- // Create a backup
1390
- async backup(
1391
- container: ContainerConfig,
1392
- outputPath: string,
1393
- options: BackupOptions,
1394
- ): Promise<BackupResult> {
1395
- return createBackup(container, outputPath, options)
1396
- }
1397
-
1398
- // Run a Valkey command file or inline command
1399
- async runScript(
1400
- container: ContainerConfig,
1401
- options: { file?: string; sql?: string; database?: string },
1402
- ): Promise<void> {
1403
- const { port, version } = container
1404
- const db = options.database || container.database || '0'
1405
-
1406
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
1407
-
1408
- if (options.file) {
1409
- // Read file and pipe to valkey-cli via stdin (avoids shell interpolation issues)
1410
- const fileContent = await readFile(options.file, 'utf-8')
1411
- const args = ['-h', '127.0.0.1', '-p', String(port), '-n', db]
1412
-
1413
- await new Promise<void>((resolve, reject) => {
1414
- const proc = spawn(valkeyCli, args, {
1415
- stdio: ['pipe', 'inherit', 'inherit'],
1416
- })
1417
-
1418
- let rejected = false
1419
-
1420
- proc.on('error', (err) => {
1421
- rejected = true
1422
- reject(err)
1423
- })
1424
-
1425
- proc.on('close', (code) => {
1426
- if (rejected) return
1427
- if (code === 0 || code === null) {
1428
- resolve()
1429
- } else {
1430
- reject(new Error(`valkey-cli exited with code ${code}`))
1431
- }
1432
- })
1433
-
1434
- // Write file content to stdin and close it
1435
- proc.stdin?.write(fileContent)
1436
- proc.stdin?.end()
1437
- })
1438
- } else if (options.sql) {
1439
- // Run inline command by piping to valkey-cli stdin (avoids shell quoting issues on Windows)
1440
- const args = ['-h', '127.0.0.1', '-p', String(port), '-n', db]
1441
-
1442
- await new Promise<void>((resolve, reject) => {
1443
- const proc = spawn(valkeyCli, args, {
1444
- stdio: ['pipe', 'inherit', 'inherit'],
1445
- })
1446
-
1447
- let rejected = false
1448
-
1449
- proc.on('error', (err) => {
1450
- rejected = true
1451
- reject(err)
1452
- })
1453
-
1454
- proc.on('close', (code) => {
1455
- if (rejected) return
1456
- if (code === 0 || code === null) {
1457
- resolve()
1458
- } else {
1459
- reject(new Error(`valkey-cli exited with code ${code}`))
1460
- }
1461
- })
1462
-
1463
- // Write command to stdin and close it
1464
- proc.stdin?.write(options.sql + '\n')
1465
- proc.stdin?.end()
1466
- })
1467
- } else {
1468
- throw new Error('Either file or sql option must be provided')
1469
- }
1470
- }
1471
-
1472
- async executeQuery(
1473
- container: ContainerConfig,
1474
- query: string,
1475
- options?: QueryOptions,
1476
- ): Promise<QueryResult> {
1477
- const { port, version } = container
1478
- const db = options?.database || container.database || '0'
1479
-
1480
- const valkeyCli = await this.getValkeyCliPathForVersion(version)
1481
-
1482
- return new Promise((resolve, reject) => {
1483
- const args = ['-h', '127.0.0.1', '-p', String(port), '-n', db]
1484
-
1485
- const proc = spawn(valkeyCli, args, {
1486
- stdio: ['pipe', 'pipe', 'pipe'],
1487
- })
1488
-
1489
- let stdout = ''
1490
- let stderr = ''
1491
-
1492
- proc.stdout?.on('data', (data: Buffer) => {
1493
- stdout += data.toString()
1494
- })
1495
- proc.stderr?.on('data', (data: Buffer) => {
1496
- stderr += data.toString()
1497
- })
1498
-
1499
- proc.on('error', reject)
1500
-
1501
- proc.on('close', (code) => {
1502
- if (code === 0 || code === null) {
1503
- // Use Redis parser since Valkey is Redis-compatible
1504
- resolve(parseRedisResult(stdout, query))
1505
- } else {
1506
- reject(new Error(stderr || `valkey-cli exited with code ${code}`))
1507
- }
1508
- })
1509
-
1510
- // Write command to stdin and close it
1511
- proc.stdin?.write(query + '\n')
1512
- proc.stdin?.end()
1513
- })
1514
- }
1515
-
1516
- /**
1517
- * List databases for Valkey.
1518
- * Valkey uses numbered databases (0-15 by default), not named databases.
1519
- * Returns the configured database number as a single-item array.
1520
- */
1521
- async listDatabases(container: ContainerConfig): Promise<string[]> {
1522
- // Valkey has numbered databases, not named ones
1523
- // Return the container's configured database
1524
- return [container.database]
1525
- }
1526
-
1527
- async createUser(
1528
- container: ContainerConfig,
1529
- options: CreateUserOptions,
1530
- ): Promise<UserCredentials> {
1531
- const { username, password } = options
1532
- assertValidUsername(username)
1533
- const { port, version } = container
1534
- const db = options.database ?? container.database ?? '0'
1535
- const valkeyCli = await this.getValkeyCliPath(version)
1536
-
1537
- // Reject passwords with characters that break ACL SETUSER syntax:
1538
- // '>' sets password, '#' sets hash, '<' removes password — all are ACL delimiters.
1539
- // Whitespace and newlines would split the command unexpectedly.
1540
- if (/[>#<\s\n\r]/.test(password)) {
1541
- throw new Error(
1542
- 'Password contains invalid characters for Valkey ACL. Passwords must not contain ">", "#", "<", whitespace, or newlines.',
1543
- )
1544
- }
1545
-
1546
- // ACL SETUSER is idempotent - sets user with full access
1547
- // Send ACL command via stdin to avoid leaking password in process argv
1548
- const cliArgs = ['-h', '127.0.0.1', '-p', String(port), '-n', db]
1549
-
1550
- await new Promise<void>((resolve, reject) => {
1551
- const proc = spawn(valkeyCli, cliArgs, {
1552
- stdio: ['pipe', 'pipe', 'pipe'],
1553
- })
1554
-
1555
- let stderr = ''
1556
- proc.stderr?.on('data', (data: Buffer) => {
1557
- stderr += data.toString()
1558
- })
1559
-
1560
- proc.on('close', (code) => {
1561
- if (code === 0) resolve()
1562
- else reject(new Error(`Failed to create user: ${stderr}`))
1563
- })
1564
- proc.on('error', reject)
1565
-
1566
- proc.stdin?.write(`ACL SETUSER ${username} on >${password} ~* &* +@all\n`)
1567
- proc.stdin?.end()
1568
- })
1569
- logDebug(`Created Valkey user: ${username}`)
1570
-
1571
- // Valkey uses redis:// scheme for compatibility
1572
- const connectionString = `redis://${encodeURIComponent(username)}:${encodeURIComponent(password)}@127.0.0.1:${port}/${db}`
1573
-
1574
- return {
1575
- username,
1576
- password,
1577
- connectionString,
1578
- engine: container.engine,
1579
- container: container.name,
1580
- database: db,
1581
- }
1582
- }
1583
- }
1584
-
1585
- export const valkeyEngine = new ValkeyEngine()