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