spindb 0.37.2 → 0.38.0

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