diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf4e4c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +node_modules/ +dist/ +*.js.map +*.d.ts +!src/**/*.d.ts + +# OS files +.DS_Store +Thumbs.db + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Environment +.env +.env.local + +# Build artifacts +out/ +bin/ +*.tgz diff --git a/Makefile b/Makefile deleted file mode 100644 index 7913e27..0000000 --- a/Makefile +++ /dev/null @@ -1,98 +0,0 @@ -GOCMD=go -GOTEST=$(GOCMD) test -GOVET=$(GOCMD) vet -BINARY_NAME=daptin-cli -VERSION?=0.0.0 -SERVICE_PORT?=3000 -DOCKER_REGISTRY?= #if set it should finished by / -EXPORT_RESULT?=false # for CI please set EXPORT_RESULT to true - -GREEN := $(shell tput -Txterm setaf 2) -YELLOW := $(shell tput -Txterm setaf 3) -WHITE := $(shell tput -Txterm setaf 7) -CYAN := $(shell tput -Txterm setaf 6) -RESET := $(shell tput -Txterm sgr0) - -.PHONY: all test build vendor - -all: help - -## Build: -build: ## Build your project and put the output binary in out/bin/ - mkdir -p out/bin - GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) . - -clean: ## Remove build related file - rm -fr ./bin - rm -fr ./out - rm -f ./junit-report.xml checkstyle-report.xml ./coverage.xml ./profile.cov yamllint-checkstyle.xml - -vendor: ## Copy of all packages needed to support builds and tests in the vendor directory - $(GOCMD) mod vendor - -watch: ## Run the code with cosmtrek/air to have automatic reload on changes - $(eval PACKAGE_NAME=$(shell head -n 1 go.mod | cut -d ' ' -f2)) - docker run -it --rm -w /go/src/$(PACKAGE_NAME) -v $(shell pwd):/go/src/$(PACKAGE_NAME) -p $(SERVICE_PORT):$(SERVICE_PORT) cosmtrek/air - -## Test: -test: ## Run the tests of the project -ifeq ($(EXPORT_RESULT), true) - GO111MODULE=off go get -u github.com/jstemmer/go-junit-report - $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > junit-report.xml) -endif - $(GOTEST) -v -race ./... $(OUTPUT_OPTIONS) - -coverage: ## Run the tests of the project and export the coverage - $(GOTEST) -cover -covermode=count -coverprofile=profile.cov ./... - $(GOCMD) tool cover -func profile.cov -ifeq ($(EXPORT_RESULT), true) - GO111MODULE=off go get -u github.com/AlekSi/gocov-xml - GO111MODULE=off go get -u github.com/axw/gocov/gocov - gocov convert profile.cov | gocov-xml > coverage.xml -endif - -## Lint: -lint: lint-go lint-dockerfile lint-yaml ## Run all available linters - -lint-dockerfile: ## Lint your Dockerfile -# If dockerfile is present we lint it. -ifeq ($(shell test -e ./Dockerfile && echo -n yes),yes) - $(eval CONFIG_OPTION = $(shell [ -e $(shell pwd)/.hadolint.yaml ] && echo "-v $(shell pwd)/.hadolint.yaml:/root/.config/hadolint.yaml" || echo "" )) - $(eval OUTPUT_OPTIONS = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "--format checkstyle" || echo "" )) - $(eval OUTPUT_FILE = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "| tee /dev/tty > checkstyle-report.xml" || echo "" )) - docker run --rm -i $(CONFIG_OPTION) hadolint/hadolint hadolint $(OUTPUT_OPTIONS) - < ./Dockerfile $(OUTPUT_FILE) -endif - -lint-go: ## Use golintci-lint on your project - $(eval OUTPUT_OPTIONS = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "--out-format checkstyle ./... | tee /dev/tty > checkstyle-report.xml" || echo "" )) - docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:latest-alpine golangci-lint run --deadline=65s $(OUTPUT_OPTIONS) - -lint-yaml: ## Use yamllint on the yaml file of your projects -ifeq ($(EXPORT_RESULT), true) - GO111MODULE=off go get -u github.com/thomaspoignant/yamllint-checkstyle - $(eval OUTPUT_OPTIONS = | tee /dev/tty | yamllint-checkstyle > yamllint-checkstyle.xml) -endif - docker run --rm -it -v $(shell pwd):/data cytopia/yamllint -f parsable $(shell git ls-files '*.yml' '*.yaml') $(OUTPUT_OPTIONS) - -## Docker: -docker-build: ## Use the dockerfile to build the container - docker build --rm --tag $(BINARY_NAME) . - -docker-release: ## Release the container with tag latest and version - docker tag $(BINARY_NAME) $(DOCKER_REGISTRY)$(BINARY_NAME):latest - docker tag $(BINARY_NAME) $(DOCKER_REGISTRY)$(BINARY_NAME):$(VERSION) - # Push the docker images - docker push $(DOCKER_REGISTRY)$(BINARY_NAME):latest - docker push $(DOCKER_REGISTRY)$(BINARY_NAME):$(VERSION) - -## Help: -help: ## Show this help. - @echo '' - @echo 'Usage:' - @echo ' ${YELLOW}make${RESET} ${GREEN}${RESET}' - @echo '' - @echo 'Targets:' - @awk 'BEGIN {FS = ":.*?## "} { \ - if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \ - else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \ - }' $(MAKEFILE_LIST) \ No newline at end of file diff --git a/README.md b/README.md index a84bd7c..00fb20e 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,213 @@ # Daptin CLI -CLI for talking to [Daptin server](https://github.com/daptin/daptin) +Command-line interface for [Daptin](https://github.com/daptin/daptin) Backend-as-a-Service. + +Built with TypeScript, inspired by [daptin-js-client](https://github.com/artpar/daptin-js-client). + +## Installation + +```bash +npm install -g daptin-cli +``` + +Or build from source: + +```bash +git clone https://github.com/daptin/daptin-cli.git +cd daptin-cli +npm install +npm run build +npm link +``` + +## Quick Start + +```bash +# Add a Daptin server +daptin-cli config add local http://localhost:6336 + +# Sign in +daptin-cli signin admin@example.com + +# List all tables +daptin-cli tables + +# List records from a table +daptin-cli list user_account --page-size 20 + +# Get a single record +daptin-cli get user_account +``` + +## Configuration + +The CLI stores configuration in `~/.daptin/config.yaml`. You can manage multiple server contexts: + +```bash +# Add servers +daptin-cli config add local http://localhost:6336 +daptin-cli config add production https://api.example.com + +# Switch active server +daptin-cli config use production + +# List all servers +daptin-cli config list + +# Show current context +daptin-cli config show + +# Remove a server +daptin-cli config remove local +``` + +Override the endpoint on any command with `--endpoint`: + +```bash +daptin-cli --endpoint http://localhost:6336 tables +``` + +## Global Options + +| Flag | Description | Default | +|------|-------------|---------| +| `-e, --endpoint ` | Daptin server endpoint | from config | +| `-c, --config ` | Config file path | `~/.daptin/config.yaml` | +| `-o, --output ` | Output format (`table`, `json`) | `table` | +| `-t, --token ` | Auth token | from config | +| `--debug` | Enable debug output | `false` | + +## Commands + +### Authentication ```bash -go get github.com/daptin/daptin-cli -go install github.com/daptin/daptin-cli -go build -o daptin-cli +# Create a new account +daptin-cli signup user@example.com + +# Sign in (prompts for password) +daptin-cli signin user@example.com + +# Sign in with 2FA +daptin-cli signin-2fa user@example.com + +# Show current user info +daptin-cli whoami +``` + +### Schema Discovery + +```bash +# List all tables +daptin-cli tables +daptin-cli tables --columns table_name,is_top_level,default_permission + +# Describe a table (columns, relations, actions) +daptin-cli describe user_account +daptin-cli describe product --columns ColumnName,ColumnType,DataType,IsNullable +``` + +### CRUD Operations + +```bash +# List records +daptin-cli list product +daptin-cli list product --page-size 50 --page 2 +daptin-cli list product --columns name,price,reference_id +daptin-cli list product --sort -price,name +daptin-cli list product --include category_id + +# Get a single record +daptin-cli get product +daptin-cli get product --columns name,price + +# Create a record +daptin-cli create product --data '{"name": "Widget", "price": 9.99}' + +# Update a record +daptin-cli update product --data '{"price": 12.99}' + +# Delete a record +daptin-cli delete product + +# Query relationships +daptin-cli relation customer order_id --page-size 20 ``` +### Actions -Describe a table schema ```bash -./daptin-cli --output table describe table world +# List all actions +daptin-cli actions + +# List actions for a specific table +daptin-cli actions user_account + +# Show action schema (input/output fields) +daptin-cli action-describe user_account signin + +# Execute an action +daptin-cli execute user_account generate_jwt_token --data '{"email": "user@example.com"}' + +# Execute an instance-level action +daptin-cli execute order mark_shipped --id --data '{"tracking_number": "ABC123"}' ``` -List items of a table +### Aggregation - ./daptin-cli list --pageSize 50 --columns , +```bash +# Count records +daptin-cli aggregate product --columns count + +# Sum and average +daptin-cli aggregate product --columns "count,sum(price),avg(price)" + +# Group by +daptin-cli aggregate order --columns "status,count,sum(total)" --group status + +# With filters +daptin-cli aggregate product --columns "count,avg(price)" --filter "gt(price,100)" + +# With limit and sort +daptin-cli aggregate order --columns "status,count" --group status --sort "-count" --limit 10 +``` + +### Output Formats ```bash -./daptin-cli list --pageSize 50 --columns reference_id,table_name world +# Table output (default) +daptin-cli list product --output table + +# JSON output +daptin-cli list product --output json + +# JSON output piped to jq +daptin-cli list product -o json | jq '.[].name' ``` +## Environment Variables -Get one row by reference_id +| Variable | Description | +|----------|-------------| +| `DAPTIN_CLI_CONFIG` | Path to config file | +| `DAPTIN_ENDPOINT` | Default server endpoint | - ./daptin-cli get-by-id --columns reference_id,table_name +## Development ```bash -./daptin-cli get-by-id --columns reference_id,table_name,is_top_level world 019228bb-a7cd-773b-a465-c92d7c54d956 +# Install dependencies +npm install + +# Run in development mode +npx tsx src/index.ts tables + +# Type check +npm run typecheck + +# Build +npm run build ``` +## License + +MIT diff --git a/VERSION b/VERSION deleted file mode 100644 index 95e94cd..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -v0.0.1 \ No newline at end of file diff --git a/config.yaml b/config.yaml deleted file mode 100644 index cadf938..0000000 --- a/config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -Hosts: - - Name: local - Endpoint: http://localhost:6336 - - Name: dapt.in - Endpoint: https://dashboard.dapt.in -CurrentContext: -AuthToken \ No newline at end of file diff --git a/generate_client_openapi.sh b/generate_client_openapi.sh deleted file mode 100644 index db77e48..0000000 --- a/generate_client_openapi.sh +++ /dev/null @@ -1,5 +0,0 @@ -go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest - -curl http://localhost:6336/openapi.yaml > daptin-openapi.yaml - -oapi-codegen --config=cfg.yaml client/daptin-openapi.yaml \ No newline at end of file diff --git a/main.go b/main.go deleted file mode 100644 index c59a7f3..0000000 --- a/main.go +++ /dev/null @@ -1,244 +0,0 @@ -package main - -import ( - "github.com/daptin/daptin-cli/src" - daptinClient "github.com/daptin/daptin-go-client" - "github.com/ghodss/yaml" - "github.com/urfave/cli/v2" - "io/ioutil" - "log" - "os" -) - -func main() { - - configFile, _ := os.UserHomeDir() - daptinCliConfig := src.DaptinCliConfig{} - - configFileEnv, ok := os.LookupEnv("DAPTIN_CLI_CONFIG") - if ok { - configFile = configFileEnv - } else { - dirPath := configFile + string(os.PathSeparator) + ".daptin" - if _, err := os.Stat(dirPath); err != nil { - err = os.Mkdir(dirPath, 0644) - } - configFile = dirPath + string(os.PathSeparator) + "config.yaml" - } - - if _, err := os.Stat(configFile); err == nil { - configFileBytes, _ := ioutil.ReadFile(configFile) - err = yaml.Unmarshal(configFileBytes, &daptinCliConfig) - } else { - _, _ = os.Create(configFile) - } - - for _, config := range daptinCliConfig.Hosts { - if config.Name == daptinCliConfig.CurrentContextName { - daptinCliConfig.Context = config - break - } - } - - appController := src.NewApplicationController(daptinCliConfig, configFile) - - app := &cli.App{ - Before: func(context *cli.Context) error { - if daptinCliConfig.CurrentContextName == "" { - daptinCliConfig.Context.Endpoint = context.String("endpoint") - } - if daptinCliConfig.CurrentContextName != "" { - var sH src.DaptinHostEndpoint - for _, h := range daptinCliConfig.Hosts { - if h.Name == daptinCliConfig.CurrentContextName { - sH = h - break - } - } - if sH.Name != "" { - daptinCliConfig.Context = sH - } - } - - var daptinClientInstance daptinClient.DaptinClient - - if daptinCliConfig.Context.Token == "" { - daptinClientInstance = daptinClient.NewDaptinClient(daptinCliConfig.Context.Endpoint, false) - } else { - daptinClientInstance = daptinClient.NewDaptinClientWithAuthToken(daptinCliConfig.Context.Endpoint, daptinCliConfig.Context.Token, false) - } - appController.SetDaptinClient(daptinClientInstance) - worlds, err := getAllItems("world", daptinClientInstance) - if err != nil { - return err - } - appController.SetWorlds(worlds) - - actions, err := getAllItems("action", daptinClientInstance) - if err != nil { - return err - } - appController.SetActions(actions) - outputRenderer := context.String("output") - switch outputRenderer { - case "table": - appController.SetRenderer(src.NewTableRenderer()) - case "json": - appController.SetRenderer(src.NewJsonRenderer()) - } - - return nil - }, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", - DefaultText: "~/.daptin/config.yaml", - EnvVars: []string{"DAPTIN_CLI_CONFIG"}, - }, - &cli.StringFlag{ - Name: "output, o", - Usage: "output format", - DefaultText: "table", - Value: "table", - EnvVars: []string{"DAPTIN_CLI_OUTPUT"}, - }, - &cli.StringFlag{ - Name: "endpoint", - Usage: "endpoint", - DefaultText: "http://localhost:6336", - Value: "http://localhost:6336", - EnvVars: []string{"DAPTIN_ENDPOINT"}, - }, - &cli.BoolFlag{ - Name: "debug, v", - Usage: "Print trace logsf", - }, - }, - Commands: []*cli.Command{ - { - Name: "set-context", - Aliases: []string{"sc"}, - Usage: "set the default context by name in config.yaml", - Action: appController.SetContext, - }, - { - Name: "signin", - Usage: "sign in", - Flags: []cli.Flag{}, - Action: appController.ActionSignIn, - }, - { - Name: "signin_with_2fa", - Usage: "Sign in with 2FA", - Flags: []cli.Flag{}, - Action: appController.ActionVerifyOtp, - }, - { - Name: "signup", - Usage: "sign up", - Flags: []cli.Flag{}, - Action: appController.ActionSignUp, - }, - { - Name: "describe", - Usage: "show schema", - Subcommands: []*cli.Command{ - { - Name: "table", - Action: appController.ActionShowWorldSchema, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "columns", - Usage: "comma separated column names to output", - Required: false, - Value: "", - }, - }, - }, - { - Name: "action", - Action: appController.ActionShowActionSchema, - Flags: []cli.Flag{}, - }, - }, - }, - { - Name: "list", - Usage: "list entity", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "filter", - Usage: "filter by keyword", - Required: false, - }, - &cli.StringFlag{ - Name: "columns", - Usage: "columns to print", - Required: false, - }, - &cli.IntFlag{ - Name: "pageSize", - Usage: "number of items per page", - Required: false, - Value: 10, - DefaultText: "10", - }, - &cli.IntFlag{ - Name: "pageNumber", - Usage: "page number", - Required: false, - Value: 0, - DefaultText: "0", - }, - }, - Action: appController.ActionListEntity, - }, - { - Name: "get-by-id", - Usage: "get table row by reference_id", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "reference_id", - Usage: "row reference_id to fetch", - Required: false, - }, - &cli.StringFlag{ - Name: "columns", - Usage: "columns to print", - Required: false, - }, - }, - Action: appController.ActionGetById, - }, - { - Name: "execute", - Usage: "execute an action", - Flags: []cli.Flag{}, - Action: appController.ActionExecute, - }, - }, - Version: "v0.0.1", - } - - cli.VersionFlag = &cli.BoolFlag{ - Name: "version", Aliases: []string{"v"}, - Usage: "print only the version", - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func getAllItems(entityName string, daptinClientInstance daptinClient.DaptinClient) ([]map[string]interface{}, error) { - allWorlds, err := daptinClientInstance.FindAll(entityName, daptinClient.DaptinQueryParameters{ - "page[size]": 500, - }) - if err != nil { - return nil, err - } - worlds := src.MapArray(allWorlds, "attributes") - return worlds, nil -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d9c0a4d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2508 @@ +{ + "name": "daptin-cli", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "daptin-cli", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "axios": "^1.7.0", + "chalk": "^4.1.2", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "inquirer": "^8.2.6", + "js-yaml": "^4.1.0" + }, + "bin": { + "daptin-cli": "dist/index.js" + }, + "devDependencies": { + "@types/inquirer": "^8.2.10", + "@types/js-yaml": "^4.0.9", + "@types/node": "^20.14.0", + "tsup": "^8.1.0", + "tsx": "^4.15.0", + "typescript": "^5.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/inquirer": { + "version": "8.2.12", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.12.tgz", + "integrity": "sha512-YxURZF2ZsSjU5TAe06tW0M3sL4UI9AMPA6dd8I72uOtppzNafcY38xkYgCZ/vsVOAyNdzHmvtTpLWilOrbP0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.32.tgz", + "integrity": "sha512-Ez8QE4DMfhjjTsES9K2dwfV258qBui7qxUsoaixZDiTzbde4U12e1pXGNu/ECsUIOi5/zoCxAQxIhQnaUQ2VvA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", + "license": "MIT", + "dependencies": { + "@inquirer/external-editor": "^1.0.0", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1cbbc66 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "daptin-cli", + "version": "0.1.0", + "description": "Command-line interface for Daptin Backend-as-a-Service", + "main": "dist/index.js", + "bin": { + "daptin-cli": "dist/index.js" + }, + "scripts": { + "build": "tsup src/index.ts --format cjs --clean --shims", + "dev": "tsx src/index.ts", + "start": "node dist/index.js", + "typecheck": "tsc --noEmit" + }, + "keywords": [ + "daptin", + "cli", + "baas", + "backend-as-a-service", + "json-api", + "rest-api" + ], + "author": "", + "license": "MIT", + "dependencies": { + "axios": "^1.7.0", + "chalk": "^4.1.2", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "inquirer": "^8.2.6", + "js-yaml": "^4.1.0" + }, + "devDependencies": { + "@types/inquirer": "^8.2.10", + "@types/js-yaml": "^4.0.9", + "@types/node": "^20.14.0", + "tsup": "^8.1.0", + "tsx": "^4.15.0", + "typescript": "^5.5.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 0000000..c36d97c --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,177 @@ +import axios, { AxiosInstance, AxiosError } from 'axios'; +import { + DaptinQueryParameters, + JsonApiListResponse, + JsonApiSingleResponse, + ActionResponse, + AggregateParams, +} from './types'; + +export class DaptinClient { + private endpoint: string; + private token: string | null; + private http: AxiosInstance; + private debug: boolean; + + constructor(endpoint: string, token?: string, debug: boolean = false) { + this.endpoint = endpoint.replace(/\/+$/, ''); + this.token = token || null; + this.debug = debug; + + this.http = axios.create({ + baseURL: this.endpoint, + headers: { + 'Content-Type': 'application/vnd.api+json', + }, + timeout: 30000, + }); + + this.http.interceptors.request.use((config) => { + if (this.token) { + config.headers['Authorization'] = `Bearer ${this.token}`; + } + return config; + }); + + if (debug) { + this.http.interceptors.request.use((config) => { + console.error(`[DEBUG] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`); + if (config.params) console.error(`[DEBUG] Params:`, config.params); + return config; + }); + this.http.interceptors.response.use( + (response) => { + console.error(`[DEBUG] Response ${response.status}`); + return response; + }, + (error: AxiosError) => { + console.error(`[DEBUG] Error ${error.response?.status || error.code}`); + return Promise.reject(error); + } + ); + } + } + + setToken(token: string): void { + this.token = token; + } + + getToken(): string | null { + return this.token; + } + + getEndpoint(): string { + return this.endpoint; + } + + // --- CRUD Operations --- + + async findAll(entity: string, params: DaptinQueryParameters = {}): Promise { + const response = await this.http.get(`/api/${entity}`, { params }); + return response.data; + } + + async findOne(entity: string, referenceId: string, params: DaptinQueryParameters = {}): Promise { + const response = await this.http.get(`/api/${entity}/${referenceId}`, { params }); + return response.data; + } + + async create(entity: string, attributes: Record): Promise { + const response = await this.http.post(`/api/${entity}`, { + data: { + type: entity, + attributes, + }, + }); + return response.data; + } + + async update( + entity: string, + referenceId: string, + attributes: Record, + relationships?: Record + ): Promise { + const data: any = { + type: entity, + id: referenceId, + attributes, + }; + if (relationships) { + data.relationships = relationships; + } + const response = await this.http.patch(`/api/${entity}/${referenceId}`, { data }); + return response.data; + } + + async deleteOne(entity: string, referenceId: string): Promise { + await this.http.delete(`/api/${entity}/${referenceId}`); + } + + // --- Actions --- + + async executeAction( + entity: string, + actionName: string, + data: Record = {} + ): Promise { + const response = await this.http.post(`/action/${entity}/${actionName}`, { + attributes: data, + }); + return response.data; + } + + async executeActionOnInstance( + entity: string, + referenceId: string, + actionName: string, + data: Record = {} + ): Promise { + const response = await this.http.post(`/action/${entity}/${referenceId}/${actionName}`, { + attributes: data, + }); + return response.data; + } + + // --- Aggregation --- + + async aggregate(entity: string, params: AggregateParams): Promise { + const response = await this.http.post(`/aggregate/${entity}`, params); + return response.data; + } + + // --- Relations --- + + async getRelation( + entity: string, + referenceId: string, + relation: string, + params: DaptinQueryParameters = {} + ): Promise { + const response = await this.http.get(`/api/${entity}/${referenceId}/${relation}`, { params }); + return response.data; + } + + // --- Meta / Discovery --- + + async getWorlds(params: DaptinQueryParameters = {}): Promise { + return this.findAll('world', { 'page[size]': 500, ...params }); + } + + async getActions(params: DaptinQueryParameters = {}): Promise { + return this.findAll('action', { 'page[size]': 500, ...params }); + } + + async getMeta(query?: string): Promise { + const url = query ? `/meta?query=${encodeURIComponent(query)}` : '/meta'; + const response = await this.http.get(url); + return response.data; + } + + async getHealth(): Promise { + const response = await this.http.get('/health'); + return response.data; + } +} + +export { DaptinClient as default }; diff --git a/src/client/types.ts b/src/client/types.ts new file mode 100644 index 0000000..0fa9d8f --- /dev/null +++ b/src/client/types.ts @@ -0,0 +1,54 @@ +export interface DaptinQueryParameters { + [key: string]: string | number | boolean | undefined; +} + +export interface JsonApiObject { + type: string; + id: string; + attributes: Record; + relationships?: Record; +} + +export interface JsonApiListResponse { + data: JsonApiObject[]; + links?: { + current_page: number; + from: number; + last_page: number; + per_page: number; + to: number; + total: number; + }; + included?: JsonApiObject[]; +} + +export interface JsonApiSingleResponse { + data: JsonApiObject; + included?: JsonApiObject[]; +} + +export interface ActionResponse { + ResponseType: string; + Attributes: Record; +} + +export interface AggregateParams { + column: string[]; + group?: string[]; + filter?: string[]; + having?: string[]; + sort?: string; + limit?: number; + join?: string; +} + +export interface DaptinHostConfig { + name: string; + endpoint: string; + token?: string; +} + +export interface DaptinCliConfig { + current_context?: string; + hosts: DaptinHostConfig[]; +} diff --git a/src/commands/action.ts b/src/commands/action.ts new file mode 100644 index 0000000..023252b --- /dev/null +++ b/src/commands/action.ts @@ -0,0 +1,214 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { getContext, handleError, extractAttributes, filterColumns } from '../context'; +import { ActionResponse } from '../client/types'; + +/** + * Process and display action responses from the server. + */ +function displayActionResponses(responses: ActionResponse[]): void { + if (!Array.isArray(responses)) { + console.log(chalk.yellow('No response from action.')); + return; + } + + for (const response of responses) { + switch (response.ResponseType) { + case 'client.notify': { + const msgType = response.Attributes.type || 'info'; + const msg = response.Attributes.message || ''; + if (msgType === 'error') { + console.error(chalk.red('Notification:'), msg); + } else if (msgType === 'warning') { + console.log(chalk.yellow('Notification:'), msg); + } else { + console.log(chalk.green('Notification:'), msg); + } + break; + } + case 'client.store.set': + console.log( + chalk.blue('Store:'), + `${response.Attributes.key} = ${String(response.Attributes.value).substring(0, 50)}...` + ); + break; + case 'client.redirect': + console.log(chalk.blue('Redirect:'), response.Attributes.location); + break; + case 'client.file.download': + console.log( + chalk.blue('File download:'), + response.Attributes.name || 'unnamed', + `(${response.Attributes.contentType || 'unknown type'})` + ); + // In CLI context, write to stdout or file + if (response.Attributes.content) { + const content = Buffer.from(response.Attributes.content, 'base64'); + process.stdout.write(content); + } + break; + default: + console.log(chalk.gray(`Response [${response.ResponseType}]:`), response.Attributes); + break; + } + } +} + +export function registerActionCommands(program: Command): void { + program + .command('actions [table]') + .description('List available actions, optionally filtered by table') + .option('--columns ', 'Comma-separated columns to display') + .action(async (table: string | undefined, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + const actionsResponse = await client.getActions(); + let actions = extractAttributes(actionsResponse.data); + + if (table) { + // Get the world reference_id for filtering + const worldsResponse = await client.getWorlds(); + const worlds = extractAttributes(worldsResponse.data); + const world = worlds.find((w) => w.table_name === table); + + if (!world) { + console.error(chalk.red(`Table "${table}" not found.`)); + process.exit(1); + } + + actions = actions.filter((a) => a.world_id === world.reference_id); + } + + if (actions.length === 0) { + console.log(chalk.yellow('No actions found.')); + return; + } + + const defaultCols = ['action_name', 'label', 'instance_optional', 'reference_id']; + const columns = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : defaultCols; + + actions = filterColumns(actions, columns); + actions.sort((a, b) => + (a.action_name || '').localeCompare(b.action_name || '') + ); + renderer.renderArray(actions, columns); + console.log(chalk.gray(`\n${actions.length} actions found.`)); + } catch (error) { + handleError(error); + } + }); + + program + .command('action-describe ') + .description('Show the schema of an action (input/output fields)') + .action(async (table: string, actionName: string, _options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + + // Find the world + const worldsResponse = await client.getWorlds(); + const worlds = extractAttributes(worldsResponse.data); + const world = worlds.find((w) => w.table_name === table); + + if (!world) { + console.error(chalk.red(`Table "${table}" not found.`)); + process.exit(1); + } + + // Find the action + const actionsResponse = await client.getActions(); + const allActions = extractAttributes(actionsResponse.data); + const action = allActions.find( + (a) => + a.world_id === world.reference_id && a.action_name === actionName + ); + + if (!action) { + console.error(chalk.red(`Action "${actionName}" not found on table "${table}".`)); + const tableActions = allActions + .filter((a) => a.world_id === world.reference_id) + .map((a) => a.action_name); + if (tableActions.length > 0) { + console.error(chalk.gray('Available actions:'), tableActions.join(', ')); + } + process.exit(1); + } + + // Parse action schema + let schema: any; + try { + schema = JSON.parse(action.action_schema); + } catch { + console.error(chalk.red('Could not parse action schema.')); + process.exit(1); + } + + console.log(chalk.cyan.bold(`\nAction: ${actionName}`)); + console.log(chalk.cyan(`Table: ${table}`)); + console.log(chalk.cyan(`Label: ${action.label || ''}`)); + + // Display input fields + const inFields = schema.InFields || []; + console.log(chalk.cyan(`\nInput Fields: ${inFields.length}`)); + if (inFields.length > 0) { + const fieldData = inFields.map((f: any) => ({ + Name: f.ColumnName || f.Name, + Type: f.ColumnType, + Nullable: f.IsNullable ? 'yes' : 'no', + })); + renderer.renderArray(fieldData); + } + + // Display output fields + const outFields = schema.OutFields || []; + console.log(chalk.cyan(`\nOutput Fields: ${outFields.length}`)); + if (outFields.length > 0) { + const outData = outFields.map((f: any, i: number) => ({ + Index: i, + Type: f.Type, + Attributes: JSON.stringify(f.Attributes || {}), + })); + renderer.renderArray(outData); + } + } catch (error) { + handleError(error); + } + }); + + program + .command('execute
') + .description('Execute an action on a table') + .option('--data ', 'JSON object with action parameters', '{}') + .option('--id ', 'Record reference_id for instance-level actions') + .action(async (table: string, actionName: string, options: any, cmd: Command) => { + try { + const { client } = getContext(cmd); + + let data: Record; + try { + data = JSON.parse(options.data); + } catch { + console.error(chalk.red('Invalid JSON in --data flag.')); + process.exit(1); + } + + let responses: ActionResponse[]; + if (options.id) { + responses = await client.executeActionOnInstance( + table, + options.id, + actionName, + data + ); + } else { + responses = await client.executeAction(table, actionName, data); + } + + displayActionResponses(responses); + } catch (error) { + handleError(error); + } + }); +} diff --git a/src/commands/aggregate.ts b/src/commands/aggregate.ts new file mode 100644 index 0000000..e36cd0f --- /dev/null +++ b/src/commands/aggregate.ts @@ -0,0 +1,66 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { getContext, handleError } from '../context'; +import { AggregateParams } from '../client/types'; + +export function registerAggregateCommands(program: Command): void { + program + .command('aggregate
') + .description('Run an aggregation query on a table') + .requiredOption('--columns ', 'Aggregation columns (comma-separated, e.g. "count,sum(price),avg(price)")') + .option('--group ', 'Group by columns (comma-separated)') + .option('--filter ', 'Filter conditions (comma-separated, e.g. "gt(price,100)")') + .option('--having ', 'Having conditions (comma-separated)') + .option('--sort ', 'Sort results') + .option('--limit ', 'Limit number of results') + .option('--join ', 'Join specification (e.g. "table@condition")') + .action(async (table: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + + const params: AggregateParams = { + column: options.columns.split(',').map((c: string) => c.trim()), + }; + + if (options.group) { + params.group = options.group.split(',').map((c: string) => c.trim()); + } + if (options.filter) { + params.filter = options.filter.split(',').map((c: string) => c.trim()); + } + if (options.having) { + params.having = options.having.split(',').map((c: string) => c.trim()); + } + if (options.sort) { + params.sort = options.sort; + } + if (options.limit) { + params.limit = parseInt(options.limit, 10); + } + if (options.join) { + params.join = options.join; + } + + const result = await client.aggregate(table, params); + + if (Array.isArray(result)) { + if (result.length === 0) { + console.log(chalk.yellow('No aggregation results.')); + return; + } + renderer.renderArray(result); + } else if (typeof result === 'object' && result !== null) { + // Single result object + if (result.data && Array.isArray(result.data)) { + renderer.renderArray(result.data); + } else { + renderer.renderObject(result); + } + } else { + console.log(result); + } + } catch (error) { + handleError(error); + } + }); +} diff --git a/src/commands/auth.ts b/src/commands/auth.ts new file mode 100644 index 0000000..511cea5 --- /dev/null +++ b/src/commands/auth.ts @@ -0,0 +1,190 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import { getContext, handleError } from '../context'; +import { ActionResponse } from '../client/types'; +import { ConfigManager } from '../config'; + +/** + * Process action responses from signin/signup. + * Handles client.store.set (saves token), client.notify, etc. + */ +function handleAuthResponse( + responses: ActionResponse[], + configManager: ConfigManager, + contextName?: string +): void { + for (const response of responses) { + switch (response.ResponseType) { + case 'client.store.set': + if (response.Attributes.key === 'token') { + const token = response.Attributes.value; + if (contextName) { + configManager.updateHostToken(contextName, token); + } + console.log(chalk.green('Authentication successful. Token saved.')); + } + break; + case 'client.notify': + const msgType = response.Attributes.type || 'info'; + const msg = response.Attributes.message || ''; + if (msgType === 'error') { + console.error(chalk.red('Server:'), msg); + } else { + console.log(chalk.blue('Server:'), msg); + } + break; + case 'client.redirect': + console.log(chalk.gray(`Redirect: ${response.Attributes.location}`)); + break; + default: + break; + } + } +} + +export function registerAuthCommands(program: Command): void { + program + .command('signup ') + .description('Create a new account on the Daptin server') + .action(async (email: string, _options: any, cmd: Command) => { + try { + const { client, configManager } = getContext(cmd); + const answers = await inquirer.prompt([ + { + type: 'password', + name: 'password', + message: 'Password:', + mask: '*', + }, + { + type: 'password', + name: 'passwordConfirm', + message: 'Confirm password:', + mask: '*', + }, + ]); + + if (answers.password !== answers.passwordConfirm) { + console.error(chalk.red('Passwords do not match.')); + process.exit(1); + } + + const responses = await client.executeAction('user_account', 'signup', { + email, + password: answers.password, + passwordConfirm: answers.passwordConfirm, + }); + + const currentCtx = configManager.getCurrentContext(); + handleAuthResponse(responses, configManager, currentCtx?.name || currentCtx?.endpoint); + } catch (error) { + handleError(error); + } + }); + + program + .command('signin ') + .description('Sign in to the Daptin server') + .action(async (email: string, _options: any, cmd: Command) => { + try { + const { client, configManager } = getContext(cmd); + const answers = await inquirer.prompt([ + { + type: 'password', + name: 'password', + message: 'Password:', + mask: '*', + }, + ]); + + const responses = await client.executeAction('user_account', 'signin', { + email, + password: answers.password, + }); + + const currentCtx = configManager.getCurrentContext(); + handleAuthResponse(responses, configManager, currentCtx?.name || currentCtx?.endpoint); + } catch (error) { + handleError(error); + } + }); + + program + .command('signin-2fa ') + .description('Sign in with two-factor authentication') + .action(async (email: string, _options: any, cmd: Command) => { + try { + const { client, configManager } = getContext(cmd); + const answers = await inquirer.prompt([ + { + type: 'password', + name: 'password', + message: 'Password:', + mask: '*', + }, + { + type: 'input', + name: 'otp', + message: 'OTP code:', + }, + ]); + + const responses = await client.executeAction('user_account', 'signin_with_2fa', { + email, + password: answers.password, + otp: answers.otp, + }); + + const currentCtx = configManager.getCurrentContext(); + handleAuthResponse(responses, configManager, currentCtx?.name || currentCtx?.endpoint); + } catch (error) { + handleError(error); + } + }); + + program + .command('whoami') + .description('Show current authentication status') + .action(async (_options: any, cmd: Command) => { + try { + const { client, configManager } = getContext(cmd); + const currentCtx = configManager.getCurrentContext(); + + console.log(chalk.cyan('Server:'), client.getEndpoint()); + + const token = client.getToken(); + if (!token) { + console.log(chalk.yellow('Not authenticated.')); + return; + } + + // Decode JWT payload (base64 middle segment) + try { + const parts = token.split('.'); + if (parts.length === 3) { + const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8')); + console.log(chalk.cyan('Email:'), payload.email || 'N/A'); + console.log(chalk.cyan('User ID:'), payload.sub || payload.id || 'N/A'); + if (payload.exp) { + const expDate = new Date(payload.exp * 1000); + const isExpired = expDate < new Date(); + console.log( + chalk.cyan('Expires:'), + expDate.toISOString(), + isExpired ? chalk.red('(expired)') : chalk.green('(valid)') + ); + } + } + } catch { + console.log(chalk.yellow('Token present but could not decode claims.')); + } + + if (currentCtx) { + console.log(chalk.cyan('Context:'), currentCtx.name); + } + } catch (error) { + handleError(error); + } + }); +} diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..676e3d6 --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,101 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { ConfigManager } from '../config'; +import { createRenderer } from '../output'; + +export function registerConfigCommands(program: Command): void { + const config = program + .command('config') + .description('Manage server configurations'); + + config + .command('add ') + .description('Add a new server configuration') + .action((name: string, endpoint: string, _options: any, cmd: Command) => { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + configManager.addHost({ name, endpoint: endpoint.replace(/\/+$/, '') }); + console.log(chalk.green(`Added server "${name}" (${endpoint})`)); + if (configManager.getConfig().current_context === name) { + console.log(chalk.gray(`Set as active context.`)); + } + }); + + config + .command('use ') + .description('Switch the active server context') + .action((name: string, _options: any, cmd: Command) => { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + if (configManager.setCurrentContext(name)) { + console.log(chalk.green(`Switched to context "${name}".`)); + } else { + console.error(chalk.red(`Context "${name}" not found.`)); + const hosts = configManager.getConfig().hosts; + if (hosts.length > 0) { + console.error('Available contexts:', hosts.map((h) => h.name).join(', ')); + } + process.exit(1); + } + }); + + config + .command('list') + .description('List all server configurations') + .action((_options: any, cmd: Command) => { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + const cfg = configManager.getConfig(); + const renderer = createRenderer(opts.output || 'table'); + + if (cfg.hosts.length === 0) { + console.log(chalk.yellow('No servers configured.')); + console.log('Use "daptin-cli config add " to add one.'); + return; + } + + const data = cfg.hosts.map((h) => ({ + name: h.name, + endpoint: h.endpoint, + active: h.name === cfg.current_context ? '*' : '', + authenticated: h.token ? 'yes' : 'no', + })); + + renderer.renderArray(data); + }); + + config + .command('remove ') + .description('Remove a server configuration') + .action((name: string, _options: any, cmd: Command) => { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + if (configManager.removeHost(name)) { + console.log(chalk.green(`Removed context "${name}".`)); + } else { + console.error(chalk.red(`Context "${name}" not found.`)); + process.exit(1); + } + }); + + config + .command('show') + .description('Show the current active configuration') + .action((_options: any, cmd: Command) => { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + const current = configManager.getCurrentContext(); + + if (!current) { + console.log(chalk.yellow('No active context set.')); + console.log('Use "daptin-cli config add " to add a server.'); + return; + } + + console.log(chalk.cyan('Active context:')); + console.log(` Name: ${current.name}`); + console.log(` Endpoint: ${current.endpoint}`); + console.log(` Auth: ${current.token ? 'authenticated' : 'not authenticated'}`); + console.log(` Config: ${configManager.getConfigPath()}`); + }); +} diff --git a/src/commands/crud.ts b/src/commands/crud.ts new file mode 100644 index 0000000..361751b --- /dev/null +++ b/src/commands/crud.ts @@ -0,0 +1,208 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { getContext, handleError, extractAttributes, filterColumns } from '../context'; +import { DaptinQueryParameters } from '../client/types'; + +export function registerCrudCommands(program: Command): void { + // --- LIST --- + program + .command('list
') + .description('List records from a table') + .option('--page-size ', 'Records per page', '10') + .option('--page ', 'Page number (1-indexed)', '1') + .option('--filter ', 'Filter conditions (JSON array)') + .option('--sort ', 'Sort fields (prefix with - for desc)') + .option('--columns ', 'Comma-separated columns to display') + .option('--include ', 'Include relationships') + .option('--fields ', 'Fields to request from server') + .action(async (table: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + const params: DaptinQueryParameters = { + 'page[size]': parseInt(options.pageSize, 10), + 'page[number]': parseInt(options.page, 10), + }; + + if (options.filter) params['query'] = options.filter; + if (options.sort) params['sort'] = options.sort; + if (options.include) params['included_relations'] = options.include; + if (options.fields) params['fields'] = options.fields; + + const response = await client.findAll(table, params); + let data = extractAttributes(response.data); + + if (data.length === 0) { + console.log(chalk.yellow('No records found.')); + return; + } + + const columns = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : undefined; + + if (columns) { + data = filterColumns(data, columns); + } + + renderer.renderArray(data, columns); + + // Show pagination info + if (response.links) { + const links = response.links as any; + const total = links.total || data.length; + const currentPage = links.current_page || parseInt(options.page, 10); + const lastPage = links.last_page || 1; + console.log( + chalk.gray(`\nPage ${currentPage}/${lastPage} (${total} total records)`) + ); + } + } catch (error) { + handleError(error); + } + }); + + // --- GET --- + program + .command('get
') + .description('Get a single record by reference_id') + .option('--columns ', 'Comma-separated columns to display') + .option('--include ', 'Include relationships') + .action(async (table: string, referenceId: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + const params: DaptinQueryParameters = {}; + + if (options.include) params['included_relations'] = options.include; + + const response = await client.findOne(table, referenceId, params); + + if (!response.data) { + console.log(chalk.yellow('Record not found.')); + return; + } + + const attrs = response.data.attributes || {}; + const columns = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : undefined; + + renderer.renderObject(attrs, columns); + } catch (error) { + handleError(error); + } + }); + + // --- CREATE --- + program + .command('create
') + .description('Create a new record') + .requiredOption('--data ', 'JSON object with record attributes') + .action(async (table: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + let attributes: Record; + + try { + attributes = JSON.parse(options.data); + } catch { + console.error(chalk.red('Invalid JSON in --data flag.')); + process.exit(1); + } + + const response = await client.create(table, attributes); + const attrs = response.data?.attributes || {}; + + console.log(chalk.green('Record created successfully.')); + renderer.renderObject(attrs); + } catch (error) { + handleError(error); + } + }); + + // --- UPDATE --- + program + .command('update
') + .description('Update an existing record') + .requiredOption('--data ', 'JSON object with attributes to update') + .action(async (table: string, referenceId: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + let attributes: Record; + + try { + attributes = JSON.parse(options.data); + } catch { + console.error(chalk.red('Invalid JSON in --data flag.')); + process.exit(1); + } + + const response = await client.update(table, referenceId, attributes); + const attrs = response.data?.attributes || {}; + + console.log(chalk.green('Record updated successfully.')); + renderer.renderObject(attrs); + } catch (error) { + handleError(error); + } + }); + + // --- DELETE --- + program + .command('delete
') + .description('Delete a record by reference_id') + .action(async (table: string, referenceId: string, _options: any, cmd: Command) => { + try { + const { client } = getContext(cmd); + await client.deleteOne(table, referenceId); + console.log(chalk.green(`Record ${referenceId} deleted from "${table}".`)); + } catch (error) { + handleError(error); + } + }); + + // --- RELATION --- + program + .command('relation
') + .description('Query related records through a relationship') + .option('--page-size ', 'Records per page', '10') + .option('--page ', 'Page number', '1') + .option('--columns ', 'Comma-separated columns to display') + .action( + async ( + table: string, + referenceId: string, + relation: string, + options: any, + cmd: Command + ) => { + try { + const { client, renderer } = getContext(cmd); + const params: DaptinQueryParameters = { + 'page[size]': parseInt(options.pageSize, 10), + 'page[number]': parseInt(options.page, 10), + }; + + const response = await client.getRelation(table, referenceId, relation, params); + + if (Array.isArray(response.data)) { + const data = extractAttributes(response.data); + if (data.length === 0) { + console.log(chalk.yellow('No related records found.')); + return; + } + const columns = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : undefined; + renderer.renderArray(columns ? filterColumns(data, columns) : data, columns); + } else if (response.data) { + const attrs = (response.data as any).attributes || {}; + renderer.renderObject(attrs); + } else { + console.log(chalk.yellow('No related records found.')); + } + } catch (error) { + handleError(error); + } + } + ); +} diff --git a/src/commands/describe.ts b/src/commands/describe.ts new file mode 100644 index 0000000..66bc33b --- /dev/null +++ b/src/commands/describe.ts @@ -0,0 +1,132 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { getContext, handleError, extractAttributes, filterColumns } from '../context'; + +export function registerDescribeCommands(program: Command): void { + program + .command('tables') + .description('List all tables on the server') + .option('--columns ', 'Comma-separated columns to display') + .action(async (options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + const response = await client.getWorlds(); + let data = extractAttributes(response.data); + + const defaultColumns = ['table_name', 'is_top_level', 'is_hidden']; + const columns = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : defaultColumns; + + data = filterColumns(data, columns); + + // Sort by table_name for readability + data.sort((a, b) => (a.table_name || '').localeCompare(b.table_name || '')); + + renderer.renderArray(data, columns); + console.log(chalk.gray(`\n${data.length} tables found.`)); + } catch (error) { + handleError(error); + } + }); + + program + .command('describe
') + .description('Show table schema (columns and actions)') + .option('--columns ', 'Comma-separated column fields to display') + .action(async (tableName: string, options: any, cmd: Command) => { + try { + const { client, renderer } = getContext(cmd); + + // Fetch all worlds to find the target table + const worldsResponse = await client.getWorlds(); + const worlds = extractAttributes(worldsResponse.data); + const world = worlds.find((w) => w.table_name === tableName); + + if (!world) { + console.error(chalk.red(`Table "${tableName}" not found.`)); + const available = worlds + .map((w) => w.table_name) + .filter(Boolean) + .sort(); + if (available.length > 0) { + console.error(chalk.gray('Available tables:'), available.join(', ')); + } + process.exit(1); + } + + // Parse the world schema + let schema: any; + try { + schema = JSON.parse(world.world_schema_json); + } catch { + console.error(chalk.red('Could not parse table schema.')); + process.exit(1); + } + + // Display columns + console.log(chalk.cyan.bold(`\nTable: ${tableName}`)); + console.log(chalk.cyan('Columns:')); + + const columnData = (schema.Columns || []).map((col: any) => ({ + ColumnName: col.ColumnName || col.Name, + ColumnType: col.ColumnType, + DataType: col.DataType || '', + IsNullable: col.IsNullable ? 'yes' : 'no', + IsUnique: col.IsUnique ? 'yes' : 'no', + IsIndexed: col.IsIndexed ? 'yes' : 'no', + IsForeignKey: col.IsForeignKey ? 'yes' : 'no', + })); + + const colDisplayCols = options.columns + ? options.columns.split(',').map((c: string) => c.trim()) + : ['ColumnName', 'ColumnType', 'IsNullable']; + + renderer.renderArray(filterColumns(columnData, colDisplayCols), colDisplayCols); + + // Display relations if any + if (schema.Relations && schema.Relations.length > 0) { + console.log(chalk.cyan(`\nRelations: ${schema.Relations.length}`)); + const relData = schema.Relations.map((rel: any) => ({ + Subject: rel.Subject || rel.SubjectName, + Relation: rel.Relation, + Object: rel.Object || rel.ObjectName, + })); + renderer.renderArray(relData); + } + + // Display associated actions + const actionsResponse = await client.getActions(); + const allActions = extractAttributes(actionsResponse.data); + const worldActions = allActions.filter( + (a) => a.world_id === world.reference_id + ); + + if (worldActions.length > 0) { + console.log(chalk.cyan(`\nActions: ${worldActions.length}`)); + const actionData = filterColumns(worldActions, [ + 'action_name', + 'label', + 'reference_id', + ]); + renderer.renderArray(actionData, ['action_name', 'label', 'reference_id']); + } else { + console.log(chalk.gray('\nNo actions associated with this table.')); + } + + // Display state machines if enabled + if (schema.StateMachines && schema.StateMachines.length > 0) { + console.log(chalk.cyan(`\nState Machines: ${schema.StateMachines.length}`)); + const smData = schema.StateMachines.map((sm: any) => ({ + Name: sm.Name, + Label: sm.Label || '', + InitialState: sm.InitialState, + Events: (sm.Events || []).length, + })); + renderer.renderArray(smData); + } + } catch (error) { + handleError(error); + } + }); +} diff --git a/src/config.go b/src/config.go deleted file mode 100644 index f9da2bb..0000000 --- a/src/config.go +++ /dev/null @@ -1,13 +0,0 @@ -package src - -type DaptinHostEndpoint struct { - Name string - Endpoint string - Token string -} - -type DaptinCliConfig struct { - CurrentContextName string - Context DaptinHostEndpoint `json:"-"` - Hosts []DaptinHostEndpoint -} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..d79733c --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,114 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as yaml from 'js-yaml'; +import { DaptinCliConfig, DaptinHostConfig } from '../client/types'; + +export class ConfigManager { + private configPath: string; + private config: DaptinCliConfig; + + constructor(configPath?: string) { + this.configPath = configPath || ConfigManager.getDefaultConfigPath(); + this.config = this.load(); + } + + static getDefaultConfigPath(): string { + const envPath = process.env.DAPTIN_CLI_CONFIG; + if (envPath) return envPath; + + const daptinDir = path.join(os.homedir(), '.daptin'); + if (!fs.existsSync(daptinDir)) { + fs.mkdirSync(daptinDir, { recursive: true }); + } + return path.join(daptinDir, 'config.yaml'); + } + + private load(): DaptinCliConfig { + try { + if (fs.existsSync(this.configPath)) { + const content = fs.readFileSync(this.configPath, 'utf-8'); + const parsed = yaml.load(content) as any; + if (!parsed) return { hosts: [] }; + + // Support both old Go config format (PascalCase) and new format (snake_case) + return { + current_context: parsed.current_context || parsed.CurrentContext || undefined, + hosts: (parsed.hosts || parsed.Hosts || []).map((h: any) => ({ + name: h.name || h.Name, + endpoint: h.endpoint || h.Endpoint, + token: h.token || h.Token || undefined, + })), + }; + } + } catch { + // Config file doesn't exist or is invalid, start fresh + } + return { hosts: [] }; + } + + save(): void { + const content = yaml.dump(this.config, { lineWidth: -1 }); + const dir = path.dirname(this.configPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(this.configPath, content, 'utf-8'); + } + + getConfig(): DaptinCliConfig { + return this.config; + } + + getCurrentContext(): DaptinHostConfig | undefined { + if (!this.config.current_context) return undefined; + return this.config.hosts.find((h) => h.name === this.config.current_context); + } + + addHost(host: DaptinHostConfig): void { + const existingIdx = this.config.hosts.findIndex((h) => h.name === host.name); + if (existingIdx >= 0) { + this.config.hosts[existingIdx] = host; + } else { + this.config.hosts.push(host); + } + if (!this.config.current_context) { + this.config.current_context = host.name; + } + this.save(); + } + + removeHost(name: string): boolean { + const index = this.config.hosts.findIndex((h) => h.name === name); + if (index < 0) return false; + this.config.hosts.splice(index, 1); + if (this.config.current_context === name) { + this.config.current_context = + this.config.hosts.length > 0 ? this.config.hosts[0].name : undefined; + } + this.save(); + return true; + } + + setCurrentContext(name: string): boolean { + const host = this.config.hosts.find((h) => h.name === name); + if (!host) return false; + this.config.current_context = name; + this.save(); + return true; + } + + updateHostToken(nameOrEndpoint: string, token: string): void { + const host = this.config.hosts.find( + (h) => h.name === nameOrEndpoint || h.endpoint === nameOrEndpoint + ); + if (host) { + host.token = token; + this.save(); + } + } + + getConfigPath(): string { + return this.configPath; + } +} diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..d16ac7f --- /dev/null +++ b/src/context.ts @@ -0,0 +1,91 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import { DaptinClient } from './client'; +import { ConfigManager } from './config'; +import { createRenderer, Renderer } from './output'; + +export interface CliContext { + client: DaptinClient; + configManager: ConfigManager; + renderer: Renderer; +} + +/** + * Build a CLI context from a command's options. + * Creates the DaptinClient, ConfigManager, and Renderer based on CLI flags and config file. + */ +export function getContext(cmd: Command): CliContext { + const opts = cmd.optsWithGlobals(); + const configManager = new ConfigManager(opts.config); + const renderer = createRenderer(opts.output || 'table'); + + const context = configManager.getCurrentContext(); + const endpoint = opts.endpoint || context?.endpoint; + const token = opts.token || context?.token; + + if (!endpoint) { + console.error(chalk.red('Error: No endpoint configured.')); + console.error('Use "daptin-cli config add " to add a server,'); + console.error('or pass --endpoint on the command line.'); + process.exit(1); + } + + const client = new DaptinClient(endpoint, token, opts.debug); + return { client, configManager, renderer }; +} + +/** + * Standard error handler for CLI commands. + * Formats axios errors and other exceptions into user-friendly messages. + */ +export function handleError(error: any): void { + if (error.response) { + const status = error.response.status; + const data = error.response.data; + const message = + typeof data === 'string' + ? data + : data?.message || data?.errors?.[0]?.title || JSON.stringify(data); + console.error(chalk.red(`Error ${status}:`), message); + } else if (error.code === 'ECONNREFUSED') { + console.error( + chalk.red('Connection refused.'), + 'Is the Daptin server running at the configured endpoint?' + ); + } else if (error.code === 'ENOTFOUND') { + console.error(chalk.red('Host not found.'), 'Check the endpoint URL.'); + } else if (error.code === 'ETIMEDOUT') { + console.error(chalk.red('Connection timed out.')); + } else if (error.message) { + console.error(chalk.red('Error:'), error.message); + } else { + console.error(chalk.red('Unknown error occurred.')); + } + process.exit(1); +} + +/** + * Extract attributes from JSON:API response objects. + */ +export function extractAttributes( + objects: Array<{ attributes?: Record }> +): Record[] { + return objects.map((obj) => obj.attributes || {}); +} + +/** + * Filter columns from a data array. + */ +export function filterColumns( + data: Record[], + columns: string[] +): Record[] { + if (!columns || columns.length === 0) return data; + return data.map((row) => { + const filtered: Record = {}; + for (const col of columns) { + if (col in row) filtered[col] = row[col]; + } + return filtered; + }); +} diff --git a/src/controller_actions.go b/src/controller_actions.go deleted file mode 100644 index 7174e43..0000000 --- a/src/controller_actions.go +++ /dev/null @@ -1,506 +0,0 @@ -package src - -import ( - "encoding/json" - "errors" - "fmt" - daptinClient "github.com/daptin/daptin-go-client" - "github.com/ghodss/yaml" - "github.com/urfave/cli/v2" - "io/ioutil" - "log" - "os" - "strings" -) - -type ApplicationController struct { - daptinClient daptinClient.DaptinClient - daptinCliConfig DaptinCliConfig - configPath string - worlds map[string]map[string]interface{} - renderer Renderer - actions map[string]map[string]interface{} -} - -func (c *ApplicationController) WriteConfig() { - yamlStr, err := yaml.Marshal(c.daptinCliConfig) - if err != nil { - log.Printf("Failed to marshal json to save config: %v", err) - return - } - err = ioutil.WriteFile(c.configPath, yamlStr, 0644) - if err != nil { - log.Printf("Failed to write config: %v", err) - } -} - -func (c *ApplicationController) SetContext(context *cli.Context) error { - - for _, host := range c.daptinCliConfig.Hosts { - if host.Name == context.String("name") { - c.daptinCliConfig.CurrentContextName = host.Name - c.WriteConfig() - return nil - } - } - - return errors.New(fmt.Sprintf("invalid name [%v], not found in config", context.String("name"))) -} - -func (c *ApplicationController) ActionSignUp(context *cli.Context) error { - - responses, err := c.daptinClient.Execute("signup", "user_account", map[string]interface{}{ - "email": context.Args().Get(0), - "password": context.Args().Get(1), - "passwordConfirm": context.Args().Get(2), - }) - if err != nil { - return err - } - - return c.HandleActionResponse(responses) - -} - -func (c *ApplicationController) ActionSignIn(context *cli.Context) error { - - responses, err := c.daptinClient.Execute("signin", "user_account", map[string]interface{}{ - "email": context.Args().Get(0), - "password": context.Args().Get(1), - }) - if err != nil { - return err - } - - return c.HandleActionResponse(responses) -} - -func (c *ApplicationController) ActionGetById(context *cli.Context) error { - - entityName := context.Args().Get(0) - referenceId := context.Args().Get(1) - params := daptinClient.DaptinQueryParameters{} - if len(referenceId) == 0 { - return fmt.Errorf("invalid reference_id") - } - - columnsFromArgs := context.String("columns") - - colNames := make([]string, 0) - if len(columnsFromArgs) > 0 { - cols := strings.Split(columnsFromArgs, ",") - for _, col := range cols { - colNames = append(colNames, col) - } - } - - result, err := c.daptinClient.FindOne(entityName, referenceId, params) - if err != nil { - return err - } - - if len(result) == 0 { - fmt.Println("No entities found") - return nil - } - - resultSet := result["attributes"].(map[string]interface{}) - - if len(colNames) > 0 { - resultSet = IncludeColumnFromMap(resultSet, colNames) - } - return c.renderer.RenderObject(resultSet) -} - -func (c *ApplicationController) ActionListEntity(context *cli.Context) error { - - entityName := context.Args().Get(0) - query := context.String("query") - params := daptinClient.DaptinQueryParameters{} - if len(query) > 0 { - params["query"] = query - } - params["page[size]"] = context.Int("pageSize") - columnsFromArgs := context.String("columns") - - colNames := make([]string, 0) - if len(columnsFromArgs) > 0 { - cols := strings.Split(columnsFromArgs, ",") - for _, col := range cols { - colNames = append(colNames, col) - } - } - - result, err := c.daptinClient.FindAll(entityName, params) - if err != nil { - return err - } - - if len(result) == 0 { - fmt.Println("No entities found") - return nil - } - - resultSet := MapArray(result, "attributes") - - if len(colNames) > 0 { - resultSet = FilterColumn(resultSet, colNames) - } - return c.renderer.RenderArray(resultSet) -} - -func FilterColumn(array []map[string]interface{}, includedColumnNames []string) []map[string]interface{} { - for i, row := range array { - array[i] = IncludeColumnFromMap(row, includedColumnNames) - } - return array -} - -func IncludeColumnFromMap(row map[string]interface{}, includedColumnNames []string) map[string]interface{} { - for colName, _ := range row { - found := false - for _, includedName := range includedColumnNames { - if colName == includedName { - found = true - } - } - if !found { - delete(row, colName) - } - } - return row -} - -func ExcludeColumnFromMap(row map[string]interface{}, excludedColumnNames []string) map[string]interface{} { - for colName, _ := range row { - found := false - for _, includedName := range excludedColumnNames { - if colName == includedName { - found = true - } - } - if found { - delete(row, colName) - } - } - return row -} - -func (c *ApplicationController) ActionVerifyOtp(context *cli.Context) error { - - responses, err := c.daptinClient.Execute("signin_with_2fa", "user_account", map[string]interface{}{ - "email": context.Args().Get(0), - "password": context.Args().Get(1), - "otp": context.Args().Get(2), - }) - if err != nil { - return err - } - - return c.HandleActionResponse(responses) -} - -func (c *ApplicationController) ExecuteAction(context *cli.Context) error { - - //actionName := context.String("name") - - responses, err := c.daptinClient.Execute("signin_with_2fa", "user_account", map[string]interface{}{ - "email": context.Args().Get(0), - "password": context.Args().Get(1), - "otp": context.Args().Get(2), - }) - if err != nil { - return err - } - - return c.HandleActionResponse(responses) -} - -func (c *ApplicationController) HandleActionResponse(responses []daptinClient.DaptinActionResponse) error { - for _, response := range responses { - //log.Printf("Action Response: %v", response) - switch response.ResponseType { - case "client.store.set": - keyName := response.Attributes["key"] - if keyName == "token" { - c.daptinCliConfig.Context.Token = response.Attributes["value"].(string) - - hostPresent := false - - for i, h := range c.daptinCliConfig.Hosts { - if h.Endpoint == c.daptinCliConfig.Context.Endpoint || h.Name == c.daptinCliConfig.Context.Name { - hostPresent = true - h.Token = c.daptinCliConfig.Context.Token - c.daptinCliConfig.Hosts[i] = h - c.daptinCliConfig.CurrentContextName = h.Name - } - } - if !hostPresent { - c.daptinCliConfig.Context.Name = c.daptinCliConfig.Context.Endpoint - c.daptinCliConfig.Context.Token = c.daptinCliConfig.Context.Token - c.daptinCliConfig.Hosts = append(c.daptinCliConfig.Hosts, c.daptinCliConfig.Context) - } - c.daptinCliConfig.CurrentContextName = c.daptinCliConfig.Context.Name - c.WriteConfig() - } - case "client.cookie.set": - case "client.notify": - log.Printf("Notice: %s", response.Attributes["message"]) - case "client.redirect": - } - } - return nil -} - -type AuthPermission int64 - -type LoopbookFsmDescription struct { - InitialState string - Name string - Label string - Events []LoopbackEventDesc -} - -type LoopbackEventDesc struct { - // Name is the event name used when calling for a transition. - Name string - Label string - Color string - - // Src is a slice of source states that the FSM must be in to perform a - // state transition. - Src []string - - // Dst is the destination state that the FSM will be in if the transition - // succeeds. - Dst string -} - -type ColumnTag struct { - ColumnName string - Tags string -} - -type ForeignKeyData struct { - DataSource string - Namespace string - KeyName string -} - -type ColumnInfo struct { - Name string `db:"name"` - ColumnName string `db:"column_name"` - ColumnDescription string `db:"column_description"` - ColumnType string `db:"column_type"` - IsPrimaryKey bool `db:"is_primary_key"` - IsAutoIncrement bool `db:"is_auto_increment"` - IsIndexed bool `db:"is_indexed"` - IsUnique bool `db:"is_unique"` - IsNullable bool `db:"is_nullable"` - Permission uint64 `db:"permission"` - IsForeignKey bool `db:"is_foreign_key"` - ExcludeFromApi bool `db:"include_in_api"` - ForeignKeyData ForeignKeyData `db:"foreign_key_data"` - DataType string `db:"data_type"` - DefaultValue string `db:"default_value"` - Options []ValueOptions -} -type ValueOptions struct { - ValueType string - Value interface{} - Label string -} - -type TableRelation struct { - Subject string - Object string - Relation string - SubjectName string - ObjectName string - Columns []ColumnInfo -} - -type TableInfo struct { - TableName string `db:"table_name"` - TableId int - DefaultPermission AuthPermission `db:"default_permission"` - Columns []ColumnInfo - StateMachines []LoopbookFsmDescription - Relations []TableRelation - IsTopLevel bool `db:"is_top_level"` - Permission AuthPermission - UserId uint64 `db:"user_account_id"` - IsHidden bool `db:"is_hidden"` - IsJoinTable bool `db:"is_join_table"` - IsStateTrackingEnabled bool `db:"is_state_tracking_enabled"` - IsAuditEnabled bool `db:"is_audit_enabled"` - TranslationsEnabled bool `db:"translation_enabled"` - DefaultGroups []string `db:"default_groups"` - DefaultRelations map[string][]string `db:"default_relations"` - Validations []ColumnTag - Conformations []ColumnTag - DefaultOrder string - Icon string - CompositeKeys [][]string -} - -func (c *ApplicationController) ActionShowWorldSchema(context *cli.Context) (err error) { - entityName := context.Args().Get(0) - - world, ok := c.worlds[entityName] - if !ok { - err := fmt.Errorf("entity [%s] not found", entityName) - return err - } - - schemaJson := world["world_schema_json"].(string) - //var worldSchemaStruct TableInfo - var mapHolder map[string]interface{} - //err := json.Unmarshal([]byte(schemaJson), &worldSchemaStruct) - err = json.Unmarshal([]byte(schemaJson), &mapHolder) - if err != nil { - return err - } - - data := mapHolder["Columns"].([]interface{}) - dataMap := make([]map[string]interface{}, len(data)) - for i, row := range data { - dataMap[i] = row.(map[string]interface{}) - } - - columns := context.String("columns") - - if len(columns) == 0 { - dataMap = FilterColumn(dataMap, []string{"ColumnName", "ColumnType"}) - } else { - columList := strings.Split(columns, ",") - dataMap = FilterColumn(dataMap, columList) - } - err = c.renderer.RenderArray(dataMap) - worldActions := make([]map[string]interface{}, 0) - for _, action := range c.actions { - if action["world_id"].(string) == world["reference_id"] { - worldActions = append(worldActions, action) - } - } - _, err = fmt.Fprintf(os.Stdout, "\nActions: %d\n", len(worldActions)) - if len(worldActions) > 0 { - worldActions = FilterColumn(worldActions, []string{"action_name", "label", "reference_id"}) - err = c.renderer.RenderArray(worldActions) - } - - return err -} - -func (c *ApplicationController) ActionShowActionSchema(context *cli.Context) error { - worldName := context.Args().Get(0) - actionName := context.Args().Get(1) - world, err := c.GetWorldByName(worldName) - if err != nil { - return err - } - action, err := c.GetAction(world["reference_id"].(string), actionName) - if err != nil { - return err - } - var actionSchema map[string]interface{} - err = json.Unmarshal([]byte(action["action_schema"].(string)), &actionSchema) - if err != nil { - return err - } - inFields := actionSchema["InFields"].([]interface{}) - fmt.Printf("InFields: %v\n", len(inFields)) - for _, inField := range inFields { - inFieldMap := inField.(map[string]interface{}) - fmt.Printf("\t%s: %s\n", inFieldMap["ColumnName"], inFieldMap["ColumnType"]) - } - - outFields := actionSchema["OutFields"].([]interface{}) - fmt.Printf("OutFields: %v\n", len(outFields)) - for i, outField := range outFields { - outFieldMap := outField.(map[string]interface{}) - fmt.Printf("\t%d: %v\n", i, outFieldMap["Type"]) - } - actionSchema = ExcludeColumnFromMap(actionSchema, []string{"InFields", "OutFields"}) - return c.renderer.RenderObject(actionSchema) -} - -func (c *ApplicationController) ActionExecute(context *cli.Context) error { - worldName := context.Args().Get(0) - actionName := context.Args().Get(1) - log.Printf("Execute action [%s][%s]\n", worldName, actionName) - world, err := c.GetWorldByName(worldName) - if err != nil { - return err - } - action, err := c.GetAction(world["reference_id"].(string), actionName) - if err != nil { - return err - } - var actionSchema map[string]interface{} - err = json.Unmarshal([]byte(action["action_schema"].(string)), &actionSchema) - if err != nil { - return err - } - err = c.renderer.RenderObject(actionSchema) - - _, err = c.daptinClient.Execute("signin", "user_account", map[string]interface{}{ - "email": context.Args().Get(0), - "password": context.Args().Get(1), - }) - //log.Printf("%v -%v", action, responses) - if err != nil { - return err - } - - return err -} - -func (c *ApplicationController) GetWorldByName(name string) (map[string]interface{}, error) { - for _, world := range c.worlds { - if world["table_name"] == name { - return world, nil - } - } - - return nil, errors.New("world not found [" + name + "]") -} - -func (c *ApplicationController) GetAction(worldId string, actionName string) (map[string]interface{}, error) { - for _, action := range c.actions { - if action["world_id"].(string) == worldId && action["action_name"].(string) == actionName { - return action, nil - } - } - return nil, errors.New("action not found [" + actionName + "]") -} - -func (c *ApplicationController) SetDaptinClient(instance daptinClient.DaptinClient) { - c.daptinClient = instance -} - -func (appController *ApplicationController) SetWorlds(worlds []map[string]interface{}) { - appController.worlds = make(map[string]map[string]interface{}) - for _, world := range worlds { - appController.worlds[world["table_name"].(string)] = world - } - -} - -func (appController *ApplicationController) SetActions(actions []map[string]interface{}) { - appController.actions = make(map[string]map[string]interface{}) - for _, action := range actions { - appController.actions[action["action_name"].(string)] = action - } -} - -func (appController *ApplicationController) SetRenderer(renderer Renderer) { - appController.renderer = renderer -} - -func NewApplicationController(config DaptinCliConfig, file string) *ApplicationController { - return &ApplicationController{ - daptinCliConfig: config, - configPath: file, - } -} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3b0380c --- /dev/null +++ b/src/index.ts @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +import { Command } from 'commander'; +import { registerConfigCommands } from './commands/config'; +import { registerAuthCommands } from './commands/auth'; +import { registerDescribeCommands } from './commands/describe'; +import { registerCrudCommands } from './commands/crud'; +import { registerActionCommands } from './commands/action'; +import { registerAggregateCommands } from './commands/aggregate'; + +const program = new Command(); + +program + .name('daptin-cli') + .description('Command-line interface for Daptin Backend-as-a-Service') + .version('0.1.0') + .option('-e, --endpoint ', 'Daptin server endpoint') + .option('-c, --config ', 'Config file path') + .option('-o, --output ', 'Output format (table, json)', 'table') + .option('-t, --token ', 'Auth token') + .option('--debug', 'Enable debug output', false); + +// Register all command groups +registerConfigCommands(program); +registerAuthCommands(program); +registerDescribeCommands(program); +registerCrudCommands(program); +registerActionCommands(program); +registerAggregateCommands(program); + +program.parse(process.argv); diff --git a/src/output/index.ts b/src/output/index.ts new file mode 100644 index 0000000..01804ce --- /dev/null +++ b/src/output/index.ts @@ -0,0 +1,105 @@ +import Table from 'cli-table3'; +import chalk from 'chalk'; + +export type OutputFormat = 'table' | 'json'; + +export interface Renderer { + renderArray(data: Record[], columns?: string[]): void; + renderObject(data: Record, columns?: string[]): void; +} + +export class TableRenderer implements Renderer { + renderArray(data: Record[], columns?: string[]): void { + if (!data || data.length === 0) { + console.log(chalk.yellow('No data found.')); + return; + } + + const headers = columns && columns.length > 0 ? columns : Object.keys(data[0]); + + const table = new Table({ + head: headers.map((h) => chalk.cyan(h)), + style: { head: [], border: [] }, + wordWrap: true, + }); + + for (const row of data) { + table.push( + headers.map((h) => { + const val = row[h]; + if (val === null || val === undefined) return ''; + const str = typeof val === 'object' ? JSON.stringify(val) : String(val); + return str.length > 60 ? str.substring(0, 57) + '...' : str; + }) + ); + } + + console.log(table.toString()); + } + + renderObject(data: Record, columns?: string[]): void { + if (!data || Object.keys(data).length === 0) { + console.log(chalk.yellow('No data found.')); + return; + } + + const keys = columns && columns.length > 0 ? columns : Object.keys(data); + + const table = new Table({ + style: { head: [], border: [] }, + }); + + for (const key of keys) { + if (!(key in data)) continue; + const val = data[key]; + let str: string; + if (val === null || val === undefined) { + str = ''; + } else if (typeof val === 'object') { + str = JSON.stringify(val, null, 2); + } else { + str = String(val); + } + if (str.length > 100) str = str.substring(0, 97) + '...'; + table.push({ [chalk.cyan(key)]: str }); + } + + console.log(table.toString()); + } +} + +export class JsonRenderer implements Renderer { + renderArray(data: Record[], columns?: string[]): void { + if (columns && columns.length > 0) { + data = data.map((row) => { + const filtered: Record = {}; + for (const col of columns) { + if (col in row) filtered[col] = row[col]; + } + return filtered; + }); + } + console.log(JSON.stringify(data, null, 2)); + } + + renderObject(data: Record, columns?: string[]): void { + if (columns && columns.length > 0) { + const filtered: Record = {}; + for (const col of columns) { + if (col in data) filtered[col] = data[col]; + } + data = filtered; + } + console.log(JSON.stringify(data, null, 2)); + } +} + +export function createRenderer(format: OutputFormat): Renderer { + switch (format) { + case 'json': + return new JsonRenderer(); + case 'table': + default: + return new TableRenderer(); + } +} diff --git a/src/renderer.go b/src/renderer.go deleted file mode 100644 index 37ec710..0000000 --- a/src/renderer.go +++ /dev/null @@ -1,123 +0,0 @@ -package src - -import ( - "bufio" - "encoding/json" - "fmt" - "golang.org/x/term" - "os" - "sort" - "strings" - "syscall" - "text/tabwriter" -) - -func readStringsAsArgs(countOfaArgs int) ([]string, error) { - reader := bufio.NewReader(os.Stdin) - - fmt.Print("Enter Username: ") - username, err := reader.ReadString('\n') - if err != nil { - return nil, err - } - - fmt.Print("Enter Password: ") - bytePassword, err := term.ReadPassword(int(syscall.Stdin)) - if err != nil { - return nil, err - } - - password := string(bytePassword) - return []string{strings.TrimSpace(username), strings.TrimSpace(password)}, nil -} - -type Renderer interface { - RenderArray(array []map[string]interface{}) error - RenderObject(object map[string]interface{}) error -} - -type TableRenderer struct { -} - -func NewTableRenderer() *TableRenderer { - return &TableRenderer{} -} - -func (t *TableRenderer) RenderObject(data map[string]interface{}) error { - var err error - for header, val := range data { - valString := fmt.Sprintf("%v", val) - if len(valString) > 50 { - valString = valString[:50] + "..." - } - _, err = fmt.Fprintf(os.Stdout, "[%s]: %s\n", header, valString) - } - return err -} - -func (t *TableRenderer) RenderArray(data []map[string]interface{}) error { - if len(data) == 0 { - fmt.Println("No data to print") - return nil - } - - // Extract headers - headers := make([]string, 0, len(data[0])) - for header := range data[0] { - headers = append(headers, header) - } - - sort.Strings(headers) - - // Create a tabwriter - tw := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.Debug) - var err error - - // Print headers - for _, header := range headers { - _, err = fmt.Fprintf(tw, "%s\t", header) - } - _, err = fmt.Fprintln(tw) - - // Print data - for _, row := range data { - for _, header := range headers { - val := row[header] - valString := fmt.Sprintf("%v", val) - if len(valString) > 50 { - valString = valString[:50] + "..." - } - _, err = fmt.Fprintf(tw, "%s\t", valString) - } - _, err = fmt.Fprintln(tw) - } - - // Flush the tabwriter - err = tw.Flush() - return err -} - -type JsonRenderer struct { -} - -func NewJsonRenderer() *JsonRenderer { - return &JsonRenderer{} -} - -func (j JsonRenderer) RenderObject(data map[string]interface{}) error { - jsonData, err := json.MarshalIndent(data, "", " ") - if err != nil { - return err - } - fmt.Println(string(jsonData)) - return nil -} - -func (j JsonRenderer) RenderArray(data []map[string]interface{}) error { - jsonData, err := json.MarshalIndent(data, "", " ") - if err != nil { - return err - } - fmt.Println(string(jsonData)) - return nil -} diff --git a/src/utils.go b/src/utils.go deleted file mode 100644 index df04cf8..0000000 --- a/src/utils.go +++ /dev/null @@ -1,11 +0,0 @@ -package src - -import daptinClient "github.com/daptin/daptin-go-client" - -func MapArray(allWorlds []daptinClient.JsonApiObject, keyName string) []map[string]interface{} { - worlds := make([]map[string]interface{}, len(allWorlds)) - for i, w := range allWorlds { - worlds[i] = w[keyName].(map[string]interface{}) - } - return worlds -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fddb997 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}