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