From 4ce89479a4cbf21fd966f5ffb137747b5e862a4e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Fri, 26 Jun 2026 19:16:19 +0530 Subject: [PATCH 01/14] feat(cli): scaffold official CLI package --- sdks/urbackend-cli/LICENSE | 0 sdks/urbackend-cli/README.md | 0 sdks/urbackend-cli/bin/ub.js | 0 sdks/urbackend-cli/dist/index.cjs | 5 +++ sdks/urbackend-cli/dist/index.cjs.map | 1 + sdks/urbackend-cli/dist/index.d.cts | 2 + sdks/urbackend-cli/dist/index.d.ts | 2 + sdks/urbackend-cli/dist/index.js | 3 ++ sdks/urbackend-cli/dist/index.js.map | 1 + sdks/urbackend-cli/index.ts | 1 + sdks/urbackend-cli/package.json | 54 +++++++++++++++++++++++++++ sdks/urbackend-cli/tsconfig.json | 24 ++++++++++++ sdks/urbackend-cli/tsup.config.ts | 11 ++++++ 13 files changed, 104 insertions(+) create mode 100644 sdks/urbackend-cli/LICENSE create mode 100644 sdks/urbackend-cli/README.md create mode 100644 sdks/urbackend-cli/bin/ub.js create mode 100644 sdks/urbackend-cli/dist/index.cjs create mode 100644 sdks/urbackend-cli/dist/index.cjs.map create mode 100644 sdks/urbackend-cli/dist/index.d.cts create mode 100644 sdks/urbackend-cli/dist/index.d.ts create mode 100644 sdks/urbackend-cli/dist/index.js create mode 100644 sdks/urbackend-cli/dist/index.js.map create mode 100644 sdks/urbackend-cli/index.ts create mode 100644 sdks/urbackend-cli/package.json create mode 100644 sdks/urbackend-cli/tsconfig.json create mode 100644 sdks/urbackend-cli/tsup.config.ts diff --git a/sdks/urbackend-cli/LICENSE b/sdks/urbackend-cli/LICENSE new file mode 100644 index 000000000..e69de29bb diff --git a/sdks/urbackend-cli/README.md b/sdks/urbackend-cli/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/sdks/urbackend-cli/bin/ub.js b/sdks/urbackend-cli/bin/ub.js new file mode 100644 index 000000000..e69de29bb diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs new file mode 100644 index 000000000..079ea7213 --- /dev/null +++ b/sdks/urbackend-cli/dist/index.cjs @@ -0,0 +1,5 @@ +"use strict"; + +// index.ts +console.log("urBackend CLI"); +//# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map new file mode 100644 index 000000000..dd676c750 --- /dev/null +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../index.ts"],"sourcesContent":["console.log(\"urBackend CLI\");"],"mappings":";;;AAAA,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.d.cts b/sdks/urbackend-cli/dist/index.d.cts new file mode 100644 index 000000000..223e65e89 --- /dev/null +++ b/sdks/urbackend-cli/dist/index.d.cts @@ -0,0 +1,2 @@ + +export { } diff --git a/sdks/urbackend-cli/dist/index.d.ts b/sdks/urbackend-cli/dist/index.d.ts new file mode 100644 index 000000000..223e65e89 --- /dev/null +++ b/sdks/urbackend-cli/dist/index.d.ts @@ -0,0 +1,2 @@ + +export { } diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js new file mode 100644 index 000000000..595424a64 --- /dev/null +++ b/sdks/urbackend-cli/dist/index.js @@ -0,0 +1,3 @@ +// index.ts +console.log("urBackend CLI"); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map new file mode 100644 index 000000000..3dee12d2b --- /dev/null +++ b/sdks/urbackend-cli/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../index.ts"],"sourcesContent":["console.log(\"urBackend CLI\");"],"mappings":";AAAA,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file diff --git a/sdks/urbackend-cli/index.ts b/sdks/urbackend-cli/index.ts new file mode 100644 index 000000000..1bf31c8be --- /dev/null +++ b/sdks/urbackend-cli/index.ts @@ -0,0 +1 @@ +console.log("urBackend CLI"); \ No newline at end of file diff --git a/sdks/urbackend-cli/package.json b/sdks/urbackend-cli/package.json new file mode 100644 index 000000000..b908a4879 --- /dev/null +++ b/sdks/urbackend-cli/package.json @@ -0,0 +1,54 @@ +{ + "name": "@urbackend/cli", + "version": "0.1.0", + "description": "Official CLI for urBackend", + "publishConfig": { + "access": "public" + }, + "type": "module", + "bin": { + "ub": "./bin/ub.js" + }, + "files": [ + "dist", + "bin", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsup --config tsup.config.ts", + "test": "vitest run", + "lint": "eslint src", + "format": "prettier --write \"src/**/*.ts\"" + }, + "keywords": [ + "urbackend", + "cli", + "baas" + ], + "author": "urBackend Contributors", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/geturbackend/urBackend", + "directory": "sdks/urbackend-cli" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^25.5.0", + "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^10.1.0", + "prettier": "^3.8.1", + "tsup": "^8.5.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.2", + "vitest": "^1.6.0" + }, + "dependencies": { + "axios": "^1.12.2", + "chalk": "^5.6.2", + "commander": "^14.0.1", + "ora": "^9.0.0" + } +} \ No newline at end of file diff --git a/sdks/urbackend-cli/tsconfig.json b/sdks/urbackend-cli/tsconfig.json new file mode 100644 index 000000000..2a4dbe9bf --- /dev/null +++ b/sdks/urbackend-cli/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "rootDir": ".", + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true + }, + "include": [ + "src", + "index.ts" + ], + "exclude": [ + "dist", + "node_modules" + ] +} \ No newline at end of file diff --git a/sdks/urbackend-cli/tsup.config.ts b/sdks/urbackend-cli/tsup.config.ts new file mode 100644 index 000000000..3f3422cf5 --- /dev/null +++ b/sdks/urbackend-cli/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["index.ts"], + format: ["esm", "cjs"], + dts: true, + sourcemap: true, + clean: true, + outDir: "dist", + target: "node20" +}); \ No newline at end of file From d160df3302e66b3733e4e5c3f78cdfdda2fecc04 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Fri, 26 Jun 2026 20:12:41 +0530 Subject: [PATCH 02/14] feat(cli): initialize CLI with Commander --- package-lock.json | 1964 +++++++++++++------------ sdks/urbackend-cli/bin/ub.js | 3 + sdks/urbackend-cli/dist/index.cjs | 6 + sdks/urbackend-cli/dist/index.cjs.map | 2 +- sdks/urbackend-cli/dist/index.js | 6 + sdks/urbackend-cli/dist/index.js.map | 2 +- sdks/urbackend-cli/index.ts | 1 + sdks/urbackend-cli/package.json | 4 +- sdks/urbackend-cli/src/cli.ts | 10 + 9 files changed, 1013 insertions(+), 985 deletions(-) create mode 100644 sdks/urbackend-cli/src/cli.ts diff --git a/package-lock.json b/package-lock.json index b4ff0db88..2adea1bbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1147,7 +1147,6 @@ "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "*", "@tailwindcss/vite": "^4.1.18", "@tanstack/react-table": "^8.21.3", "@vercel/analytics": "^2.0.1", @@ -2992,7 +2991,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3026,7 +3024,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3060,7 +3057,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3126,7 +3122,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5392,6 +5387,13 @@ "@types/ms": "*" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "license": "MIT" @@ -5747,6 +5749,10 @@ "win32" ] }, + "node_modules/@urbackend/cli": { + "resolved": "sdks/urbackend-cli", + "link": true + }, "node_modules/@urbackend/common": { "resolved": "packages/common", "link": true @@ -5778,6 +5784,219 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/snapshot/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/accepts": { "version": "2.0.0", "license": "MIT", @@ -5943,6 +6162,16 @@ "dev": true, "license": "MIT" }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/asynckit": { "version": "0.4.0", "license": "MIT" @@ -6382,6 +6611,35 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "dev": true, @@ -6450,6 +6708,8 @@ }, "node_modules/check-error": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "license": "MIT", "dependencies": { @@ -6494,7 +6754,6 @@ }, "node_modules/cli-cursor": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" @@ -6506,6 +6765,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", + "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "5.2.0", "dev": true, @@ -7021,6 +7292,8 @@ }, "node_modules/deep-eql": { "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "license": "MIT", "dependencies": { @@ -7171,6 +7444,8 @@ }, "node_modules/diff-sequences": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", "engines": { @@ -7418,7 +7693,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7435,7 +7709,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7452,7 +7725,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7469,7 +7741,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7486,7 +7757,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7503,7 +7773,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7520,7 +7789,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7537,7 +7805,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7554,7 +7821,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7571,7 +7837,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7588,7 +7853,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7605,7 +7869,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7622,7 +7885,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7639,7 +7901,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7656,7 +7917,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7673,7 +7933,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7690,7 +7949,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7707,7 +7965,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7724,7 +7981,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7741,7 +7997,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7758,7 +8013,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7775,7 +8029,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8041,6 +8294,8 @@ }, "node_modules/estree-walker": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -8576,7 +8831,6 @@ }, "node_modules/get-east-asian-width": { "version": "1.6.0", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -8587,6 +8841,8 @@ }, "node_modules/get-func-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "license": "MIT", "engines": { @@ -9285,6 +9541,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "dev": true, @@ -9414,6 +9682,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "dev": true, @@ -10506,6 +10786,22 @@ "version": "4.1.1", "license": "MIT" }, + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/log-update": { "version": "6.1.0", "dev": true, @@ -10662,6 +10958,8 @@ }, "node_modules/loupe": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -11578,7 +11876,6 @@ }, "node_modules/mimic-function": { "version": "5.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -12041,6 +12338,83 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz", + "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.3.2", + "string-width": "^8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -12231,6 +12605,8 @@ }, "node_modules/pathval": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "license": "MIT", "engines": { @@ -12986,7 +13362,6 @@ }, "node_modules/restore-cursor": { "version": "5.1.0", - "dev": true, "license": "MIT", "dependencies": { "onetime": "^7.0.0", @@ -13001,7 +13376,6 @@ }, "node_modules/restore-cursor/node_modules/onetime": { "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" @@ -13399,7 +13773,6 @@ }, "node_modules/signal-exit": { "version": "4.1.0", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -13543,6 +13916,25 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", + "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "dev": true, @@ -13990,6 +14382,8 @@ }, "node_modules/tinyspy": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, "license": "MIT", "engines": { @@ -14694,53 +15088,373 @@ } } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", "dev": true, "license": "MIT", "dependencies": { - "xml-name-validator": "^5.0.0" + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=18" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/walker": { - "version": "1.0.8", + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/web-dashboard": { - "resolved": "apps/web-dashboard", - "link": true - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "license": "BSD-2-Clause", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=12" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", + "node_modules/vitest/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/vitest/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vitest/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-dashboard": { + "resolved": "apps/web-dashboard", + "link": true + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -15025,6 +15739,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.4.2", "license": "MIT", @@ -15685,501 +16411,294 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "sdks/urbackend-react": { - "name": "@urbackend/react", - "version": "0.2.4", + "sdks/urbackend-cli": { + "name": "@urbackend/cli", + "version": "0.1.0", + "license": "MIT", "dependencies": { - "@urbackend/sdk": "^0.4.4" + "axios": "^1.12.2", + "chalk": "^5.6.2", + "commander": "^14.0.3", + "ora": "^9.0.0" + }, + "bin": { + "ub": "bin/ub.js" }, "devDependencies": { - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0", - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.0", - "jsdom": "^24.0.0", - "tsup": "^8.0.0", - "typescript": "^5.0.0", + "@eslint/js": "^10.0.1", + "@types/node": "^25.5.0", + "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^10.1.0", + "prettier": "^3.8.1", + "tsup": "^8.5.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.2", "vitest": "^1.6.0" - }, - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" } }, - "sdks/urbackend-react/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "sdks/urbackend-react/node_modules/@jest/schemas": { - "version": "29.6.3", + "sdks/urbackend-cli/node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "sdks/urbackend-react/node_modules/@sinclair/typebox": { - "version": "0.27.10", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-react/node_modules/@types/react": { - "version": "18.3.29", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" - } - }, - "sdks/urbackend-react/node_modules/@types/react-dom": { - "version": "18.3.7", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-react/node_modules/@vitest/expect": { - "version": "1.6.1", + "sdks/urbackend-cli/node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" + "@eslint/core": "^1.2.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-react/node_modules/@vitest/runner": { - "version": "1.6.1", + "sdks/urbackend-cli/node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@types/json-schema": "^7.0.15" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-react/node_modules/@vitest/snapshot": { - "version": "1.6.1", + "sdks/urbackend-cli/node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "sdks/urbackend-react/node_modules/@vitest/spy": { - "version": "1.6.1", + "sdks/urbackend-cli/node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^2.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-react/node_modules/@vitest/utils": { - "version": "1.6.1", + "sdks/urbackend-cli/node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-react/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, + "sdks/urbackend-cli/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "sdks/urbackend-react/node_modules/assertion-error": { - "version": "1.1.0", - "dev": true, + "sdks/urbackend-cli/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "license": "MIT", "engines": { - "node": "*" + "node": ">=20" } }, - "sdks/urbackend-react/node_modules/chai": { - "version": "4.5.0", + "sdks/urbackend-cli/node_modules/eslint": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", + "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" }, - "engines": { - "node": ">=4" - } - }, - "sdks/urbackend-react/node_modules/esbuild": { - "version": "0.21.5", - "dev": true, - "hasInstallScript": true, - "license": "MIT", "bin": { - "esbuild": "bin/esbuild" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.13.0 || >=24" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "sdks/urbackend-react/node_modules/execa": { - "version": "8.0.1", + "sdks/urbackend-cli/node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=16.17" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "sdks/urbackend-react/node_modules/get-stream": { - "version": "8.0.1", + "sdks/urbackend-cli/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=16" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/human-signals": { - "version": "5.0.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" + "url": "https://opencollective.com/eslint" } }, - "sdks/urbackend-react/node_modules/is-stream": { - "version": "3.0.0", + "sdks/urbackend-cli/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "sdks/urbackend-react/node_modules/mimic-fn": { - "version": "4.0.0", + "sdks/urbackend-cli/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4" } }, - "sdks/urbackend-react/node_modules/npm-run-path": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/onetime": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/p-limit": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/path-key": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/pathe": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-react/node_modules/pretty-format": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "sdks/urbackend-react/node_modules/std-env": { - "version": "3.10.0", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-react/node_modules/strip-final-newline": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-react/node_modules/type-detect": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "sdks/urbackend-react/node_modules/vite": { - "version": "5.4.21", - "dev": true, - "license": "MIT", + "sdks/urbackend-react": { + "name": "@urbackend/react", + "version": "0.2.4", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" + "@urbackend/sdk": "^0.4.4" }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "devDependencies": { + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "jsdom": "^24.0.0", + "tsup": "^8.0.0", + "typescript": "^5.0.0", + "vitest": "^1.6.0" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "react": ">=17.0.0", + "react-dom": ">=17.0.0" } }, - "sdks/urbackend-react/node_modules/vitest": { - "version": "1.6.1", + "sdks/urbackend-react/node_modules/@types/react": { + "version": "18.3.29", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } + "@types/prop-types": "*", + "csstype": "^3.2.2" } }, - "sdks/urbackend-react/node_modules/yocto-queue": { - "version": "1.2.2", + "sdks/urbackend-react/node_modules/@types/react-dom": { + "version": "18.3.7", "dev": true, "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@types/react": "^18.0.0" } }, "sdks/urbackend-sdk": { @@ -16207,23 +16726,6 @@ "dev": true, "license": "MIT" }, - "sdks/urbackend-sdk/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "sdks/urbackend-sdk/node_modules/@eslint/config-array": { "version": "0.23.3", "dev": true, @@ -16298,30 +16800,10 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "sdks/urbackend-sdk/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "sdks/urbackend-sdk/node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-sdk/node_modules/@vitest/coverage-v8": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.1.tgz", - "integrity": "sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==", + "sdks/urbackend-sdk/node_modules/@vitest/coverage-v8": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.1.tgz", + "integrity": "sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==", "dev": true, "license": "MIT", "dependencies": { @@ -16346,161 +16828,6 @@ "vitest": "1.6.1" } }, - "sdks/urbackend-sdk/node_modules/@vitest/expect": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", - "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "sdks/urbackend-sdk/node_modules/@vitest/runner": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", - "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "sdks/urbackend-sdk/node_modules/@vitest/snapshot": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", - "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "sdks/urbackend-sdk/node_modules/@vitest/spy": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", - "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^2.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "sdks/urbackend-sdk/node_modules/@vitest/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "sdks/urbackend-sdk/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "sdks/urbackend-sdk/node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "sdks/urbackend-sdk/node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "sdks/urbackend-sdk/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "sdks/urbackend-sdk/node_modules/eslint": { "version": "10.1.0", "dev": true, @@ -16599,53 +16926,6 @@ "url": "https://opencollective.com/eslint" } }, - "sdks/urbackend-sdk/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "sdks/urbackend-sdk/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, "sdks/urbackend-sdk/node_modules/ignore": { "version": "5.3.2", "dev": true, @@ -16654,19 +16934,6 @@ "node": ">= 4" } }, - "sdks/urbackend-sdk/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "sdks/urbackend-sdk/node_modules/magicast": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", @@ -16678,271 +16945,6 @@ "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } - }, - "sdks/urbackend-sdk/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-sdk/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "sdks/urbackend-sdk/node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "sdks/urbackend-sdk/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/urbackend-sdk/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "sdks/urbackend-sdk/node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "sdks/urbackend-sdk/node_modules/vitest": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", - "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "sdks/urbackend-sdk/node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/sdks/urbackend-cli/bin/ub.js b/sdks/urbackend-cli/bin/ub.js index e69de29bb..a2c488d4b 100644 --- a/sdks/urbackend-cli/bin/ub.js +++ b/sdks/urbackend-cli/bin/ub.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import("../dist/index.js"); \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs index 079ea7213..0d533f0af 100644 --- a/sdks/urbackend-cli/dist/index.cjs +++ b/sdks/urbackend-cli/dist/index.cjs @@ -1,5 +1,11 @@ "use strict"; +// src/cli.ts +var import_commander = require("commander"); +var program = new import_commander.Command(); +program.name("ub").description("Official CLI for urBackend").version("0.1.0"); +program.parse(); + // index.ts console.log("urBackend CLI"); //# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map index dd676c750..b600a8ba5 100644 --- a/sdks/urbackend-cli/dist/index.cjs.map +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"sources":["../index.ts"],"sourcesContent":["console.log(\"urBackend CLI\");"],"mappings":";;;AAAA,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/cli.ts","../index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram.parse();","import \"./src/cli.js\";\r\nconsole.log(\"urBackend CLI\");"],"mappings":";;;AAAA,uBAAwB;AAExB,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,4BAA4B,EACxC,QAAQ,OAAO;AAElB,QAAQ,MAAM;;;ACRd,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js index 595424a64..a9b4737ce 100644 --- a/sdks/urbackend-cli/dist/index.js +++ b/sdks/urbackend-cli/dist/index.js @@ -1,3 +1,9 @@ +// src/cli.ts +import { Command } from "commander"; +var program = new Command(); +program.name("ub").description("Official CLI for urBackend").version("0.1.0"); +program.parse(); + // index.ts console.log("urBackend CLI"); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map index 3dee12d2b..f2d6eb792 100644 --- a/sdks/urbackend-cli/dist/index.js.map +++ b/sdks/urbackend-cli/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../index.ts"],"sourcesContent":["console.log(\"urBackend CLI\");"],"mappings":";AAAA,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/cli.ts","../index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram.parse();","import \"./src/cli.js\";\r\nconsole.log(\"urBackend CLI\");"],"mappings":";AAAA,SAAS,eAAe;AAExB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,4BAA4B,EACxC,QAAQ,OAAO;AAElB,QAAQ,MAAM;;;ACRd,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file diff --git a/sdks/urbackend-cli/index.ts b/sdks/urbackend-cli/index.ts index 1bf31c8be..94a7bd95d 100644 --- a/sdks/urbackend-cli/index.ts +++ b/sdks/urbackend-cli/index.ts @@ -1 +1,2 @@ +import "./src/cli.js"; console.log("urBackend CLI"); \ No newline at end of file diff --git a/sdks/urbackend-cli/package.json b/sdks/urbackend-cli/package.json index b908a4879..def4f8f03 100644 --- a/sdks/urbackend-cli/package.json +++ b/sdks/urbackend-cli/package.json @@ -48,7 +48,7 @@ "dependencies": { "axios": "^1.12.2", "chalk": "^5.6.2", - "commander": "^14.0.1", + "commander": "^14.0.3", "ora": "^9.0.0" } -} \ No newline at end of file +} diff --git a/sdks/urbackend-cli/src/cli.ts b/sdks/urbackend-cli/src/cli.ts new file mode 100644 index 000000000..3d7d3bd65 --- /dev/null +++ b/sdks/urbackend-cli/src/cli.ts @@ -0,0 +1,10 @@ +import { Command } from "commander"; + +const program = new Command(); + +program + .name("ub") + .description("Official CLI for urBackend") + .version("0.1.0"); + +program.parse(); \ No newline at end of file From 262006b2794e1b8f6a48b2f1e490becf56a8da58 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sat, 27 Jun 2026 23:59:36 +0530 Subject: [PATCH 03/14] feat(cli): scaffold official urBackend CLI authentication --- sdks/urbackend-cli/dist/index.cjs | 247 +++++++++++++++++- sdks/urbackend-cli/dist/index.cjs.map | 2 +- sdks/urbackend-cli/dist/index.js | 225 +++++++++++++++- sdks/urbackend-cli/dist/index.js.map | 2 +- sdks/urbackend-cli/index.ts | 1 - sdks/urbackend-cli/package.json | 1 - sdks/urbackend-cli/src/cli.ts | 27 +- sdks/urbackend-cli/src/commands/login.ts | 52 ++++ sdks/urbackend-cli/src/commands/logout.ts | 8 + sdks/urbackend-cli/src/commands/whoami.ts | 29 ++ sdks/urbackend-cli/src/core/api.ts | 69 +++++ sdks/urbackend-cli/src/core/config.ts | 60 +++++ sdks/urbackend-cli/src/core/constants.ts | 24 ++ sdks/urbackend-cli/src/core/errors.ts | 16 ++ sdks/urbackend-cli/src/core/logger.ts | 19 ++ .../src/services/auth.service.ts | 17 ++ sdks/urbackend-cli/src/types/auth.ts | 18 ++ sdks/urbackend-cli/src/types/config.ts | 5 + sdks/urbackend-cli/src/utils/token.ts | 3 + 19 files changed, 814 insertions(+), 11 deletions(-) create mode 100644 sdks/urbackend-cli/src/commands/login.ts create mode 100644 sdks/urbackend-cli/src/commands/logout.ts create mode 100644 sdks/urbackend-cli/src/commands/whoami.ts create mode 100644 sdks/urbackend-cli/src/core/api.ts create mode 100644 sdks/urbackend-cli/src/core/config.ts create mode 100644 sdks/urbackend-cli/src/core/constants.ts create mode 100644 sdks/urbackend-cli/src/core/errors.ts create mode 100644 sdks/urbackend-cli/src/core/logger.ts create mode 100644 sdks/urbackend-cli/src/services/auth.service.ts create mode 100644 sdks/urbackend-cli/src/types/auth.ts create mode 100644 sdks/urbackend-cli/src/types/config.ts create mode 100644 sdks/urbackend-cli/src/utils/token.ts diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs index 0d533f0af..4e3c0ae1a 100644 --- a/sdks/urbackend-cli/dist/index.cjs +++ b/sdks/urbackend-cli/dist/index.cjs @@ -1,11 +1,252 @@ "use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); // src/cli.ts var import_commander = require("commander"); + +// src/commands/login.ts +var import_promises = require("readline/promises"); +var import_node_process = require("process"); + +// src/core/config.ts +var import_node_fs = __toESM(require("fs"), 1); + +// src/core/constants.ts +var import_node_os = __toESM(require("os"), 1); +var import_node_path = __toESM(require("path"), 1); +var CONFIG_DIR = import_node_path.default.join( + import_node_os.default.homedir(), + ".ub" +); +var CONFIG_PATH = import_node_path.default.join( + CONFIG_DIR, + "config.json" +); +var DEFAULT_API_BASE = process.env.URBACKEND_API_URL ?? "https://api.urbackend.bitbros.in"; + +// src/core/config.ts +var DEFAULT_CONFIG = { + apiBase: DEFAULT_API_BASE +}; +function loadConfig() { + if (!import_node_fs.default.existsSync(CONFIG_PATH)) { + return DEFAULT_CONFIG; + } + try { + const raw = import_node_fs.default.readFileSync(CONFIG_PATH, "utf8"); + return { + ...DEFAULT_CONFIG, + ...JSON.parse(raw) + }; + } catch { + return DEFAULT_CONFIG; + } +} +function saveConfig(config) { + if (!import_node_fs.default.existsSync(CONFIG_DIR)) { + import_node_fs.default.mkdirSync(CONFIG_DIR, { recursive: true }); + } + import_node_fs.default.writeFileSync( + CONFIG_PATH, + JSON.stringify(config, null, 2) + ); +} +function saveToken(token) { + const config = loadConfig(); + saveConfig({ + ...config, + pat: token + }); +} +function clearToken() { + const config = loadConfig(); + delete config.pat; + saveConfig(config); +} + +// src/core/errors.ts +var APIError = class extends Error { + constructor(status, message) { + super(message); + this.status = status; + this.name = "APIError"; + } + status; +}; + +// src/core/api.ts +async function apiFetch(endpoint, options = {}) { + const config = loadConfig(); + const headers = new Headers( + options.headers + ); + if (options.body && !headers.has("Content-Type")) { + headers.set( + "Content-Type", + "application/json" + ); + } + const token = options.token ?? config.pat; + if (token) { + headers.set( + "Authorization", + `Bearer ${token}` + ); + } + const response = await fetch( + `${config.apiBase}${endpoint}`, + { + ...options, + headers + } + ); + if (!response.ok) { + let message = response.statusText; + try { + const body = await response.json(); + message = body.message ?? body.error ?? message; + } catch { + } + throw new APIError( + response.status, + message + ); + } + if (response.status === 204) { + return void 0; + } + return await response.json(); +} + +// src/services/auth.service.ts +async function authenticate(token) { + return apiFetch("/api/user/cli/me", { + method: "GET", + token + }); +} +async function getProfile() { + return apiFetch("/api/user/cli/me", { + method: "GET" + }); +} + +// src/utils/token.ts +function isValidPAT(token) { + return token.startsWith("ubpat_"); +} + +// src/core/logger.ts +var PREFIX = "ub"; +var logger = { + info(message) { + console.log(`${PREFIX}: ${message}`); + }, + success(message) { + console.log(`\u2713 ${message}`); + }, + warn(message) { + console.warn(`\u26A0 ${message}`); + }, + error(message) { + console.error(`\u2716 ${message}`); + } +}; + +// src/commands/login.ts +async function loginCommand() { + const rl = (0, import_promises.createInterface)({ + input: import_node_process.stdin, + output: import_node_process.stdout + }); + const token = (await rl.question("Paste your Personal Access Token: ")).trim(); + rl.close(); + if (!isValidPAT(token)) { + logger.error("Invalid Personal Access Token."); + return; + } + try { + const profile = await authenticate(token); + saveToken(token); + logger.success("Successfully logged in."); + console.log(""); + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + if (profile.auth.scopes.length > 0) { + console.log( + `Scopes : ${profile.auth.scopes.join(", ")}` + ); + } + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + logger.error("Unable to connect to urBackend."); + } +} + +// src/commands/logout.ts +async function logoutCommand() { + clearToken(); + logger.success("Logged out successfully."); +} + +// src/commands/whoami.ts +async function whoamiCommand() { + try { + const profile = await getProfile(); + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + if (profile.developer.githubUsername) { + console.log( + `GitHub : ${profile.developer.githubUsername}` + ); + } + console.log( + `Token Type : ${profile.auth.tokenType}` + ); + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + logger.error("Unable to connect to urBackend."); + } +} + +// src/cli.ts var program = new import_commander.Command(); program.name("ub").description("Official CLI for urBackend").version("0.1.0"); +program.command("login").description( + "Authenticate using a Personal Access Token" +).action(loginCommand); +program.command("logout").description( + "Remove the local authentication" +).action(logoutCommand); +program.command("whoami").description( + "Display the authenticated developer" +).action(whoamiCommand); program.parse(); - -// index.ts -console.log("urBackend CLI"); //# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map index b600a8ba5..56afc462c 100644 --- a/sdks/urbackend-cli/dist/index.cjs.map +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/cli.ts","../index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram.parse();","import \"./src/cli.js\";\r\nconsole.log(\"urBackend CLI\");"],"mappings":";;;AAAA,uBAAwB;AAExB,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,4BAA4B,EACxC,QAAQ,OAAO;AAElB,QAAQ,MAAM;;;ACRd,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/core/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nimport { loginCommand } from \"./commands/login.js\";\r\nimport { logoutCommand } from \"./commands/logout.js\";\r\nimport { whoamiCommand } from \"./commands/whoami.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\r\n \"Authenticate using a Personal Access Token\",\r\n )\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\r\n \"Remove the local authentication\",\r\n )\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\r\n \"Display the authenticated developer\",\r\n )\r\n .action(whoamiCommand);\r\n\r\nprogram.parse();\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\nimport { authenticate } from \"../services/auth.service.js\";\r\nimport { saveToken } from \"../core/config.js\";\r\nimport { isValidPAT } from \"../utils/token.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function loginCommand() {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n const token = (\r\n await rl.question(\"Paste your Personal Access Token: \")\r\n ).trim();\r\n\r\n rl.close();\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\"Invalid Personal Access Token.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Successfully logged in.\");\r\n\r\n console.log(\"\");\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(\r\n `Scopes : ${profile.auth.scopes.join(\", \")}`,\r\n );\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}","import fs from \"node:fs\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport {\r\n CONFIG_DIR,\r\n CONFIG_PATH,\r\n DEFAULT_API_BASE,\r\n} from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return DEFAULT_CONFIG;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(\r\n CONFIG_PATH,\r\n JSON.stringify(config, null, 2),\r\n );\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n\r\n saveConfig({\r\n ...config,\r\n pat: token,\r\n });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n\r\n delete config.pat;\r\n\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(\r\n os.homedir(),\r\n \".ub\",\r\n);\r\n\r\nexport const CONFIG_PATH = path.join(\r\n CONFIG_DIR,\r\n \"config.json\",\r\n);\r\n\r\n/**\r\n * Default production API.\r\n *\r\n * Can be overridden during development by\r\n * URBACKEND_API_URL.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ??\r\n \"https://api.urbackend.bitbros.in\";","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n\r\nexport class ConfigError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ConfigError\";\r\n }\r\n}","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions\r\n extends RequestInit {\r\n token?: string;\r\n}\r\n\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(\r\n options.headers,\r\n );\r\n\r\n if (\r\n options.body &&\r\n !headers.has(\"Content-Type\")\r\n) {\r\n headers.set(\r\n \"Content-Type\",\r\n \"application/json\",\r\n );\r\n}\r\n const token =\r\n options.token ?? config.pat;\r\n\r\n if (token) {\r\n headers.set(\r\n \"Authorization\",\r\n `Bearer ${token}`,\r\n );\r\n }\r\n\r\n const response = await fetch(\r\n `${config.apiBase}${endpoint}`,\r\n {\r\n ...options,\r\n headers,\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = await response.json();\r\n\r\n message =\r\n body.message ??\r\n body.error ??\r\n message;\r\n } catch {}\r\n\r\n throw new APIError(\r\n response.status,\r\n message,\r\n );\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\nexport async function authenticate(\r\n token: string,\r\n): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n}","export function isValidPAT(token: string): boolean {\r\n return token.startsWith(\"ubpat_\");\r\n}","const PREFIX = \"ub\";\r\n\r\nexport const logger = {\r\n info(message: string) {\r\n console.log(`${PREFIX}: ${message}`);\r\n },\r\n\r\n success(message: string) {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n warn(message: string) {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n error(message: string) {\r\n console.error(`✖ ${message}`);\r\n },\r\n};","import { clearToken } from \"../core/config.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function logoutCommand() {\r\n clearToken();\r\n\r\n logger.success(\"Logged out successfully.\");\r\n}","import { getProfile } from \"../services/auth.service.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function whoamiCommand() {\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(\r\n `GitHub : ${profile.developer.githubUsername}`,\r\n );\r\n }\r\n\r\n console.log(\r\n `Token Type : ${profile.auth.tokenType}`,\r\n );\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}"],"mappings":"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","names":["path","os","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js index a9b4737ce..24aa01c27 100644 --- a/sdks/urbackend-cli/dist/index.js +++ b/sdks/urbackend-cli/dist/index.js @@ -1,9 +1,228 @@ // src/cli.ts import { Command } from "commander"; + +// src/commands/login.ts +import { createInterface } from "readline/promises"; +import { stdin, stdout } from "process"; + +// src/core/config.ts +import fs from "fs"; + +// src/core/constants.ts +import os from "os"; +import path from "path"; +var CONFIG_DIR = path.join( + os.homedir(), + ".ub" +); +var CONFIG_PATH = path.join( + CONFIG_DIR, + "config.json" +); +var DEFAULT_API_BASE = process.env.URBACKEND_API_URL ?? "https://api.urbackend.bitbros.in"; + +// src/core/config.ts +var DEFAULT_CONFIG = { + apiBase: DEFAULT_API_BASE +}; +function loadConfig() { + if (!fs.existsSync(CONFIG_PATH)) { + return DEFAULT_CONFIG; + } + try { + const raw = fs.readFileSync(CONFIG_PATH, "utf8"); + return { + ...DEFAULT_CONFIG, + ...JSON.parse(raw) + }; + } catch { + return DEFAULT_CONFIG; + } +} +function saveConfig(config) { + if (!fs.existsSync(CONFIG_DIR)) { + fs.mkdirSync(CONFIG_DIR, { recursive: true }); + } + fs.writeFileSync( + CONFIG_PATH, + JSON.stringify(config, null, 2) + ); +} +function saveToken(token) { + const config = loadConfig(); + saveConfig({ + ...config, + pat: token + }); +} +function clearToken() { + const config = loadConfig(); + delete config.pat; + saveConfig(config); +} + +// src/core/errors.ts +var APIError = class extends Error { + constructor(status, message) { + super(message); + this.status = status; + this.name = "APIError"; + } + status; +}; + +// src/core/api.ts +async function apiFetch(endpoint, options = {}) { + const config = loadConfig(); + const headers = new Headers( + options.headers + ); + if (options.body && !headers.has("Content-Type")) { + headers.set( + "Content-Type", + "application/json" + ); + } + const token = options.token ?? config.pat; + if (token) { + headers.set( + "Authorization", + `Bearer ${token}` + ); + } + const response = await fetch( + `${config.apiBase}${endpoint}`, + { + ...options, + headers + } + ); + if (!response.ok) { + let message = response.statusText; + try { + const body = await response.json(); + message = body.message ?? body.error ?? message; + } catch { + } + throw new APIError( + response.status, + message + ); + } + if (response.status === 204) { + return void 0; + } + return await response.json(); +} + +// src/services/auth.service.ts +async function authenticate(token) { + return apiFetch("/api/user/cli/me", { + method: "GET", + token + }); +} +async function getProfile() { + return apiFetch("/api/user/cli/me", { + method: "GET" + }); +} + +// src/utils/token.ts +function isValidPAT(token) { + return token.startsWith("ubpat_"); +} + +// src/core/logger.ts +var PREFIX = "ub"; +var logger = { + info(message) { + console.log(`${PREFIX}: ${message}`); + }, + success(message) { + console.log(`\u2713 ${message}`); + }, + warn(message) { + console.warn(`\u26A0 ${message}`); + }, + error(message) { + console.error(`\u2716 ${message}`); + } +}; + +// src/commands/login.ts +async function loginCommand() { + const rl = createInterface({ + input: stdin, + output: stdout + }); + const token = (await rl.question("Paste your Personal Access Token: ")).trim(); + rl.close(); + if (!isValidPAT(token)) { + logger.error("Invalid Personal Access Token."); + return; + } + try { + const profile = await authenticate(token); + saveToken(token); + logger.success("Successfully logged in."); + console.log(""); + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + if (profile.auth.scopes.length > 0) { + console.log( + `Scopes : ${profile.auth.scopes.join(", ")}` + ); + } + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + logger.error("Unable to connect to urBackend."); + } +} + +// src/commands/logout.ts +async function logoutCommand() { + clearToken(); + logger.success("Logged out successfully."); +} + +// src/commands/whoami.ts +async function whoamiCommand() { + try { + const profile = await getProfile(); + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + if (profile.developer.githubUsername) { + console.log( + `GitHub : ${profile.developer.githubUsername}` + ); + } + console.log( + `Token Type : ${profile.auth.tokenType}` + ); + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + logger.error("Unable to connect to urBackend."); + } +} + +// src/cli.ts var program = new Command(); program.name("ub").description("Official CLI for urBackend").version("0.1.0"); +program.command("login").description( + "Authenticate using a Personal Access Token" +).action(loginCommand); +program.command("logout").description( + "Remove the local authentication" +).action(logoutCommand); +program.command("whoami").description( + "Display the authenticated developer" +).action(whoamiCommand); program.parse(); - -// index.ts -console.log("urBackend CLI"); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map index f2d6eb792..226dbc73c 100644 --- a/sdks/urbackend-cli/dist/index.js.map +++ b/sdks/urbackend-cli/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/cli.ts","../index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram.parse();","import \"./src/cli.js\";\r\nconsole.log(\"urBackend CLI\");"],"mappings":";AAAA,SAAS,eAAe;AAExB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,4BAA4B,EACxC,QAAQ,OAAO;AAElB,QAAQ,MAAM;;;ACRd,QAAQ,IAAI,eAAe;","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/core/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nimport { loginCommand } from \"./commands/login.js\";\r\nimport { logoutCommand } from \"./commands/logout.js\";\r\nimport { whoamiCommand } from \"./commands/whoami.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\r\n \"Authenticate using a Personal Access Token\",\r\n )\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\r\n \"Remove the local authentication\",\r\n )\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\r\n \"Display the authenticated developer\",\r\n )\r\n .action(whoamiCommand);\r\n\r\nprogram.parse();\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\nimport { authenticate } from \"../services/auth.service.js\";\r\nimport { saveToken } from \"../core/config.js\";\r\nimport { isValidPAT } from \"../utils/token.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function loginCommand() {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n const token = (\r\n await rl.question(\"Paste your Personal Access Token: \")\r\n ).trim();\r\n\r\n rl.close();\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\"Invalid Personal Access Token.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Successfully logged in.\");\r\n\r\n console.log(\"\");\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(\r\n `Scopes : ${profile.auth.scopes.join(\", \")}`,\r\n );\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}","import fs from \"node:fs\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport {\r\n CONFIG_DIR,\r\n CONFIG_PATH,\r\n DEFAULT_API_BASE,\r\n} from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return DEFAULT_CONFIG;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(\r\n CONFIG_PATH,\r\n JSON.stringify(config, null, 2),\r\n );\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n\r\n saveConfig({\r\n ...config,\r\n pat: token,\r\n });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n\r\n delete config.pat;\r\n\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(\r\n os.homedir(),\r\n \".ub\",\r\n);\r\n\r\nexport const CONFIG_PATH = path.join(\r\n CONFIG_DIR,\r\n \"config.json\",\r\n);\r\n\r\n/**\r\n * Default production API.\r\n *\r\n * Can be overridden during development by\r\n * URBACKEND_API_URL.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ??\r\n \"https://api.urbackend.bitbros.in\";","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n\r\nexport class ConfigError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ConfigError\";\r\n }\r\n}","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions\r\n extends RequestInit {\r\n token?: string;\r\n}\r\n\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(\r\n options.headers,\r\n );\r\n\r\n if (\r\n options.body &&\r\n !headers.has(\"Content-Type\")\r\n) {\r\n headers.set(\r\n \"Content-Type\",\r\n \"application/json\",\r\n );\r\n}\r\n const token =\r\n options.token ?? config.pat;\r\n\r\n if (token) {\r\n headers.set(\r\n \"Authorization\",\r\n `Bearer ${token}`,\r\n );\r\n }\r\n\r\n const response = await fetch(\r\n `${config.apiBase}${endpoint}`,\r\n {\r\n ...options,\r\n headers,\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = await response.json();\r\n\r\n message =\r\n body.message ??\r\n body.error ??\r\n message;\r\n } catch {}\r\n\r\n throw new APIError(\r\n response.status,\r\n message,\r\n );\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\nexport async function authenticate(\r\n token: string,\r\n): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n}","export function isValidPAT(token: string): boolean {\r\n return token.startsWith(\"ubpat_\");\r\n}","const PREFIX = \"ub\";\r\n\r\nexport const logger = {\r\n info(message: string) {\r\n console.log(`${PREFIX}: ${message}`);\r\n },\r\n\r\n success(message: string) {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n warn(message: string) {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n error(message: string) {\r\n console.error(`✖ ${message}`);\r\n },\r\n};","import { clearToken } from \"../core/config.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function logoutCommand() {\r\n clearToken();\r\n\r\n logger.success(\"Logged out successfully.\");\r\n}","import { getProfile } from \"../services/auth.service.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function whoamiCommand() {\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(\r\n `GitHub : ${profile.developer.githubUsername}`,\r\n );\r\n }\r\n\r\n console.log(\r\n `Token Type : ${profile.auth.tokenType}`,\r\n );\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}"],"mappings":"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","names":[]} \ No newline at end of file diff --git a/sdks/urbackend-cli/index.ts b/sdks/urbackend-cli/index.ts index 94a7bd95d..84828c393 100644 --- a/sdks/urbackend-cli/index.ts +++ b/sdks/urbackend-cli/index.ts @@ -1,2 +1 @@ import "./src/cli.js"; -console.log("urBackend CLI"); \ No newline at end of file diff --git a/sdks/urbackend-cli/package.json b/sdks/urbackend-cli/package.json index def4f8f03..35430dec1 100644 --- a/sdks/urbackend-cli/package.json +++ b/sdks/urbackend-cli/package.json @@ -46,7 +46,6 @@ "vitest": "^1.6.0" }, "dependencies": { - "axios": "^1.12.2", "chalk": "^5.6.2", "commander": "^14.0.3", "ora": "^9.0.0" diff --git a/sdks/urbackend-cli/src/cli.ts b/sdks/urbackend-cli/src/cli.ts index 3d7d3bd65..95a71b7d3 100644 --- a/sdks/urbackend-cli/src/cli.ts +++ b/sdks/urbackend-cli/src/cli.ts @@ -1,5 +1,9 @@ import { Command } from "commander"; +import { loginCommand } from "./commands/login.js"; +import { logoutCommand } from "./commands/logout.js"; +import { whoamiCommand } from "./commands/whoami.js"; + const program = new Command(); program @@ -7,4 +11,25 @@ program .description("Official CLI for urBackend") .version("0.1.0"); -program.parse(); \ No newline at end of file +program + .command("login") + .description( + "Authenticate using a Personal Access Token", + ) + .action(loginCommand); + +program + .command("logout") + .description( + "Remove the local authentication", + ) + .action(logoutCommand); + +program + .command("whoami") + .description( + "Display the authenticated developer", + ) + .action(whoamiCommand); + +program.parse(); diff --git a/sdks/urbackend-cli/src/commands/login.ts b/sdks/urbackend-cli/src/commands/login.ts new file mode 100644 index 000000000..31714e2a2 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/login.ts @@ -0,0 +1,52 @@ +import { createInterface } from "node:readline/promises"; +import { stdin, stdout } from "node:process"; + +import { authenticate } from "../services/auth.service.js"; +import { saveToken } from "../core/config.js"; +import { isValidPAT } from "../utils/token.js"; +import { APIError } from "../core/errors.js"; +import { logger } from "../core/logger.js"; + +export async function loginCommand() { + const rl = createInterface({ + input: stdin, + output: stdout, + }); + + const token = ( + await rl.question("Paste your Personal Access Token: ") + ).trim(); + + rl.close(); + + if (!isValidPAT(token)) { + logger.error("Invalid Personal Access Token."); + return; + } + + try { + const profile = await authenticate(token); + + saveToken(token); + + logger.success("Successfully logged in."); + + console.log(""); + + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + + if (profile.auth.scopes.length > 0) { + console.log( + `Scopes : ${profile.auth.scopes.join(", ")}`, + ); + } + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + + logger.error("Unable to connect to urBackend."); + } +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/logout.ts b/sdks/urbackend-cli/src/commands/logout.ts new file mode 100644 index 000000000..a80efea89 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/logout.ts @@ -0,0 +1,8 @@ +import { clearToken } from "../core/config.js"; +import { logger } from "../core/logger.js"; + +export async function logoutCommand() { + clearToken(); + + logger.success("Logged out successfully."); +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/whoami.ts b/sdks/urbackend-cli/src/commands/whoami.ts new file mode 100644 index 000000000..057f14dd8 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/whoami.ts @@ -0,0 +1,29 @@ +import { getProfile } from "../services/auth.service.js"; +import { APIError } from "../core/errors.js"; +import { logger } from "../core/logger.js"; + +export async function whoamiCommand() { + try { + const profile = await getProfile(); + + console.log(`Email : ${profile.developer.email}`); + console.log(`Plan : ${profile.developer.plan}`); + + if (profile.developer.githubUsername) { + console.log( + `GitHub : ${profile.developer.githubUsername}`, + ); + } + + console.log( + `Token Type : ${profile.auth.tokenType}`, + ); + } catch (error) { + if (error instanceof APIError) { + logger.error(error.message); + return; + } + + logger.error("Unable to connect to urBackend."); + } +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/api.ts b/sdks/urbackend-cli/src/core/api.ts new file mode 100644 index 000000000..6c8044ef9 --- /dev/null +++ b/sdks/urbackend-cli/src/core/api.ts @@ -0,0 +1,69 @@ +import { loadConfig } from "./config.js"; +import { APIError } from "./errors.js"; + +export interface FetchOptions + extends RequestInit { + token?: string; +} + +export async function apiFetch( + endpoint: string, + options: FetchOptions = {}, +): Promise { + const config = loadConfig(); + + const headers = new Headers( + options.headers, + ); + + if ( + options.body && + !headers.has("Content-Type") +) { + headers.set( + "Content-Type", + "application/json", + ); +} + const token = + options.token ?? config.pat; + + if (token) { + headers.set( + "Authorization", + `Bearer ${token}`, + ); + } + + const response = await fetch( + `${config.apiBase}${endpoint}`, + { + ...options, + headers, + }, + ); + + if (!response.ok) { + let message = response.statusText; + + try { + const body = await response.json(); + + message = + body.message ?? + body.error ?? + message; + } catch {} + + throw new APIError( + response.status, + message, + ); + } + + if (response.status === 204) { + return undefined as T; + } + + return (await response.json()) as T; +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/config.ts b/sdks/urbackend-cli/src/core/config.ts new file mode 100644 index 000000000..e07667bb4 --- /dev/null +++ b/sdks/urbackend-cli/src/core/config.ts @@ -0,0 +1,60 @@ +import fs from "node:fs"; +import type { CLIConfig } from "../types/config.js"; +import { + CONFIG_DIR, + CONFIG_PATH, + DEFAULT_API_BASE, +} from "./constants.js"; + +const DEFAULT_CONFIG: CLIConfig = { + apiBase: DEFAULT_API_BASE, +}; + +export function loadConfig(): CLIConfig { + if (!fs.existsSync(CONFIG_PATH)) { + return DEFAULT_CONFIG; + } + + try { + const raw = fs.readFileSync(CONFIG_PATH, "utf8"); + + return { + ...DEFAULT_CONFIG, + ...JSON.parse(raw), + }; + } catch { + return DEFAULT_CONFIG; + } +} + +export function saveConfig(config: CLIConfig): void { + if (!fs.existsSync(CONFIG_DIR)) { + fs.mkdirSync(CONFIG_DIR, { recursive: true }); + } + + fs.writeFileSync( + CONFIG_PATH, + JSON.stringify(config, null, 2), + ); +} + +export function saveToken(token: string): void { + const config = loadConfig(); + + saveConfig({ + ...config, + pat: token, + }); +} + +export function clearToken(): void { + const config = loadConfig(); + + delete config.pat; + + saveConfig(config); +} + +export function getToken(): string | undefined { + return loadConfig().pat; +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/constants.ts b/sdks/urbackend-cli/src/core/constants.ts new file mode 100644 index 000000000..7a07a5a38 --- /dev/null +++ b/sdks/urbackend-cli/src/core/constants.ts @@ -0,0 +1,24 @@ +import os from "node:os"; +import path from "node:path"; + +export const CLI_NAME = "ub"; + +export const CONFIG_DIR = path.join( + os.homedir(), + ".ub", +); + +export const CONFIG_PATH = path.join( + CONFIG_DIR, + "config.json", +); + +/** + * Default production API. + * + * Can be overridden during development by + * URBACKEND_API_URL. + */ +export const DEFAULT_API_BASE = + process.env.URBACKEND_API_URL ?? + "https://api.urbackend.bitbros.in"; \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/errors.ts b/sdks/urbackend-cli/src/core/errors.ts new file mode 100644 index 000000000..f32be5173 --- /dev/null +++ b/sdks/urbackend-cli/src/core/errors.ts @@ -0,0 +1,16 @@ +export class APIError extends Error { + constructor( + public readonly status: number, + message: string, + ) { + super(message); + this.name = "APIError"; + } +} + +export class ConfigError extends Error { + constructor(message: string) { + super(message); + this.name = "ConfigError"; + } +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/logger.ts b/sdks/urbackend-cli/src/core/logger.ts new file mode 100644 index 000000000..e83eaa20e --- /dev/null +++ b/sdks/urbackend-cli/src/core/logger.ts @@ -0,0 +1,19 @@ +const PREFIX = "ub"; + +export const logger = { + info(message: string) { + console.log(`${PREFIX}: ${message}`); + }, + + success(message: string) { + console.log(`✓ ${message}`); + }, + + warn(message: string) { + console.warn(`⚠ ${message}`); + }, + + error(message: string) { + console.error(`✖ ${message}`); + }, +}; \ No newline at end of file diff --git a/sdks/urbackend-cli/src/services/auth.service.ts b/sdks/urbackend-cli/src/services/auth.service.ts new file mode 100644 index 000000000..1c02dfe96 --- /dev/null +++ b/sdks/urbackend-cli/src/services/auth.service.ts @@ -0,0 +1,17 @@ +import { apiFetch } from "../core/api.js"; +import type { CLIProfile } from "../types/auth.js"; + +export async function authenticate( + token: string, +): Promise { + return apiFetch("/api/user/cli/me", { + method: "GET", + token, + }); +} + +export async function getProfile(): Promise { + return apiFetch("/api/user/cli/me", { + method: "GET", + }); +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/types/auth.ts b/sdks/urbackend-cli/src/types/auth.ts new file mode 100644 index 000000000..3e9ac8542 --- /dev/null +++ b/sdks/urbackend-cli/src/types/auth.ts @@ -0,0 +1,18 @@ +export interface AuthInfo { + scopes: string[]; + tokenType: string; +} + +export interface DeveloperProfile { + id: string; + email: string; + plan: string; + isVerified?: boolean; + githubUsername?: string | null; + avatarUrl?: string | null; +} + +export interface CLIProfile { + developer: DeveloperProfile; + auth: AuthInfo; +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/types/config.ts b/sdks/urbackend-cli/src/types/config.ts new file mode 100644 index 000000000..8cf8e0d9c --- /dev/null +++ b/sdks/urbackend-cli/src/types/config.ts @@ -0,0 +1,5 @@ +export interface CLIConfig { + apiBase: string; + pat?: string; + currentProject?: string; +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/utils/token.ts b/sdks/urbackend-cli/src/utils/token.ts new file mode 100644 index 000000000..7d0564459 --- /dev/null +++ b/sdks/urbackend-cli/src/utils/token.ts @@ -0,0 +1,3 @@ +export function isValidPAT(token: string): boolean { + return token.startsWith("ubpat_"); +} \ No newline at end of file From da88ad416f1a2b02981bdc8d5ad09fd31739fdcc Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 00:01:48 +0530 Subject: [PATCH 04/14] feat(cli): implement authentication commands --- .../src/controllers/cli.controller.js | 28 +++++++++++++++++++ .../src/middlewares/authMiddleware.js | 8 ++---- apps/dashboard-api/src/routes/user.js | 3 ++ package-lock.json | 1 - 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 apps/dashboard-api/src/controllers/cli.controller.js diff --git a/apps/dashboard-api/src/controllers/cli.controller.js b/apps/dashboard-api/src/controllers/cli.controller.js new file mode 100644 index 000000000..f61373790 --- /dev/null +++ b/apps/dashboard-api/src/controllers/cli.controller.js @@ -0,0 +1,28 @@ +const { Developer, ApiResponse, AppError } = require("@urbackend/common"); + +module.exports.getCLIProfile = async (req, res, next) => { + try { + const developer = await Developer.findById(req.developer._id) + .select("email plan githubUsername avatarUrl"); + + if (!developer) { + return next(new AppError(404, "Developer not found")); + } + + return new ApiResponse({ + developer: { + id: developer._id, + email: developer.email, + plan: developer.plan, + githubUsername: developer.githubUsername, + avatarUrl: developer.avatarUrl, + }, + auth: { + scopes: req.cliScopes, + tokenType: req.cliTokenType, + }, + }).send(res); + } catch (err) { + next(err); + } +}; \ No newline at end of file diff --git a/apps/dashboard-api/src/middlewares/authMiddleware.js b/apps/dashboard-api/src/middlewares/authMiddleware.js index bee883a65..9c46f33f2 100644 --- a/apps/dashboard-api/src/middlewares/authMiddleware.js +++ b/apps/dashboard-api/src/middlewares/authMiddleware.js @@ -32,10 +32,8 @@ module.exports = function (req, res, next) { // Proceed to the next middleware or route handler next(); } catch (err) { - if (process.env.NODE_ENV !== 'test') { - console.error(err); - } + console.error("JWT ERROR:", err); - return next(new AppError(401, 'Invalid Token')); - } + return next(new AppError(401, "Invalid Token")); +} }; diff --git a/apps/dashboard-api/src/routes/user.js b/apps/dashboard-api/src/routes/user.js index 640e9338f..43ffe037b 100644 --- a/apps/dashboard-api/src/routes/user.js +++ b/apps/dashboard-api/src/routes/user.js @@ -3,7 +3,10 @@ const router = express.Router(); const authorization = require('../middlewares/authMiddleware'); const { getMe, updateOnboarding } = require('../controllers/auth.controller'); const { createPAT, listPATs, revokePAT } = require('../controllers/pat.controller'); +const authenticateCLI = require("../middlewares/authenticateCLI"); +const {getCLIProfile,} = require("../controllers/cli.controller"); +router.get("/cli/me", authenticateCLI, getCLIProfile); router.get('/me', authorization, getMe); router.patch('/onboarding', authorization, updateOnboarding); diff --git a/package-lock.json b/package-lock.json index 2adea1bbf..823c9c49a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16416,7 +16416,6 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "axios": "^1.12.2", "chalk": "^5.6.2", "commander": "^14.0.3", "ora": "^9.0.0" From 075393a46fff0c78837b0d4aeedd55bc90892138 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 14:48:43 +0530 Subject: [PATCH 05/14] pre-ui PAT commit --- .../dashboard-api/src/routes/cli.routes.ts | 0 package-lock.json | 401 +---- sdks/urbackend-cli/README.md | 0 sdks/urbackend-cli/bin/ub.js | 6 +- sdks/urbackend-cli/dist/index.cjs | 690 +++++++- sdks/urbackend-cli/dist/index.cjs.map | 2 +- sdks/urbackend-cli/dist/index.js | 691 +++++++- sdks/urbackend-cli/dist/index.js.map | 2 +- sdks/urbackend-cli/index.ts | 1 - sdks/urbackend-cli/package-lock.json | 1509 +++++++++++++++++ sdks/urbackend-cli/package.json | 44 +- sdks/urbackend-cli/src/cli.ts | 35 - sdks/urbackend-cli/src/commands/auth/login.ts | 49 + .../urbackend-cli/src/commands/auth/logout.ts | 7 + .../urbackend-cli/src/commands/auth/whoami.ts | 46 + .../src/commands/collection/delete.ts | 67 + .../src/commands/collection/list.ts | 66 + .../src/commands/doctor/index.ts | 156 ++ sdks/urbackend-cli/src/commands/login.ts | 52 - sdks/urbackend-cli/src/commands/logout.ts | 8 - .../src/commands/project/info.ts | 58 + .../src/commands/project/list.ts | 52 + .../urbackend-cli/src/commands/project/use.ts | 71 + .../src/commands/status/index.ts | 102 ++ sdks/urbackend-cli/src/commands/whoami.ts | 29 - sdks/urbackend-cli/src/core/api.ts | 74 +- sdks/urbackend-cli/src/core/config.ts | 48 +- sdks/urbackend-cli/src/core/constants.ts | 19 +- sdks/urbackend-cli/src/core/errors.ts | 7 - sdks/urbackend-cli/src/core/logger.ts | 18 +- sdks/urbackend-cli/src/index.ts | 101 ++ .../src/services/analytics.service.ts | 56 + .../src/services/auth.service.ts | 17 +- .../src/services/collection.service.ts | 39 + .../src/services/project.service.ts | 39 + sdks/urbackend-cli/src/types/analytics.ts | 36 + sdks/urbackend-cli/src/types/auth.ts | 50 +- sdks/urbackend-cli/src/types/config.ts | 2 +- sdks/urbackend-cli/src/types/project.ts | 45 + sdks/urbackend-cli/src/utils/format.ts | 29 + sdks/urbackend-cli/src/utils/prompt.ts | 22 + sdks/urbackend-cli/src/utils/token.ts | 8 +- sdks/urbackend-cli/tsconfig.json | 26 +- sdks/urbackend-cli/tsup.config.ts | 12 +- 44 files changed, 3953 insertions(+), 839 deletions(-) rename sdks/urbackend-cli/LICENSE => apps/dashboard-api/src/routes/cli.routes.ts (100%) delete mode 100644 sdks/urbackend-cli/README.md delete mode 100644 sdks/urbackend-cli/index.ts create mode 100644 sdks/urbackend-cli/package-lock.json delete mode 100644 sdks/urbackend-cli/src/cli.ts create mode 100644 sdks/urbackend-cli/src/commands/auth/login.ts create mode 100644 sdks/urbackend-cli/src/commands/auth/logout.ts create mode 100644 sdks/urbackend-cli/src/commands/auth/whoami.ts create mode 100644 sdks/urbackend-cli/src/commands/collection/delete.ts create mode 100644 sdks/urbackend-cli/src/commands/collection/list.ts create mode 100644 sdks/urbackend-cli/src/commands/doctor/index.ts delete mode 100644 sdks/urbackend-cli/src/commands/login.ts delete mode 100644 sdks/urbackend-cli/src/commands/logout.ts create mode 100644 sdks/urbackend-cli/src/commands/project/info.ts create mode 100644 sdks/urbackend-cli/src/commands/project/list.ts create mode 100644 sdks/urbackend-cli/src/commands/project/use.ts create mode 100644 sdks/urbackend-cli/src/commands/status/index.ts delete mode 100644 sdks/urbackend-cli/src/commands/whoami.ts create mode 100644 sdks/urbackend-cli/src/index.ts create mode 100644 sdks/urbackend-cli/src/services/analytics.service.ts create mode 100644 sdks/urbackend-cli/src/services/collection.service.ts create mode 100644 sdks/urbackend-cli/src/services/project.service.ts create mode 100644 sdks/urbackend-cli/src/types/analytics.ts create mode 100644 sdks/urbackend-cli/src/types/project.ts create mode 100644 sdks/urbackend-cli/src/utils/format.ts create mode 100644 sdks/urbackend-cli/src/utils/prompt.ts diff --git a/sdks/urbackend-cli/LICENSE b/apps/dashboard-api/src/routes/cli.routes.ts similarity index 100% rename from sdks/urbackend-cli/LICENSE rename to apps/dashboard-api/src/routes/cli.routes.ts diff --git a/package-lock.json b/package-lock.json index 823c9c49a..a00ddc3e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6754,6 +6754,7 @@ }, "node_modules/cli-cursor": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" @@ -6765,18 +6766,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", - "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "5.2.0", "dev": true, @@ -8831,6 +8820,7 @@ }, "node_modules/get-east-asian-width": { "version": "1.6.0", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -9541,18 +9531,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { "version": "2.0.3", "dev": true, @@ -9682,18 +9660,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakmap": { "version": "2.0.2", "dev": true, @@ -10786,22 +10752,6 @@ "version": "4.1.1", "license": "MIT" }, - "node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update": { "version": "6.1.0", "dev": true, @@ -11876,6 +11826,7 @@ }, "node_modules/mimic-function": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -12338,83 +12289,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz", - "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.6.2", - "cli-cursor": "^5.0.0", - "cli-spinners": "^3.2.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.1.0", - "log-symbols": "^7.0.1", - "stdin-discarder": "^0.3.2", - "string-width": "^8.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", - "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -13362,6 +13236,7 @@ }, "node_modules/restore-cursor": { "version": "5.1.0", + "dev": true, "license": "MIT", "dependencies": { "onetime": "^7.0.0", @@ -13376,6 +13251,7 @@ }, "node_modules/restore-cursor/node_modules/onetime": { "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" @@ -13773,6 +13649,7 @@ }, "node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -13923,18 +13800,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stdin-discarder": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", - "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "dev": true, @@ -15739,18 +15604,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { "version": "4.4.2", "license": "MIT", @@ -16416,251 +16269,45 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "chalk": "^5.6.2", - "commander": "^14.0.3", - "ora": "^9.0.0" + "commander": "^12.1.0" }, "bin": { "ub": "bin/ub.js" }, "devDependencies": { - "@eslint/js": "^10.0.1", - "@types/node": "^25.5.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", - "prettier": "^3.8.1", + "@types/node": "^22.0.0", "tsup": "^8.5.1", - "typescript": "^5.9.3", - "typescript-eslint": "^8.57.2", - "vitest": "^1.6.0" - } - }, - "sdks/urbackend-cli/node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "sdks/urbackend-cli/node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" + "typescript": "^5.7.2" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "sdks/urbackend-cli/node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=20.0.0" } }, - "sdks/urbackend-cli/node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "sdks/urbackend-cli/node_modules/@types/node": { + "version": "22.20.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.0.tgz", + "integrity": "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==", "dev": true, "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "sdks/urbackend-cli/node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "sdks/urbackend-cli/node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", - "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "sdks/urbackend-cli/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "undici-types": "~6.21.0" } }, "sdks/urbackend-cli/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "sdks/urbackend-cli/node_modules/eslint": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", - "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", - "dev": true, + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", - "workspaces": [ - "packages/*" - ], - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "sdks/urbackend-cli/node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "sdks/urbackend-cli/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "sdks/urbackend-cli/node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "sdks/urbackend-cli/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "sdks/urbackend-cli/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==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } + "license": "MIT" }, "sdks/urbackend-react": { "name": "@urbackend/react", diff --git a/sdks/urbackend-cli/README.md b/sdks/urbackend-cli/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/sdks/urbackend-cli/bin/ub.js b/sdks/urbackend-cli/bin/ub.js index a2c488d4b..c5cc43369 100644 --- a/sdks/urbackend-cli/bin/ub.js +++ b/sdks/urbackend-cli/bin/ub.js @@ -1,3 +1,7 @@ #!/usr/bin/env node -import("../dist/index.js"); \ No newline at end of file +// Resolve the correct dist entry based on the module system +import("../dist/index.js").catch((err) => { + console.error("Failed to start urBackend CLI:", err.message); + process.exit(1); +}); diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs index 4e3c0ae1a..cb4e2fbb9 100644 --- a/sdks/urbackend-cli/dist/index.cjs +++ b/sdks/urbackend-cli/dist/index.cjs @@ -1,3 +1,4 @@ +// @urbackend/cli — Official urBackend CLI "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; @@ -22,27 +23,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge mod )); -// src/cli.ts +// src/index.ts var import_commander = require("commander"); -// src/commands/login.ts -var import_promises = require("readline/promises"); -var import_node_process = require("process"); - // src/core/config.ts var import_node_fs = __toESM(require("fs"), 1); +var import_node_path2 = __toESM(require("path"), 1); // src/core/constants.ts var import_node_os = __toESM(require("os"), 1); var import_node_path = __toESM(require("path"), 1); -var CONFIG_DIR = import_node_path.default.join( - import_node_os.default.homedir(), - ".ub" -); -var CONFIG_PATH = import_node_path.default.join( - CONFIG_DIR, - "config.json" -); +var CONFIG_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".ub"); +var CONFIG_PATH = import_node_path.default.join(CONFIG_DIR, "config.json"); var DEFAULT_API_BASE = process.env.URBACKEND_API_URL ?? "https://api.urbackend.bitbros.in"; // src/core/config.ts @@ -51,7 +43,7 @@ var DEFAULT_CONFIG = { }; function loadConfig() { if (!import_node_fs.default.existsSync(CONFIG_PATH)) { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } try { const raw = import_node_fs.default.readFileSync(CONFIG_PATH, "utf8"); @@ -60,30 +52,40 @@ function loadConfig() { ...JSON.parse(raw) }; } catch { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } } function saveConfig(config) { if (!import_node_fs.default.existsSync(CONFIG_DIR)) { import_node_fs.default.mkdirSync(CONFIG_DIR, { recursive: true }); } - import_node_fs.default.writeFileSync( - CONFIG_PATH, - JSON.stringify(config, null, 2) - ); + const tmp = CONFIG_PATH + ".tmp"; + import_node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + import_node_fs.default.renameSync(tmp, CONFIG_PATH); } function saveToken(token) { const config = loadConfig(); - saveConfig({ - ...config, - pat: token - }); + saveConfig({ ...config, pat: token }); } function clearToken() { const config = loadConfig(); delete config.pat; + delete config.currentProject; saveConfig(config); } +function getToken() { + return loadConfig().pat; +} +function saveCurrentProject(projectId) { + const config = loadConfig(); + saveConfig({ ...config, currentProject: projectId }); +} +function getCurrentProject() { + return loadConfig().currentProject; +} +function configFilePath() { + return CONFIG_PATH; +} // src/core/errors.ts var APIError = class extends Error { @@ -98,29 +100,27 @@ var APIError = class extends Error { // src/core/api.ts async function apiFetch(endpoint, options = {}) { const config = loadConfig(); - const headers = new Headers( - options.headers - ); + const headers = new Headers(options.headers); if (options.body && !headers.has("Content-Type")) { - headers.set( - "Content-Type", - "application/json" - ); + headers.set("Content-Type", "application/json"); } const token = options.token ?? config.pat; if (token) { - headers.set( - "Authorization", - `Bearer ${token}` - ); + headers.set("Authorization", `Bearer ${token}`); } - const response = await fetch( - `${config.apiBase}${endpoint}`, - { + const url = `${config.apiBase}/api${endpoint}`; + let response; + try { + response = await fetch(url, { ...options, headers - } - ); + }); + } catch { + throw new APIError( + 0, + "Unable to connect to the urBackend API. Is the server running?" + ); + } if (!response.ok) { let message = response.statusText; try { @@ -128,10 +128,7 @@ async function apiFetch(endpoint, options = {}) { message = body.message ?? body.error ?? message; } catch { } - throw new APIError( - response.status, - message - ); + throw new APIError(response.status, message); } if (response.status === 204) { return void 0; @@ -141,112 +138,625 @@ async function apiFetch(endpoint, options = {}) { // src/services/auth.service.ts async function authenticate(token) { - return apiFetch("/api/user/cli/me", { + const res = await apiFetch("/user/cli/me", { method: "GET", token }); + console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); + return res.data; } async function getProfile() { - return apiFetch("/api/user/cli/me", { + const res = await apiFetch("/user/cli/me", { method: "GET" }); + return res.data; } // src/utils/token.ts function isValidPAT(token) { - return token.startsWith("ubpat_"); + return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); +} + +// src/utils/prompt.ts +var import_promises = require("readline/promises"); +var import_node_process = require("process"); +async function prompt(question) { + const rl = (0, import_promises.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout }); + const answer = (await rl.question(question)).trim(); + rl.close(); + return answer; +} +async function confirm(question) { + const answer = await prompt(`${question} (y/n): `); + return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; +} + +// src/utils/format.ts +function formatBytes(bytes) { + if (bytes === 0) return "0 B"; + const units = ["B", "KB", "MB", "GB", "TB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + const value = (bytes / Math.pow(1024, i)).toFixed(2); + return `${value} ${units[i]}`; +} +function formatDate(iso) { + if (!iso) return "Never"; + return new Date(iso).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric" + }); +} +function label(text, width = 14) { + return text.padEnd(width); } // src/core/logger.ts -var PREFIX = "ub"; var logger = { - info(message) { - console.log(`${PREFIX}: ${message}`); - }, success(message) { console.log(`\u2713 ${message}`); }, + error(message) { + console.error(`\u2716 ${message}`); + }, warn(message) { console.warn(`\u26A0 ${message}`); }, - error(message) { - console.error(`\u2716 ${message}`); + info(message) { + console.log(message); } }; -// src/commands/login.ts +// src/commands/auth/login.ts async function loginCommand() { - const rl = (0, import_promises.createInterface)({ - input: import_node_process.stdin, - output: import_node_process.stdout - }); - const token = (await rl.question("Paste your Personal Access Token: ")).trim(); - rl.close(); + console.log("Generate a Personal Access Token from the urBackend dashboard:"); + console.log(" Settings \u2192 Access Tokens \u2192 New Token\n"); + const token = await prompt("Paste your Personal Access Token: "); if (!isValidPAT(token)) { - logger.error("Invalid Personal Access Token."); + logger.error( + "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters." + ); return; } try { const profile = await authenticate(token); saveToken(token); - logger.success("Successfully logged in."); - console.log(""); - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); + logger.success("Logged in successfully.\n"); + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); + if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } if (profile.auth.scopes.length > 0) { - console.log( - `Scopes : ${profile.auth.scopes.join(", ")}` - ); + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); } } catch (error) { if (error instanceof APIError) { - logger.error(error.message); + if (error.status === 401) { + logger.error("Invalid or expired token. Generate a new one from the dashboard."); + } else { + logger.error(error.message); + } return; } - logger.error("Unable to connect to urBackend."); + logger.error("Unable to connect to the urBackend API."); } } -// src/commands/logout.ts +// src/commands/auth/logout.ts async function logoutCommand() { clearToken(); - logger.success("Logged out successfully."); + logger.success("Logged out. Your token has been removed from this machine."); } -// src/commands/whoami.ts +// src/commands/auth/whoami.ts async function whoamiCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } try { const profile = await getProfile(); - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } + console.log(`${label("Token type")} ${profile.auth.tokenType}`); + if (profile.auth.scopes.length > 0) { + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); + } + const currentProject = getCurrentProject(); + if (currentProject) { + console.log(`${label("Project")} ${currentProject}`); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/project.service.ts +async function listProjects() { + const res = await apiFetch("/projects", { + method: "GET" + }); + return res.data ?? []; +} +async function getProject(projectId) { + const res = await apiFetch(`/projects/${projectId}`, { + method: "GET" + }); + return res.data; +} + +// src/commands/project/list.ts +async function projectListCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + try { + const projects = await listProjects(); + const currentProjectId = getCurrentProject(); + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + console.log(` +Found ${projects.length} project(s): +`); + for (const project of projects) { + const active = project._id === currentProjectId ? " (active)" : ""; + const db = formatBytes(project.databaseUsed ?? 0); + const colCount = project.collections?.length ?? 0; + console.log(` ${project.name}${active}`); + console.log(` ID ${project._id}`); + console.log(` Collections ${colCount}`); + console.log(` DB used ${db}`); + if (project.description) { + console.log(` Description ${project.description}`); + } + console.log(); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/project/use.ts +async function projectUseCommand(projectIdOrName) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + try { + const projects = await listProjects(); + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + let selectedId; + if (projectIdOrName) { + const match = projects.find( + (p) => p._id === projectIdOrName || p.name.toLowerCase() === projectIdOrName.toLowerCase() + ); + if (!match) { + logger.error(`No project found matching "${projectIdOrName}".`); + logger.info("Run 'ub project list' to see available projects."); + return; + } + selectedId = match._id; + } else { + console.log("\nAvailable projects:\n"); + projects.forEach((p, i) => { + console.log(` [${i + 1}] ${p.name} (${p._id})`); + }); + console.log(); + const answer = await prompt("Enter project number: "); + const index = parseInt(answer, 10) - 1; + if (isNaN(index) || index < 0 || index >= projects.length) { + logger.error("Invalid selection."); + return; + } + selectedId = projects[index]._id; + } + saveCurrentProject(selectedId); + const project = projects.find((p) => p._id === selectedId); + logger.success(`Switched to project: ${project.name} (${selectedId})`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/project/info.ts +async function projectInfoCommand(projectId) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const id = projectId ?? getCurrentProject(); + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(id); + console.log(` +${project.name} +`); + console.log(`${label("ID")} ${project._id}`); + if (project.description) { + console.log(`${label("Description")} ${project.description}`); + } + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + console.log(`${label("DB used")} ${formatBytes(project.databaseUsed ?? 0)}`); + console.log(`${label("Storage")} ${formatBytes(project.storageUsed ?? 0)}`); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); + if (project.collections && project.collections.length > 0) { + console.log("\nCollections:"); + for (const col of project.collections) { + const fieldCount = col.model?.length ?? 0; + console.log(` \u2022 ${col.name} (${fieldCount} field${fieldCount !== 1 ? "s" : ""})`); + } + } + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/collection/list.ts +async function collectionListCommand(projectId) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const id = projectId ?? getCurrentProject(); + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(id); + const collections = project.collections ?? []; + if (collections.length === 0) { + logger.info(`No collections in project "${project.name}".`); + return; + } + console.log(` +Collections in "${project.name}" (${collections.length}): +`); + for (const col of collections) { + const fieldCount = col.model?.length ?? 0; + const rlsMode = col.rls?.enabled ? col.rls.mode : "disabled"; + console.log(` ${col.name}`); + console.log(` ${label("Fields", 10)} ${fieldCount}`); + console.log(` ${label("RLS", 10)} ${rlsMode}`); + if (col.model && col.model.length > 0) { + const fieldSummary = col.model.slice(0, 5).map((f) => `${f.key}: ${f.type}${f.required ? "*" : ""}`).join(", "); + const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : ""; + console.log(` ${label("Schema", 10)} ${fieldSummary}${extra}`); + } + console.log(); + } + console.log(" * = required field"); + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found or access denied."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/collection.service.ts +async function deleteCollection(projectId, collectionName) { + return apiFetch( + `/projects/${projectId}/collections/${collectionName}`, + { method: "DELETE" } + ); +} + +// src/commands/collection/delete.ts +async function collectionDeleteCommand(collectionName, options) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const projectId = options.project ?? getCurrentProject(); + if (!projectId) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(projectId); + const exists = project.collections?.some((c) => c.name === collectionName); + if (!exists) { + logger.error( + `Collection "${collectionName}" not found in project "${project.name}".` + ); + return; + } + if (!options.force) { console.log( - `GitHub : ${profile.developer.githubUsername}` + ` +\u26A0 This will permanently delete the collection "${collectionName}" and ALL its data. +` ); + const ok = await confirm("Are you sure?"); + if (!ok) { + logger.info("Aborted."); + return; + } + } + await deleteCollection(projectId, collectionName); + logger.success(`Collection "${collectionName}" deleted.`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Collection or project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/analytics.service.ts +async function getGlobalStats() { + const res = await apiFetch("/analytics/stats", { + method: "GET" + }); + return res.data; +} +async function getRecentActivity() { + const res = await apiFetch("/analytics/activity", { + method: "GET" + }); + return Array.isArray(res.data) ? res.data : []; +} + +// src/commands/status/index.ts +function statusIcon(status) { + if (status < 300) return "\u2713"; + if (status < 400) return "\u2192"; + if (status < 500) return "\u26A0"; + return "\u2716"; +} +async function statusCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const currentProjectId = getCurrentProject(); + try { + const stats = await getGlobalStats(); + console.log("\n\u2500\u2500 Account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); + if (stats.planExpiresAt) { + console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); } + console.log("\n\u2500\u2500 Usage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log( + `${label("Projects")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}` + ); + console.log( + `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}` + ); + console.log( + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}` + ); console.log( - `Token Type : ${profile.auth.tokenType}` + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}` ); + console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); + console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); + if (currentProjectId) { + try { + const project = await getProject(currentProjectId); + console.log("\n\u2500\u2500 Active project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log(`${label("Name")} ${project.name}`); + console.log(`${label("ID")} ${project._id}`); + console.log( + `${label("Collections")} ${project.collections?.length ?? 0}` + ); + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + } catch { + } + } else { + console.log( + "\nTip: Run 'ub project use' to select a project and see per-project details." + ); + } + try { + const activity = await getRecentActivity(); + if (activity.length > 0) { + console.log("\n\u2500\u2500 Recent activity (last 10) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + for (const log of activity.slice(0, 10)) { + const icon = statusIcon(log.status); + const time = new Date(log.timestamp).toLocaleTimeString(); + console.log( + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}` + ); + } + } + } catch { + } + console.log(); } catch (error) { if (error instanceof APIError) { - logger.error(error.message); + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } return; } - logger.error("Unable to connect to urBackend."); + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/doctor/index.ts +async function checkApiReachable(apiBase) { + const start = Date.now(); + try { + await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5e3) }); + return Date.now() - start; + } catch { + return null; + } +} +async function doctorCommand(options = {}) { + const config = loadConfig(); + const token = getToken(); + const currentProjectId = getCurrentProject(); + const result = { + configFound: false, + patValid: false, + projectSelected: !!currentProjectId, + projectAccessible: false, + dashboardApiReachable: false, + dashboardApiLatencyMs: null, + email: null, + plan: null, + projectName: null + }; + try { + const fs2 = await import("fs"); + result.configFound = fs2.existsSync(configFilePath()); + } catch { + result.configFound = false; + } + const latency = await checkApiReachable(config.apiBase); + result.dashboardApiReachable = latency !== null; + result.dashboardApiLatencyMs = latency; + if (token) { + try { + const profile = await getProfile(); + result.patValid = true; + result.email = profile.developer.email; + result.plan = profile.developer.plan; + } catch { + result.patValid = false; + } + } + if (currentProjectId && result.patValid) { + try { + const project = await getProject(currentProjectId); + result.projectAccessible = true; + result.projectName = project.name; + } catch { + result.projectAccessible = false; + } + } + if (options.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } + console.log("\n\u2500\u2500 urBackend CLI Doctor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"); + const ok = "\u2713"; + const fail = "\u2716"; + const warn = "\u26A0"; + console.log( + ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : "Not found \u2014 run 'ub login'"}` + ); + console.log( + ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${result.dashboardApiReachable ? `Reachable (${result.dashboardApiLatencyMs}ms)` : `Unreachable \u2014 ${config.apiBase}`}` + ); + if (!token) { + console.log(` ${fail} PAT Not set \u2014 run 'ub login'`); + } else { + console.log( + ` ${result.patValid ? ok : fail} PAT ${result.patValid ? `Valid (${result.email}, ${result.plan})` : "Invalid or expired \u2014 run 'ub login'"}` + ); + } + if (!currentProjectId) { + console.log(` ${warn} Active project Not selected \u2014 run 'ub project use'`); + } else { + console.log( + ` ${result.projectAccessible ? ok : fail} Active project ${result.projectAccessible ? `${result.projectName} (${currentProjectId})` : `ID ${currentProjectId} \u2014 not found or access denied`}` + ); + } + console.log(); + const allGood = result.configFound && result.patValid && result.dashboardApiReachable && result.projectSelected && result.projectAccessible; + if (allGood) { + logger.success("Everything looks good."); + } else { + logger.warn("Some checks failed. Review the output above."); } + console.log(); } -// src/cli.ts +// src/index.ts var program = new import_commander.Command(); -program.name("ub").description("Official CLI for urBackend").version("0.1.0"); -program.command("login").description( - "Authenticate using a Personal Access Token" -).action(loginCommand); -program.command("logout").description( - "Remove the local authentication" -).action(logoutCommand); -program.command("whoami").description( - "Display the authenticated developer" -).action(whoamiCommand); -program.parse(); +program.name("ub").description("Official urBackend CLI \u2014 manage projects, schemas, and more").version("0.1.0"); +program.command("login").description("Authenticate with a Personal Access Token").action(loginCommand); +program.command("logout").description("Remove stored credentials from this machine").action(logoutCommand); +program.command("whoami").description("Show the currently authenticated developer").action(whoamiCommand); +var projectCmd = program.command("project").description("Manage urBackend projects"); +projectCmd.command("list").alias("ls").description("List all accessible projects").action(projectListCommand); +projectCmd.command("use [projectIdOrName]").description("Set the active project for subsequent commands").action(projectUseCommand); +projectCmd.command("info [projectId]").description("Show details for the active (or specified) project").action(projectInfoCommand); +var collectionCmd = program.command("collection").alias("col").description("Manage collections inside the active project"); +collectionCmd.command("list").alias("ls").description("List all collections in the active project").option("-p, --project ", "Target a specific project ID").action((options) => collectionListCommand(options.project)); +collectionCmd.command("delete ").alias("rm").description("Delete a collection and all its data").option("-f, --force", "Skip confirmation prompt").option("-p, --project ", "Target a specific project ID").action((name, options) => collectionDeleteCommand(name, options)); +program.command("status").description("Show account usage and recent API activity").action(statusCommand); +program.command("doctor").description("Run diagnostic checks on your CLI setup").option("--json", "Output results as JSON (useful for CI/AI agents)").action((options) => doctorCommand(options)); +program.parseAsync(process.argv).catch((err) => { + console.error(`\u2716 ${err.message}`); + process.exit(1); +}); //# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map index 56afc462c..f8a6d6250 100644 --- a/sdks/urbackend-cli/dist/index.cjs.map +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/core/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nimport { loginCommand } from \"./commands/login.js\";\r\nimport { logoutCommand } from \"./commands/logout.js\";\r\nimport { whoamiCommand } from \"./commands/whoami.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\r\n \"Authenticate using a Personal Access Token\",\r\n )\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\r\n \"Remove the local authentication\",\r\n )\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\r\n \"Display the authenticated developer\",\r\n )\r\n .action(whoamiCommand);\r\n\r\nprogram.parse();\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\nimport { authenticate } from \"../services/auth.service.js\";\r\nimport { saveToken } from \"../core/config.js\";\r\nimport { isValidPAT } from \"../utils/token.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function loginCommand() {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n const token = (\r\n await rl.question(\"Paste your Personal Access Token: \")\r\n ).trim();\r\n\r\n rl.close();\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\"Invalid Personal Access Token.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Successfully logged in.\");\r\n\r\n console.log(\"\");\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(\r\n `Scopes : ${profile.auth.scopes.join(\", \")}`,\r\n );\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}","import fs from \"node:fs\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport {\r\n CONFIG_DIR,\r\n CONFIG_PATH,\r\n DEFAULT_API_BASE,\r\n} from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return DEFAULT_CONFIG;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(\r\n CONFIG_PATH,\r\n JSON.stringify(config, null, 2),\r\n );\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n\r\n saveConfig({\r\n ...config,\r\n pat: token,\r\n });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n\r\n delete config.pat;\r\n\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(\r\n os.homedir(),\r\n \".ub\",\r\n);\r\n\r\nexport const CONFIG_PATH = path.join(\r\n CONFIG_DIR,\r\n \"config.json\",\r\n);\r\n\r\n/**\r\n * Default production API.\r\n *\r\n * Can be overridden during development by\r\n * URBACKEND_API_URL.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ??\r\n \"https://api.urbackend.bitbros.in\";","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n\r\nexport class ConfigError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ConfigError\";\r\n }\r\n}","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions\r\n extends RequestInit {\r\n token?: string;\r\n}\r\n\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(\r\n options.headers,\r\n );\r\n\r\n if (\r\n options.body &&\r\n !headers.has(\"Content-Type\")\r\n) {\r\n headers.set(\r\n \"Content-Type\",\r\n \"application/json\",\r\n );\r\n}\r\n const token =\r\n options.token ?? config.pat;\r\n\r\n if (token) {\r\n headers.set(\r\n \"Authorization\",\r\n `Bearer ${token}`,\r\n );\r\n }\r\n\r\n const response = await fetch(\r\n `${config.apiBase}${endpoint}`,\r\n {\r\n ...options,\r\n headers,\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = await response.json();\r\n\r\n message =\r\n body.message ??\r\n body.error ??\r\n message;\r\n } catch {}\r\n\r\n throw new APIError(\r\n response.status,\r\n message,\r\n );\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\nexport async function authenticate(\r\n token: string,\r\n): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n}","export function isValidPAT(token: string): boolean {\r\n return token.startsWith(\"ubpat_\");\r\n}","const PREFIX = \"ub\";\r\n\r\nexport const logger = {\r\n info(message: string) {\r\n console.log(`${PREFIX}: ${message}`);\r\n },\r\n\r\n success(message: string) {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n warn(message: string) {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n error(message: string) {\r\n console.error(`✖ ${message}`);\r\n },\r\n};","import { clearToken } from \"../core/config.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function logoutCommand() {\r\n clearToken();\r\n\r\n logger.success(\"Logged out successfully.\");\r\n}","import { getProfile } from \"../services/auth.service.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function whoamiCommand() {\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(\r\n `GitHub : ${profile.developer.githubUsername}`,\r\n );\r\n }\r\n\r\n console.log(\r\n `Token Type : ${profile.auth.tokenType}`,\r\n );\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}"],"mappings":"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","names":["path","os","fs"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/auth/login.js\";\nimport { logoutCommand } from \"./commands/auth/logout.js\";\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\nimport { projectListCommand } from \"./commands/project/list.js\";\nimport { projectUseCommand } from \"./commands/project/use.js\";\nimport { projectInfoCommand } from \"./commands/project/info.js\";\nimport { collectionListCommand } from \"./commands/collection/list.js\";\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\nimport { statusCommand } from \"./commands/status/index.js\";\nimport { doctorCommand } from \"./commands/doctor/index.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ub\")\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\n .version(\"0.1.0\");\n\n// ── Authentication ──────────────────────────────────────────────────────────\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with a Personal Access Token\")\n .action(loginCommand);\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials from this machine\")\n .action(logoutCommand);\n\nprogram\n .command(\"whoami\")\n .description(\"Show the currently authenticated developer\")\n .action(whoamiCommand);\n\n// ── Projects ─────────────────────────────────────────────────────────────────\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage urBackend projects\");\n\nprojectCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all accessible projects\")\n .action(projectListCommand);\n\nprojectCmd\n .command(\"use [projectIdOrName]\")\n .description(\"Set the active project for subsequent commands\")\n .action(projectUseCommand);\n\nprojectCmd\n .command(\"info [projectId]\")\n .description(\"Show details for the active (or specified) project\")\n .action(projectInfoCommand);\n\n// ── Collections ──────────────────────────────────────────────────────────────\n\nconst collectionCmd = program\n .command(\"collection\")\n .alias(\"col\")\n .description(\"Manage collections inside the active project\");\n\ncollectionCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all collections in the active project\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((options) => collectionListCommand(options.project));\n\ncollectionCmd\n .command(\"delete \")\n .alias(\"rm\")\n .description(\"Delete a collection and all its data\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((name, options) => collectionDeleteCommand(name, options));\n\n// ── Status ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"status\")\n .description(\"Show account usage and recent API activity\")\n .action(statusCommand);\n\n// ── Doctor ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"doctor\")\n .description(\"Run diagnostic checks on your CLI setup\")\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\n .action((options) => doctorCommand(options));\n\n// ── Parse ─────────────────────────────────────────────────────────────────────\n\nprogram.parseAsync(process.argv).catch((err: Error) => {\n console.error(`✖ ${err.message}`);\n process.exit(1);\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { CLIConfig } from \"../types/config.js\";\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\n\nconst DEFAULT_CONFIG: CLIConfig = {\n apiBase: DEFAULT_API_BASE,\n};\n\nexport function loadConfig(): CLIConfig {\n if (!fs.existsSync(CONFIG_PATH)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\n return {\n ...DEFAULT_CONFIG,\n ...JSON.parse(raw),\n };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(config: CLIConfig): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n\n // Atomic write: write to temp file then rename to avoid corruption on crash\n const tmp = CONFIG_PATH + \".tmp\";\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\n fs.renameSync(tmp, CONFIG_PATH);\n}\n\nexport function saveToken(token: string): void {\n const config = loadConfig();\n saveConfig({ ...config, pat: token });\n}\n\nexport function clearToken(): void {\n const config = loadConfig();\n delete config.pat;\n delete config.currentProject;\n saveConfig(config);\n}\n\nexport function getToken(): string | undefined {\n return loadConfig().pat;\n}\n\nexport function saveCurrentProject(projectId: string): void {\n const config = loadConfig();\n saveConfig({ ...config, currentProject: projectId });\n}\n\nexport function getCurrentProject(): string | undefined {\n return loadConfig().currentProject;\n}\n\nexport function configFilePath(): string {\n return CONFIG_PATH;\n}\n\nexport function configDirPath(): string {\n return path.dirname(CONFIG_PATH);\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport const CLI_NAME = \"ub\";\n\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\n\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/**\n * Default production API base URL.\n * Override via URBACKEND_API_URL env variable for local development.\n */\nexport const DEFAULT_API_BASE =\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\n","export class APIError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"APIError\";\n }\n}\n","import { loadConfig } from \"./config.js\";\nimport { APIError } from \"./errors.js\";\n\nexport interface FetchOptions extends RequestInit {\n /** Override the PAT for this single request (e.g. during login verification) */\n token?: string;\n}\n\n/**\n * Central HTTP client for all CLI → dashboard-api communication.\n * Automatically injects the stored PAT as a Bearer token.\n *\n * All dashboard-api routes are mounted under /api — this client\n * prepends that prefix automatically so callers just write /user/cli/me.\n */\nexport async function apiFetch(\n endpoint: string,\n options: FetchOptions = {},\n): Promise {\n const config = loadConfig();\n\n const headers = new Headers(options.headers);\n\n if (options.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n const token = options.token ?? config.pat;\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n\n // All dashboard-api routes are under /api\n const url = `${config.apiBase}/api${endpoint}`;\n\n let response: Response;\n\n try {\n response = await fetch(url, {\n ...options,\n headers,\n });\n } catch {\n throw new APIError(\n 0,\n \"Unable to connect to the urBackend API. Is the server running?\",\n );\n }\n\n if (!response.ok) {\n let message = response.statusText;\n\n try {\n const body = (await response.json()) as {\n message?: string;\n error?: string;\n };\n message = body.message ?? body.error ?? message;\n } catch {\n // ignore — use statusText\n }\n\n throw new APIError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}","import { apiFetch } from \"../core/api.js\";\nimport type { CLIProfile } from \"../types/auth.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function authenticate(token: string): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n token,\n });\n console.log(\"DEBUG raw response:\", JSON.stringify(res, null, 2));\n return res.data;\n}\n\nexport async function getProfile(): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n });\n return res.data;\n}","/**\n * Validates the format of a Personal Access Token.\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\n */\nexport function isValidPAT(token: string): boolean {\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\n\n/**\n * Prompts the user for a single line of input.\n * Automatically closes the readline interface after reading.\n */\nexport async function prompt(question: string): Promise {\n const rl = createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(question)).trim();\n rl.close();\n return answer;\n}\n\n/**\n * Prompts for a yes/no confirmation.\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\n */\nexport async function confirm(question: string): Promise {\n const answer = await prompt(`${question} (y/n): `);\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n","/**\n * Formats bytes into a human-readable string (KB, MB, GB).\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\n return `${value} ${units[i]}`;\n}\n\n/**\n * Formats an ISO date string to a locale-friendly short format.\n */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"Never\";\n return new Date(iso).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * Pads a string label to a fixed width for aligned terminal output.\n */\nexport function label(text: string, width = 14): string {\n return text.padEnd(width);\n}\n","export const logger = {\n success(message: string): void {\n console.log(`✓ ${message}`);\n },\n\n error(message: string): void {\n console.error(`✖ ${message}`);\n },\n\n warn(message: string): void {\n console.warn(`⚠ ${message}`);\n },\n\n info(message: string): void {\n console.log(message);\n },\n};\n","import { authenticate } from \"../../services/auth.service.js\";\nimport { saveToken } from \"../../core/config.js\";\nimport { isValidPAT } from \"../../utils/token.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function loginCommand(): Promise {\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\n console.log(\" Settings → Access Tokens → New Token\\n\");\n\n const token = await prompt(\"Paste your Personal Access Token: \");\n\n if (!isValidPAT(token)) {\n logger.error(\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\n );\n return;\n }\n\n try {\n const profile = await authenticate(token);\n\n saveToken(token);\n\n logger.success(\"Logged in successfully.\\n\");\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { clearToken } from \"../../core/config.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function logoutCommand(): Promise {\n clearToken();\n logger.success(\"Logged out. Your token has been removed from this machine.\");\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function whoamiCommand(): Promise {\n const token = getToken();\n\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const profile = await getProfile();\n\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n\n const currentProject = getCurrentProject();\n if (currentProject) {\n console.log(`${label(\"Project\")} ${currentProject}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Project, ProjectListItem } from \"../types/project.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function listProjects(): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"GET\",\n });\n return res.data ?? [];\n}\n\nexport async function getProject(projectId: string): Promise {\n const res = await apiFetch>(`/projects/${projectId}`, {\n method: \"GET\",\n });\n return res.data;\n}\n\nexport async function createProject(payload: {\n name: string;\n description?: string;\n}): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n return res.data;\n}\n\nexport async function deleteProject(projectId: string): Promise {\n return apiFetch(`/projects/${projectId}`, {\n method: \"DELETE\",\n });\n}","import { listProjects } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectListCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n const currentProjectId = getCurrentProject();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n console.log(`\\nFound ${projects.length} project(s):\\n`);\n\n for (const project of projects) {\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\n const db = formatBytes(project.databaseUsed ?? 0);\n const colCount = project.collections?.length ?? 0;\n\n console.log(` ${project.name}${active}`);\n console.log(` ID ${project._id}`);\n console.log(` Collections ${colCount}`);\n console.log(` DB used ${db}`);\n\n if (project.description) {\n console.log(` Description ${project.description}`);\n }\n\n console.log();\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { listProjects, getProject } from \"../../services/project.service.js\";\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n let selectedId: string | undefined;\n\n if (projectIdOrName) {\n // Try matching by ID or name\n const match = projects.find(\n (p) =>\n p._id === projectIdOrName ||\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\n );\n if (!match) {\n logger.error(`No project found matching \"${projectIdOrName}\".`);\n logger.info(\"Run 'ub project list' to see available projects.\");\n return;\n }\n selectedId = match._id;\n } else {\n // Interactive selection\n console.log(\"\\nAvailable projects:\\n\");\n projects.forEach((p, i) => {\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\n });\n console.log();\n\n const answer = await prompt(\"Enter project number: \");\n const index = parseInt(answer, 10) - 1;\n\n if (isNaN(index) || index < 0 || index >= projects.length) {\n logger.error(\"Invalid selection.\");\n return;\n }\n\n selectedId = projects[index]._id;\n }\n\n saveCurrentProject(selectedId);\n\n const project = projects.find((p) => p._id === selectedId)!;\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes, label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectInfoCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n\n console.log(`\\n${project.name}\\n`);\n console.log(`${label(\"ID\")} ${project._id}`);\n\n if (project.description) {\n console.log(`${label(\"Description\")} ${project.description}`);\n }\n\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\n\n if (project.collections && project.collections.length > 0) {\n console.log(\"\\nCollections:\");\n for (const col of project.collections) {\n const fieldCount = col.model?.length ?? 0;\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\n }\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function collectionListCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n const collections = project.collections ?? [];\n\n if (collections.length === 0) {\n logger.info(`No collections in project \"${project.name}\".`);\n return;\n }\n\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\n\n for (const col of collections) {\n const fieldCount = col.model?.length ?? 0;\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\n\n console.log(` ${col.name}`);\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\n\n if (col.model && col.model.length > 0) {\n const fieldSummary = col.model\n .slice(0, 5)\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\n .join(\", \");\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\n }\n\n console.log();\n }\n\n console.log(\" * = required field\");\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found or access denied.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Collection } from \"../types/project.js\";\n\ninterface CollectionResponse {\n data: Collection;\n message: string;\n}\n\n/**\n * Creates a new collection inside a project.\n * Endpoint: POST /projects/:projectId/collections\n */\nexport async function createCollection(\n projectId: string,\n payload: { name: string; model: Collection[\"model\"] },\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/collections`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n return res.data;\n}\n\n/**\n * Deletes a collection from a project.\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\n */\nexport async function deleteCollection(\n projectId: string,\n collectionName: string,\n): Promise {\n return apiFetch(\n `/projects/${projectId}/collections/${collectionName}`,\n { method: \"DELETE\" },\n );\n}\n","import { deleteCollection } from \"../../services/collection.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { confirm } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface CollectionDeleteOptions {\n force?: boolean;\n project?: string;\n}\n\nexport async function collectionDeleteCommand(\n collectionName: string,\n options: CollectionDeleteOptions,\n): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const projectId = options.project ?? getCurrentProject();\n if (!projectId) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n // Verify the collection actually exists before asking for confirmation\n const project = await getProject(projectId);\n const exists = project.collections?.some((c) => c.name === collectionName);\n\n if (!exists) {\n logger.error(\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\n );\n return;\n }\n\n if (!options.force) {\n console.log(\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\n );\n const ok = await confirm(\"Are you sure?\");\n if (!ok) {\n logger.info(\"Aborted.\");\n return;\n }\n }\n\n await deleteCollection(projectId, collectionName);\n logger.success(`Collection \"${collectionName}\" deleted.`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Collection or project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { GlobalStats, RecentActivityLog } from \"../types/analytics.js\";\n\ninterface GlobalStatsResponse {\n data: GlobalStats;\n}\n\ninterface RecentActivityResponse {\n data: RecentActivityLog[];\n}\n\ninterface ProjectAnalyticsResponse {\n data: {\n totalRequests: number;\n successRequests: number;\n errorRequests: number;\n topCollections?: { name: string; requests: number }[];\n };\n}\n\n/**\n * Fetches global usage stats across all developer projects.\n * Endpoint: GET /analytics/stats\n */\nexport async function getGlobalStats(): Promise {\n const res = await apiFetch(\"/analytics/stats\", {\n method: \"GET\",\n });\n return res.data;\n}\n\n/**\n * Fetches the 20 most recent API logs across all projects.\n * Endpoint: GET /analytics/activity\n */\nexport async function getRecentActivity(): Promise {\n const res = await apiFetch(\"/analytics/activity\", {\n method: \"GET\",\n });\n return Array.isArray(res.data) ? res.data : [];\n}\n\n/**\n * Fetches per-project analytics.\n * Endpoint: GET /projects/:projectId/analytics?range=last24h\n */\nexport async function getProjectAnalytics(\n projectId: string,\n range: \"last1h\" | \"last24h\" | \"last7d\" | \"last30d\" | \"allTime\" = \"last24h\",\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/analytics?range=${range}`,\n { method: \"GET\" },\n );\n return res.data;\n}\n","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { formatBytes, label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nfunction statusIcon(status: number): string {\n if (status < 300) return \"✓\";\n if (status < 400) return \"→\";\n if (status < 500) return \"⚠\";\n return \"✖\";\n}\n\nexport async function statusCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const currentProjectId = getCurrentProject();\n\n try {\n // Fetch global stats (works without a current project)\n const stats = await getGlobalStats();\n\n console.log(\"\\n── Account ─────────────────────────────────────\");\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\n\n if (stats.planExpiresAt) {\n console.log(`${label(\"Plan expires\")} ${formatDate(stats.planExpiresAt)}`);\n }\n\n console.log(\"\\n── Usage ────────────────────────────────────────\");\n console.log(\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\n );\n console.log(\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\n );\n console.log(\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`,\n );\n console.log(\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`,\n );\n console.log(`${label(\"Total requests\")} ${stats.usage.totalRequests.toLocaleString()}`);\n console.log(`${label(\"Total users\")} ${stats.usage.totalUsers.toLocaleString()}`);\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\n\n // Per-project detail if a project is active\n if (currentProjectId) {\n try {\n const project = await getProject(currentProjectId);\n console.log(\"\\n── Active project ───────────────────────────────\");\n console.log(`${label(\"Name\")} ${project.name}`);\n console.log(`${label(\"ID\")} ${project._id}`);\n console.log(\n `${label(\"Collections\")} ${project.collections?.length ?? 0}`,\n );\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n } catch {\n // Non-fatal — global stats already displayed\n }\n } else {\n console.log(\n \"\\nTip: Run 'ub project use' to select a project and see per-project details.\",\n );\n }\n\n // Recent activity\n try {\n const activity = await getRecentActivity();\n\n if (activity.length > 0) {\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\n for (const log of activity.slice(0, 10)) {\n const icon = statusIcon(log.status);\n const time = new Date(log.timestamp).toLocaleTimeString();\n console.log(\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`,\n );\n }\n }\n } catch {\n // Non-fatal — skip activity if it fails\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport {\n getToken,\n getCurrentProject,\n loadConfig,\n configFilePath,\n} from \"../../core/config.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\ninterface DoctorResult {\n configFound: boolean;\n patValid: boolean;\n projectSelected: boolean;\n projectAccessible: boolean;\n dashboardApiReachable: boolean;\n dashboardApiLatencyMs: number | null;\n email: string | null;\n plan: string | null;\n projectName: string | null;\n}\n\nexport interface DoctorOptions {\n json?: boolean;\n}\n\nasync function checkApiReachable(apiBase: string): Promise {\n const start = Date.now();\n try {\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\n return Date.now() - start;\n } catch {\n return null;\n }\n}\n\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\n const config = loadConfig();\n const token = getToken();\n const currentProjectId = getCurrentProject();\n\n const result: DoctorResult = {\n configFound: false,\n patValid: false,\n projectSelected: !!currentProjectId,\n projectAccessible: false,\n dashboardApiReachable: false,\n dashboardApiLatencyMs: null,\n email: null,\n plan: null,\n projectName: null,\n };\n\n // 1. Config file\n try {\n const fs = await import(\"node:fs\");\n result.configFound = fs.existsSync(configFilePath());\n } catch {\n result.configFound = false;\n }\n\n // 2. API reachability (independent of auth)\n const latency = await checkApiReachable(config.apiBase);\n result.dashboardApiReachable = latency !== null;\n result.dashboardApiLatencyMs = latency;\n\n // 3. PAT validity\n if (token) {\n try {\n const profile = await getProfile();\n result.patValid = true;\n result.email = profile.developer.email;\n result.plan = profile.developer.plan;\n } catch {\n result.patValid = false;\n }\n }\n\n // 4. Project accessibility\n if (currentProjectId && result.patValid) {\n try {\n const project = await getProject(currentProjectId);\n result.projectAccessible = true;\n result.projectName = project.name;\n } catch {\n result.projectAccessible = false;\n }\n }\n\n // Output\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\n\n const ok = \"✓\";\n const fail = \"✖\";\n const warn = \"⚠\";\n\n console.log(\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\n );\n\n console.log(\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\n result.dashboardApiReachable\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\n : `Unreachable — ${config.apiBase}`\n }`,\n );\n\n if (!token) {\n console.log(` ${fail} PAT Not set — run 'ub login'`);\n } else {\n console.log(\n ` ${result.patValid ? ok : fail} PAT ${\n result.patValid\n ? `Valid (${result.email}, ${result.plan})`\n : \"Invalid or expired — run 'ub login'\"\n }`,\n );\n }\n\n if (!currentProjectId) {\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\n } else {\n console.log(\n ` ${result.projectAccessible ? ok : fail} Active project ${\n result.projectAccessible\n ? `${result.projectName} (${currentProjectId})`\n : `ID ${currentProjectId} — not found or access denied`\n }`,\n );\n }\n\n console.log();\n\n // Summary\n const allGood =\n result.configFound &&\n result.patValid &&\n result.dashboardApiReachable &&\n result.projectSelected &&\n result.projectAccessible;\n\n if (allGood) {\n logger.success(\"Everything looks good.\");\n } else {\n logger.warn(\"Some checks failed. Review the output above.\");\n }\n\n console.log();\n}\n"],"mappings":"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","names":["import_node_path","path","os","fs","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js index 24aa01c27..e29b7c200 100644 --- a/sdks/urbackend-cli/dist/index.js +++ b/sdks/urbackend-cli/dist/index.js @@ -1,24 +1,17 @@ -// src/cli.ts -import { Command } from "commander"; +// @urbackend/cli — Official urBackend CLI -// src/commands/login.ts -import { createInterface } from "readline/promises"; -import { stdin, stdout } from "process"; +// src/index.ts +import { Command } from "commander"; // src/core/config.ts import fs from "fs"; +import path2 from "path"; // src/core/constants.ts import os from "os"; import path from "path"; -var CONFIG_DIR = path.join( - os.homedir(), - ".ub" -); -var CONFIG_PATH = path.join( - CONFIG_DIR, - "config.json" -); +var CONFIG_DIR = path.join(os.homedir(), ".ub"); +var CONFIG_PATH = path.join(CONFIG_DIR, "config.json"); var DEFAULT_API_BASE = process.env.URBACKEND_API_URL ?? "https://api.urbackend.bitbros.in"; // src/core/config.ts @@ -27,7 +20,7 @@ var DEFAULT_CONFIG = { }; function loadConfig() { if (!fs.existsSync(CONFIG_PATH)) { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } try { const raw = fs.readFileSync(CONFIG_PATH, "utf8"); @@ -36,30 +29,40 @@ function loadConfig() { ...JSON.parse(raw) }; } catch { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } } function saveConfig(config) { if (!fs.existsSync(CONFIG_DIR)) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } - fs.writeFileSync( - CONFIG_PATH, - JSON.stringify(config, null, 2) - ); + const tmp = CONFIG_PATH + ".tmp"; + fs.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + fs.renameSync(tmp, CONFIG_PATH); } function saveToken(token) { const config = loadConfig(); - saveConfig({ - ...config, - pat: token - }); + saveConfig({ ...config, pat: token }); } function clearToken() { const config = loadConfig(); delete config.pat; + delete config.currentProject; saveConfig(config); } +function getToken() { + return loadConfig().pat; +} +function saveCurrentProject(projectId) { + const config = loadConfig(); + saveConfig({ ...config, currentProject: projectId }); +} +function getCurrentProject() { + return loadConfig().currentProject; +} +function configFilePath() { + return CONFIG_PATH; +} // src/core/errors.ts var APIError = class extends Error { @@ -74,29 +77,27 @@ var APIError = class extends Error { // src/core/api.ts async function apiFetch(endpoint, options = {}) { const config = loadConfig(); - const headers = new Headers( - options.headers - ); + const headers = new Headers(options.headers); if (options.body && !headers.has("Content-Type")) { - headers.set( - "Content-Type", - "application/json" - ); + headers.set("Content-Type", "application/json"); } const token = options.token ?? config.pat; if (token) { - headers.set( - "Authorization", - `Bearer ${token}` - ); + headers.set("Authorization", `Bearer ${token}`); } - const response = await fetch( - `${config.apiBase}${endpoint}`, - { + const url = `${config.apiBase}/api${endpoint}`; + let response; + try { + response = await fetch(url, { ...options, headers - } - ); + }); + } catch { + throw new APIError( + 0, + "Unable to connect to the urBackend API. Is the server running?" + ); + } if (!response.ok) { let message = response.statusText; try { @@ -104,10 +105,7 @@ async function apiFetch(endpoint, options = {}) { message = body.message ?? body.error ?? message; } catch { } - throw new APIError( - response.status, - message - ); + throw new APIError(response.status, message); } if (response.status === 204) { return void 0; @@ -117,112 +115,625 @@ async function apiFetch(endpoint, options = {}) { // src/services/auth.service.ts async function authenticate(token) { - return apiFetch("/api/user/cli/me", { + const res = await apiFetch("/user/cli/me", { method: "GET", token }); + console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); + return res.data; } async function getProfile() { - return apiFetch("/api/user/cli/me", { + const res = await apiFetch("/user/cli/me", { method: "GET" }); + return res.data; } // src/utils/token.ts function isValidPAT(token) { - return token.startsWith("ubpat_"); + return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); +} + +// src/utils/prompt.ts +import { createInterface } from "readline/promises"; +import { stdin, stdout } from "process"; +async function prompt(question) { + const rl = createInterface({ input: stdin, output: stdout }); + const answer = (await rl.question(question)).trim(); + rl.close(); + return answer; +} +async function confirm(question) { + const answer = await prompt(`${question} (y/n): `); + return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; +} + +// src/utils/format.ts +function formatBytes(bytes) { + if (bytes === 0) return "0 B"; + const units = ["B", "KB", "MB", "GB", "TB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + const value = (bytes / Math.pow(1024, i)).toFixed(2); + return `${value} ${units[i]}`; +} +function formatDate(iso) { + if (!iso) return "Never"; + return new Date(iso).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric" + }); +} +function label(text, width = 14) { + return text.padEnd(width); } // src/core/logger.ts -var PREFIX = "ub"; var logger = { - info(message) { - console.log(`${PREFIX}: ${message}`); - }, success(message) { console.log(`\u2713 ${message}`); }, + error(message) { + console.error(`\u2716 ${message}`); + }, warn(message) { console.warn(`\u26A0 ${message}`); }, - error(message) { - console.error(`\u2716 ${message}`); + info(message) { + console.log(message); } }; -// src/commands/login.ts +// src/commands/auth/login.ts async function loginCommand() { - const rl = createInterface({ - input: stdin, - output: stdout - }); - const token = (await rl.question("Paste your Personal Access Token: ")).trim(); - rl.close(); + console.log("Generate a Personal Access Token from the urBackend dashboard:"); + console.log(" Settings \u2192 Access Tokens \u2192 New Token\n"); + const token = await prompt("Paste your Personal Access Token: "); if (!isValidPAT(token)) { - logger.error("Invalid Personal Access Token."); + logger.error( + "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters." + ); return; } try { const profile = await authenticate(token); saveToken(token); - logger.success("Successfully logged in."); - console.log(""); - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); + logger.success("Logged in successfully.\n"); + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); + if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } if (profile.auth.scopes.length > 0) { - console.log( - `Scopes : ${profile.auth.scopes.join(", ")}` - ); + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); } } catch (error) { if (error instanceof APIError) { - logger.error(error.message); + if (error.status === 401) { + logger.error("Invalid or expired token. Generate a new one from the dashboard."); + } else { + logger.error(error.message); + } return; } - logger.error("Unable to connect to urBackend."); + logger.error("Unable to connect to the urBackend API."); } } -// src/commands/logout.ts +// src/commands/auth/logout.ts async function logoutCommand() { clearToken(); - logger.success("Logged out successfully."); + logger.success("Logged out. Your token has been removed from this machine."); } -// src/commands/whoami.ts +// src/commands/auth/whoami.ts async function whoamiCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } try { const profile = await getProfile(); - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } + console.log(`${label("Token type")} ${profile.auth.tokenType}`); + if (profile.auth.scopes.length > 0) { + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); + } + const currentProject = getCurrentProject(); + if (currentProject) { + console.log(`${label("Project")} ${currentProject}`); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/project.service.ts +async function listProjects() { + const res = await apiFetch("/projects", { + method: "GET" + }); + return res.data ?? []; +} +async function getProject(projectId) { + const res = await apiFetch(`/projects/${projectId}`, { + method: "GET" + }); + return res.data; +} + +// src/commands/project/list.ts +async function projectListCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + try { + const projects = await listProjects(); + const currentProjectId = getCurrentProject(); + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + console.log(` +Found ${projects.length} project(s): +`); + for (const project of projects) { + const active = project._id === currentProjectId ? " (active)" : ""; + const db = formatBytes(project.databaseUsed ?? 0); + const colCount = project.collections?.length ?? 0; + console.log(` ${project.name}${active}`); + console.log(` ID ${project._id}`); + console.log(` Collections ${colCount}`); + console.log(` DB used ${db}`); + if (project.description) { + console.log(` Description ${project.description}`); + } + console.log(); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/project/use.ts +async function projectUseCommand(projectIdOrName) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + try { + const projects = await listProjects(); + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + let selectedId; + if (projectIdOrName) { + const match = projects.find( + (p) => p._id === projectIdOrName || p.name.toLowerCase() === projectIdOrName.toLowerCase() + ); + if (!match) { + logger.error(`No project found matching "${projectIdOrName}".`); + logger.info("Run 'ub project list' to see available projects."); + return; + } + selectedId = match._id; + } else { + console.log("\nAvailable projects:\n"); + projects.forEach((p, i) => { + console.log(` [${i + 1}] ${p.name} (${p._id})`); + }); + console.log(); + const answer = await prompt("Enter project number: "); + const index = parseInt(answer, 10) - 1; + if (isNaN(index) || index < 0 || index >= projects.length) { + logger.error("Invalid selection."); + return; + } + selectedId = projects[index]._id; + } + saveCurrentProject(selectedId); + const project = projects.find((p) => p._id === selectedId); + logger.success(`Switched to project: ${project.name} (${selectedId})`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/project/info.ts +async function projectInfoCommand(projectId) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const id = projectId ?? getCurrentProject(); + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(id); + console.log(` +${project.name} +`); + console.log(`${label("ID")} ${project._id}`); + if (project.description) { + console.log(`${label("Description")} ${project.description}`); + } + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + console.log(`${label("DB used")} ${formatBytes(project.databaseUsed ?? 0)}`); + console.log(`${label("Storage")} ${formatBytes(project.storageUsed ?? 0)}`); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); + if (project.collections && project.collections.length > 0) { + console.log("\nCollections:"); + for (const col of project.collections) { + const fieldCount = col.model?.length ?? 0; + console.log(` \u2022 ${col.name} (${fieldCount} field${fieldCount !== 1 ? "s" : ""})`); + } + } + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/collection/list.ts +async function collectionListCommand(projectId) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const id = projectId ?? getCurrentProject(); + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(id); + const collections = project.collections ?? []; + if (collections.length === 0) { + logger.info(`No collections in project "${project.name}".`); + return; + } + console.log(` +Collections in "${project.name}" (${collections.length}): +`); + for (const col of collections) { + const fieldCount = col.model?.length ?? 0; + const rlsMode = col.rls?.enabled ? col.rls.mode : "disabled"; + console.log(` ${col.name}`); + console.log(` ${label("Fields", 10)} ${fieldCount}`); + console.log(` ${label("RLS", 10)} ${rlsMode}`); + if (col.model && col.model.length > 0) { + const fieldSummary = col.model.slice(0, 5).map((f) => `${f.key}: ${f.type}${f.required ? "*" : ""}`).join(", "); + const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : ""; + console.log(` ${label("Schema", 10)} ${fieldSummary}${extra}`); + } + console.log(); + } + console.log(" * = required field"); + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found or access denied."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/collection.service.ts +async function deleteCollection(projectId, collectionName) { + return apiFetch( + `/projects/${projectId}/collections/${collectionName}`, + { method: "DELETE" } + ); +} + +// src/commands/collection/delete.ts +async function collectionDeleteCommand(collectionName, options) { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const projectId = options.project ?? getCurrentProject(); + if (!projectId) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + try { + const project = await getProject(projectId); + const exists = project.collections?.some((c) => c.name === collectionName); + if (!exists) { + logger.error( + `Collection "${collectionName}" not found in project "${project.name}".` + ); + return; + } + if (!options.force) { console.log( - `GitHub : ${profile.developer.githubUsername}` + ` +\u26A0 This will permanently delete the collection "${collectionName}" and ALL its data. +` ); + const ok = await confirm("Are you sure?"); + if (!ok) { + logger.info("Aborted."); + return; + } + } + await deleteCollection(projectId, collectionName); + logger.success(`Collection "${collectionName}" deleted.`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Collection or project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/services/analytics.service.ts +async function getGlobalStats() { + const res = await apiFetch("/analytics/stats", { + method: "GET" + }); + return res.data; +} +async function getRecentActivity() { + const res = await apiFetch("/analytics/activity", { + method: "GET" + }); + return Array.isArray(res.data) ? res.data : []; +} + +// src/commands/status/index.ts +function statusIcon(status) { + if (status < 300) return "\u2713"; + if (status < 400) return "\u2192"; + if (status < 500) return "\u26A0"; + return "\u2716"; +} +async function statusCommand() { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + const currentProjectId = getCurrentProject(); + try { + const stats = await getGlobalStats(); + console.log("\n\u2500\u2500 Account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); + if (stats.planExpiresAt) { + console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); } + console.log("\n\u2500\u2500 Usage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log( + `${label("Projects")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}` + ); + console.log( + `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}` + ); + console.log( + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}` + ); console.log( - `Token Type : ${profile.auth.tokenType}` + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}` ); + console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); + console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); + if (currentProjectId) { + try { + const project = await getProject(currentProjectId); + console.log("\n\u2500\u2500 Active project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + console.log(`${label("Name")} ${project.name}`); + console.log(`${label("ID")} ${project._id}`); + console.log( + `${label("Collections")} ${project.collections?.length ?? 0}` + ); + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + } catch { + } + } else { + console.log( + "\nTip: Run 'ub project use' to select a project and see per-project details." + ); + } + try { + const activity = await getRecentActivity(); + if (activity.length > 0) { + console.log("\n\u2500\u2500 Recent activity (last 10) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); + for (const log of activity.slice(0, 10)) { + const icon = statusIcon(log.status); + const time = new Date(log.timestamp).toLocaleTimeString(); + console.log( + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}` + ); + } + } + } catch { + } + console.log(); } catch (error) { if (error instanceof APIError) { - logger.error(error.message); + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } return; } - logger.error("Unable to connect to urBackend."); + logger.error("Unable to connect to the urBackend API."); + } +} + +// src/commands/doctor/index.ts +async function checkApiReachable(apiBase) { + const start = Date.now(); + try { + await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5e3) }); + return Date.now() - start; + } catch { + return null; + } +} +async function doctorCommand(options = {}) { + const config = loadConfig(); + const token = getToken(); + const currentProjectId = getCurrentProject(); + const result = { + configFound: false, + patValid: false, + projectSelected: !!currentProjectId, + projectAccessible: false, + dashboardApiReachable: false, + dashboardApiLatencyMs: null, + email: null, + plan: null, + projectName: null + }; + try { + const fs2 = await import("fs"); + result.configFound = fs2.existsSync(configFilePath()); + } catch { + result.configFound = false; + } + const latency = await checkApiReachable(config.apiBase); + result.dashboardApiReachable = latency !== null; + result.dashboardApiLatencyMs = latency; + if (token) { + try { + const profile = await getProfile(); + result.patValid = true; + result.email = profile.developer.email; + result.plan = profile.developer.plan; + } catch { + result.patValid = false; + } + } + if (currentProjectId && result.patValid) { + try { + const project = await getProject(currentProjectId); + result.projectAccessible = true; + result.projectName = project.name; + } catch { + result.projectAccessible = false; + } + } + if (options.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } + console.log("\n\u2500\u2500 urBackend CLI Doctor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"); + const ok = "\u2713"; + const fail = "\u2716"; + const warn = "\u26A0"; + console.log( + ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : "Not found \u2014 run 'ub login'"}` + ); + console.log( + ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${result.dashboardApiReachable ? `Reachable (${result.dashboardApiLatencyMs}ms)` : `Unreachable \u2014 ${config.apiBase}`}` + ); + if (!token) { + console.log(` ${fail} PAT Not set \u2014 run 'ub login'`); + } else { + console.log( + ` ${result.patValid ? ok : fail} PAT ${result.patValid ? `Valid (${result.email}, ${result.plan})` : "Invalid or expired \u2014 run 'ub login'"}` + ); + } + if (!currentProjectId) { + console.log(` ${warn} Active project Not selected \u2014 run 'ub project use'`); + } else { + console.log( + ` ${result.projectAccessible ? ok : fail} Active project ${result.projectAccessible ? `${result.projectName} (${currentProjectId})` : `ID ${currentProjectId} \u2014 not found or access denied`}` + ); + } + console.log(); + const allGood = result.configFound && result.patValid && result.dashboardApiReachable && result.projectSelected && result.projectAccessible; + if (allGood) { + logger.success("Everything looks good."); + } else { + logger.warn("Some checks failed. Review the output above."); } + console.log(); } -// src/cli.ts +// src/index.ts var program = new Command(); -program.name("ub").description("Official CLI for urBackend").version("0.1.0"); -program.command("login").description( - "Authenticate using a Personal Access Token" -).action(loginCommand); -program.command("logout").description( - "Remove the local authentication" -).action(logoutCommand); -program.command("whoami").description( - "Display the authenticated developer" -).action(whoamiCommand); -program.parse(); +program.name("ub").description("Official urBackend CLI \u2014 manage projects, schemas, and more").version("0.1.0"); +program.command("login").description("Authenticate with a Personal Access Token").action(loginCommand); +program.command("logout").description("Remove stored credentials from this machine").action(logoutCommand); +program.command("whoami").description("Show the currently authenticated developer").action(whoamiCommand); +var projectCmd = program.command("project").description("Manage urBackend projects"); +projectCmd.command("list").alias("ls").description("List all accessible projects").action(projectListCommand); +projectCmd.command("use [projectIdOrName]").description("Set the active project for subsequent commands").action(projectUseCommand); +projectCmd.command("info [projectId]").description("Show details for the active (or specified) project").action(projectInfoCommand); +var collectionCmd = program.command("collection").alias("col").description("Manage collections inside the active project"); +collectionCmd.command("list").alias("ls").description("List all collections in the active project").option("-p, --project ", "Target a specific project ID").action((options) => collectionListCommand(options.project)); +collectionCmd.command("delete ").alias("rm").description("Delete a collection and all its data").option("-f, --force", "Skip confirmation prompt").option("-p, --project ", "Target a specific project ID").action((name, options) => collectionDeleteCommand(name, options)); +program.command("status").description("Show account usage and recent API activity").action(statusCommand); +program.command("doctor").description("Run diagnostic checks on your CLI setup").option("--json", "Output results as JSON (useful for CI/AI agents)").action((options) => doctorCommand(options)); +program.parseAsync(process.argv).catch((err) => { + console.error(`\u2716 ${err.message}`); + process.exit(1); +}); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map index 226dbc73c..9d93b94d0 100644 --- a/sdks/urbackend-cli/dist/index.js.map +++ b/sdks/urbackend-cli/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/core/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\r\n\r\nimport { loginCommand } from \"./commands/login.js\";\r\nimport { logoutCommand } from \"./commands/logout.js\";\r\nimport { whoamiCommand } from \"./commands/whoami.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official CLI for urBackend\")\r\n .version(\"0.1.0\");\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\r\n \"Authenticate using a Personal Access Token\",\r\n )\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\r\n \"Remove the local authentication\",\r\n )\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\r\n \"Display the authenticated developer\",\r\n )\r\n .action(whoamiCommand);\r\n\r\nprogram.parse();\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\nimport { authenticate } from \"../services/auth.service.js\";\r\nimport { saveToken } from \"../core/config.js\";\r\nimport { isValidPAT } from \"../utils/token.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function loginCommand() {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n const token = (\r\n await rl.question(\"Paste your Personal Access Token: \")\r\n ).trim();\r\n\r\n rl.close();\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\"Invalid Personal Access Token.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Successfully logged in.\");\r\n\r\n console.log(\"\");\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(\r\n `Scopes : ${profile.auth.scopes.join(\", \")}`,\r\n );\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}","import fs from \"node:fs\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport {\r\n CONFIG_DIR,\r\n CONFIG_PATH,\r\n DEFAULT_API_BASE,\r\n} from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return DEFAULT_CONFIG;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(\r\n CONFIG_PATH,\r\n JSON.stringify(config, null, 2),\r\n );\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n\r\n saveConfig({\r\n ...config,\r\n pat: token,\r\n });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n\r\n delete config.pat;\r\n\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(\r\n os.homedir(),\r\n \".ub\",\r\n);\r\n\r\nexport const CONFIG_PATH = path.join(\r\n CONFIG_DIR,\r\n \"config.json\",\r\n);\r\n\r\n/**\r\n * Default production API.\r\n *\r\n * Can be overridden during development by\r\n * URBACKEND_API_URL.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ??\r\n \"https://api.urbackend.bitbros.in\";","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n\r\nexport class ConfigError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ConfigError\";\r\n }\r\n}","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions\r\n extends RequestInit {\r\n token?: string;\r\n}\r\n\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(\r\n options.headers,\r\n );\r\n\r\n if (\r\n options.body &&\r\n !headers.has(\"Content-Type\")\r\n) {\r\n headers.set(\r\n \"Content-Type\",\r\n \"application/json\",\r\n );\r\n}\r\n const token =\r\n options.token ?? config.pat;\r\n\r\n if (token) {\r\n headers.set(\r\n \"Authorization\",\r\n `Bearer ${token}`,\r\n );\r\n }\r\n\r\n const response = await fetch(\r\n `${config.apiBase}${endpoint}`,\r\n {\r\n ...options,\r\n headers,\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = await response.json();\r\n\r\n message =\r\n body.message ??\r\n body.error ??\r\n message;\r\n } catch {}\r\n\r\n throw new APIError(\r\n response.status,\r\n message,\r\n );\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\nexport async function authenticate(\r\n token: string,\r\n): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n return apiFetch(\"/api/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n}","export function isValidPAT(token: string): boolean {\r\n return token.startsWith(\"ubpat_\");\r\n}","const PREFIX = \"ub\";\r\n\r\nexport const logger = {\r\n info(message: string) {\r\n console.log(`${PREFIX}: ${message}`);\r\n },\r\n\r\n success(message: string) {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n warn(message: string) {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n error(message: string) {\r\n console.error(`✖ ${message}`);\r\n },\r\n};","import { clearToken } from \"../core/config.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function logoutCommand() {\r\n clearToken();\r\n\r\n logger.success(\"Logged out successfully.\");\r\n}","import { getProfile } from \"../services/auth.service.js\";\r\nimport { APIError } from \"../core/errors.js\";\r\nimport { logger } from \"../core/logger.js\";\r\n\r\nexport async function whoamiCommand() {\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`Email : ${profile.developer.email}`);\r\n console.log(`Plan : ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(\r\n `GitHub : ${profile.developer.githubUsername}`,\r\n );\r\n }\r\n\r\n console.log(\r\n `Token Type : ${profile.auth.tokenType}`,\r\n );\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n logger.error(error.message);\r\n return;\r\n }\r\n\r\n logger.error(\"Unable to connect to urBackend.\");\r\n }\r\n}"],"mappings":"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","names":[]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/auth/login.js\";\nimport { logoutCommand } from \"./commands/auth/logout.js\";\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\nimport { projectListCommand } from \"./commands/project/list.js\";\nimport { projectUseCommand } from \"./commands/project/use.js\";\nimport { projectInfoCommand } from \"./commands/project/info.js\";\nimport { collectionListCommand } from \"./commands/collection/list.js\";\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\nimport { statusCommand } from \"./commands/status/index.js\";\nimport { doctorCommand } from \"./commands/doctor/index.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ub\")\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\n .version(\"0.1.0\");\n\n// ── Authentication ──────────────────────────────────────────────────────────\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with a Personal Access Token\")\n .action(loginCommand);\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials from this machine\")\n .action(logoutCommand);\n\nprogram\n .command(\"whoami\")\n .description(\"Show the currently authenticated developer\")\n .action(whoamiCommand);\n\n// ── Projects ─────────────────────────────────────────────────────────────────\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage urBackend projects\");\n\nprojectCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all accessible projects\")\n .action(projectListCommand);\n\nprojectCmd\n .command(\"use [projectIdOrName]\")\n .description(\"Set the active project for subsequent commands\")\n .action(projectUseCommand);\n\nprojectCmd\n .command(\"info [projectId]\")\n .description(\"Show details for the active (or specified) project\")\n .action(projectInfoCommand);\n\n// ── Collections ──────────────────────────────────────────────────────────────\n\nconst collectionCmd = program\n .command(\"collection\")\n .alias(\"col\")\n .description(\"Manage collections inside the active project\");\n\ncollectionCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all collections in the active project\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((options) => collectionListCommand(options.project));\n\ncollectionCmd\n .command(\"delete \")\n .alias(\"rm\")\n .description(\"Delete a collection and all its data\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((name, options) => collectionDeleteCommand(name, options));\n\n// ── Status ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"status\")\n .description(\"Show account usage and recent API activity\")\n .action(statusCommand);\n\n// ── Doctor ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"doctor\")\n .description(\"Run diagnostic checks on your CLI setup\")\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\n .action((options) => doctorCommand(options));\n\n// ── Parse ─────────────────────────────────────────────────────────────────────\n\nprogram.parseAsync(process.argv).catch((err: Error) => {\n console.error(`✖ ${err.message}`);\n process.exit(1);\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { CLIConfig } from \"../types/config.js\";\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\n\nconst DEFAULT_CONFIG: CLIConfig = {\n apiBase: DEFAULT_API_BASE,\n};\n\nexport function loadConfig(): CLIConfig {\n if (!fs.existsSync(CONFIG_PATH)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\n return {\n ...DEFAULT_CONFIG,\n ...JSON.parse(raw),\n };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(config: CLIConfig): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n\n // Atomic write: write to temp file then rename to avoid corruption on crash\n const tmp = CONFIG_PATH + \".tmp\";\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\n fs.renameSync(tmp, CONFIG_PATH);\n}\n\nexport function saveToken(token: string): void {\n const config = loadConfig();\n saveConfig({ ...config, pat: token });\n}\n\nexport function clearToken(): void {\n const config = loadConfig();\n delete config.pat;\n delete config.currentProject;\n saveConfig(config);\n}\n\nexport function getToken(): string | undefined {\n return loadConfig().pat;\n}\n\nexport function saveCurrentProject(projectId: string): void {\n const config = loadConfig();\n saveConfig({ ...config, currentProject: projectId });\n}\n\nexport function getCurrentProject(): string | undefined {\n return loadConfig().currentProject;\n}\n\nexport function configFilePath(): string {\n return CONFIG_PATH;\n}\n\nexport function configDirPath(): string {\n return path.dirname(CONFIG_PATH);\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport const CLI_NAME = \"ub\";\n\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\n\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/**\n * Default production API base URL.\n * Override via URBACKEND_API_URL env variable for local development.\n */\nexport const DEFAULT_API_BASE =\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\n","export class APIError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"APIError\";\n }\n}\n","import { loadConfig } from \"./config.js\";\nimport { APIError } from \"./errors.js\";\n\nexport interface FetchOptions extends RequestInit {\n /** Override the PAT for this single request (e.g. during login verification) */\n token?: string;\n}\n\n/**\n * Central HTTP client for all CLI → dashboard-api communication.\n * Automatically injects the stored PAT as a Bearer token.\n *\n * All dashboard-api routes are mounted under /api — this client\n * prepends that prefix automatically so callers just write /user/cli/me.\n */\nexport async function apiFetch(\n endpoint: string,\n options: FetchOptions = {},\n): Promise {\n const config = loadConfig();\n\n const headers = new Headers(options.headers);\n\n if (options.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n const token = options.token ?? config.pat;\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n\n // All dashboard-api routes are under /api\n const url = `${config.apiBase}/api${endpoint}`;\n\n let response: Response;\n\n try {\n response = await fetch(url, {\n ...options,\n headers,\n });\n } catch {\n throw new APIError(\n 0,\n \"Unable to connect to the urBackend API. Is the server running?\",\n );\n }\n\n if (!response.ok) {\n let message = response.statusText;\n\n try {\n const body = (await response.json()) as {\n message?: string;\n error?: string;\n };\n message = body.message ?? body.error ?? message;\n } catch {\n // ignore — use statusText\n }\n\n throw new APIError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}","import { apiFetch } from \"../core/api.js\";\nimport type { CLIProfile } from \"../types/auth.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function authenticate(token: string): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n token,\n });\n console.log(\"DEBUG raw response:\", JSON.stringify(res, null, 2));\n return res.data;\n}\n\nexport async function getProfile(): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n });\n return res.data;\n}","/**\n * Validates the format of a Personal Access Token.\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\n */\nexport function isValidPAT(token: string): boolean {\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\n\n/**\n * Prompts the user for a single line of input.\n * Automatically closes the readline interface after reading.\n */\nexport async function prompt(question: string): Promise {\n const rl = createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(question)).trim();\n rl.close();\n return answer;\n}\n\n/**\n * Prompts for a yes/no confirmation.\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\n */\nexport async function confirm(question: string): Promise {\n const answer = await prompt(`${question} (y/n): `);\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n","/**\n * Formats bytes into a human-readable string (KB, MB, GB).\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\n return `${value} ${units[i]}`;\n}\n\n/**\n * Formats an ISO date string to a locale-friendly short format.\n */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"Never\";\n return new Date(iso).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * Pads a string label to a fixed width for aligned terminal output.\n */\nexport function label(text: string, width = 14): string {\n return text.padEnd(width);\n}\n","export const logger = {\n success(message: string): void {\n console.log(`✓ ${message}`);\n },\n\n error(message: string): void {\n console.error(`✖ ${message}`);\n },\n\n warn(message: string): void {\n console.warn(`⚠ ${message}`);\n },\n\n info(message: string): void {\n console.log(message);\n },\n};\n","import { authenticate } from \"../../services/auth.service.js\";\nimport { saveToken } from \"../../core/config.js\";\nimport { isValidPAT } from \"../../utils/token.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function loginCommand(): Promise {\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\n console.log(\" Settings → Access Tokens → New Token\\n\");\n\n const token = await prompt(\"Paste your Personal Access Token: \");\n\n if (!isValidPAT(token)) {\n logger.error(\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\n );\n return;\n }\n\n try {\n const profile = await authenticate(token);\n\n saveToken(token);\n\n logger.success(\"Logged in successfully.\\n\");\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { clearToken } from \"../../core/config.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function logoutCommand(): Promise {\n clearToken();\n logger.success(\"Logged out. Your token has been removed from this machine.\");\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function whoamiCommand(): Promise {\n const token = getToken();\n\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const profile = await getProfile();\n\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n\n const currentProject = getCurrentProject();\n if (currentProject) {\n console.log(`${label(\"Project\")} ${currentProject}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Project, ProjectListItem } from \"../types/project.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function listProjects(): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"GET\",\n });\n return res.data ?? [];\n}\n\nexport async function getProject(projectId: string): Promise {\n const res = await apiFetch>(`/projects/${projectId}`, {\n method: \"GET\",\n });\n return res.data;\n}\n\nexport async function createProject(payload: {\n name: string;\n description?: string;\n}): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n return res.data;\n}\n\nexport async function deleteProject(projectId: string): Promise {\n return apiFetch(`/projects/${projectId}`, {\n method: \"DELETE\",\n });\n}","import { listProjects } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectListCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n const currentProjectId = getCurrentProject();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n console.log(`\\nFound ${projects.length} project(s):\\n`);\n\n for (const project of projects) {\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\n const db = formatBytes(project.databaseUsed ?? 0);\n const colCount = project.collections?.length ?? 0;\n\n console.log(` ${project.name}${active}`);\n console.log(` ID ${project._id}`);\n console.log(` Collections ${colCount}`);\n console.log(` DB used ${db}`);\n\n if (project.description) {\n console.log(` Description ${project.description}`);\n }\n\n console.log();\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { listProjects, getProject } from \"../../services/project.service.js\";\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n let selectedId: string | undefined;\n\n if (projectIdOrName) {\n // Try matching by ID or name\n const match = projects.find(\n (p) =>\n p._id === projectIdOrName ||\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\n );\n if (!match) {\n logger.error(`No project found matching \"${projectIdOrName}\".`);\n logger.info(\"Run 'ub project list' to see available projects.\");\n return;\n }\n selectedId = match._id;\n } else {\n // Interactive selection\n console.log(\"\\nAvailable projects:\\n\");\n projects.forEach((p, i) => {\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\n });\n console.log();\n\n const answer = await prompt(\"Enter project number: \");\n const index = parseInt(answer, 10) - 1;\n\n if (isNaN(index) || index < 0 || index >= projects.length) {\n logger.error(\"Invalid selection.\");\n return;\n }\n\n selectedId = projects[index]._id;\n }\n\n saveCurrentProject(selectedId);\n\n const project = projects.find((p) => p._id === selectedId)!;\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes, label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectInfoCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n\n console.log(`\\n${project.name}\\n`);\n console.log(`${label(\"ID\")} ${project._id}`);\n\n if (project.description) {\n console.log(`${label(\"Description\")} ${project.description}`);\n }\n\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\n\n if (project.collections && project.collections.length > 0) {\n console.log(\"\\nCollections:\");\n for (const col of project.collections) {\n const fieldCount = col.model?.length ?? 0;\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\n }\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function collectionListCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n const collections = project.collections ?? [];\n\n if (collections.length === 0) {\n logger.info(`No collections in project \"${project.name}\".`);\n return;\n }\n\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\n\n for (const col of collections) {\n const fieldCount = col.model?.length ?? 0;\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\n\n console.log(` ${col.name}`);\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\n\n if (col.model && col.model.length > 0) {\n const fieldSummary = col.model\n .slice(0, 5)\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\n .join(\", \");\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\n }\n\n console.log();\n }\n\n console.log(\" * = required field\");\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found or access denied.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Collection } from \"../types/project.js\";\n\ninterface CollectionResponse {\n data: Collection;\n message: string;\n}\n\n/**\n * Creates a new collection inside a project.\n * Endpoint: POST /projects/:projectId/collections\n */\nexport async function createCollection(\n projectId: string,\n payload: { name: string; model: Collection[\"model\"] },\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/collections`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n return res.data;\n}\n\n/**\n * Deletes a collection from a project.\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\n */\nexport async function deleteCollection(\n projectId: string,\n collectionName: string,\n): Promise {\n return apiFetch(\n `/projects/${projectId}/collections/${collectionName}`,\n { method: \"DELETE\" },\n );\n}\n","import { deleteCollection } from \"../../services/collection.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { confirm } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface CollectionDeleteOptions {\n force?: boolean;\n project?: string;\n}\n\nexport async function collectionDeleteCommand(\n collectionName: string,\n options: CollectionDeleteOptions,\n): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const projectId = options.project ?? getCurrentProject();\n if (!projectId) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n // Verify the collection actually exists before asking for confirmation\n const project = await getProject(projectId);\n const exists = project.collections?.some((c) => c.name === collectionName);\n\n if (!exists) {\n logger.error(\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\n );\n return;\n }\n\n if (!options.force) {\n console.log(\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\n );\n const ok = await confirm(\"Are you sure?\");\n if (!ok) {\n logger.info(\"Aborted.\");\n return;\n }\n }\n\n await deleteCollection(projectId, collectionName);\n logger.success(`Collection \"${collectionName}\" deleted.`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Collection or project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { GlobalStats, RecentActivityLog } from \"../types/analytics.js\";\n\ninterface GlobalStatsResponse {\n data: GlobalStats;\n}\n\ninterface RecentActivityResponse {\n data: RecentActivityLog[];\n}\n\ninterface ProjectAnalyticsResponse {\n data: {\n totalRequests: number;\n successRequests: number;\n errorRequests: number;\n topCollections?: { name: string; requests: number }[];\n };\n}\n\n/**\n * Fetches global usage stats across all developer projects.\n * Endpoint: GET /analytics/stats\n */\nexport async function getGlobalStats(): Promise {\n const res = await apiFetch(\"/analytics/stats\", {\n method: \"GET\",\n });\n return res.data;\n}\n\n/**\n * Fetches the 20 most recent API logs across all projects.\n * Endpoint: GET /analytics/activity\n */\nexport async function getRecentActivity(): Promise {\n const res = await apiFetch(\"/analytics/activity\", {\n method: \"GET\",\n });\n return Array.isArray(res.data) ? res.data : [];\n}\n\n/**\n * Fetches per-project analytics.\n * Endpoint: GET /projects/:projectId/analytics?range=last24h\n */\nexport async function getProjectAnalytics(\n projectId: string,\n range: \"last1h\" | \"last24h\" | \"last7d\" | \"last30d\" | \"allTime\" = \"last24h\",\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/analytics?range=${range}`,\n { method: \"GET\" },\n );\n return res.data;\n}\n","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { formatBytes, label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nfunction statusIcon(status: number): string {\n if (status < 300) return \"✓\";\n if (status < 400) return \"→\";\n if (status < 500) return \"⚠\";\n return \"✖\";\n}\n\nexport async function statusCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const currentProjectId = getCurrentProject();\n\n try {\n // Fetch global stats (works without a current project)\n const stats = await getGlobalStats();\n\n console.log(\"\\n── Account ─────────────────────────────────────\");\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\n\n if (stats.planExpiresAt) {\n console.log(`${label(\"Plan expires\")} ${formatDate(stats.planExpiresAt)}`);\n }\n\n console.log(\"\\n── Usage ────────────────────────────────────────\");\n console.log(\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\n );\n console.log(\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\n );\n console.log(\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`,\n );\n console.log(\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`,\n );\n console.log(`${label(\"Total requests\")} ${stats.usage.totalRequests.toLocaleString()}`);\n console.log(`${label(\"Total users\")} ${stats.usage.totalUsers.toLocaleString()}`);\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\n\n // Per-project detail if a project is active\n if (currentProjectId) {\n try {\n const project = await getProject(currentProjectId);\n console.log(\"\\n── Active project ───────────────────────────────\");\n console.log(`${label(\"Name\")} ${project.name}`);\n console.log(`${label(\"ID\")} ${project._id}`);\n console.log(\n `${label(\"Collections\")} ${project.collections?.length ?? 0}`,\n );\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n } catch {\n // Non-fatal — global stats already displayed\n }\n } else {\n console.log(\n \"\\nTip: Run 'ub project use' to select a project and see per-project details.\",\n );\n }\n\n // Recent activity\n try {\n const activity = await getRecentActivity();\n\n if (activity.length > 0) {\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\n for (const log of activity.slice(0, 10)) {\n const icon = statusIcon(log.status);\n const time = new Date(log.timestamp).toLocaleTimeString();\n console.log(\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`,\n );\n }\n }\n } catch {\n // Non-fatal — skip activity if it fails\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport {\n getToken,\n getCurrentProject,\n loadConfig,\n configFilePath,\n} from \"../../core/config.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\ninterface DoctorResult {\n configFound: boolean;\n patValid: boolean;\n projectSelected: boolean;\n projectAccessible: boolean;\n dashboardApiReachable: boolean;\n dashboardApiLatencyMs: number | null;\n email: string | null;\n plan: string | null;\n projectName: string | null;\n}\n\nexport interface DoctorOptions {\n json?: boolean;\n}\n\nasync function checkApiReachable(apiBase: string): Promise {\n const start = Date.now();\n try {\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\n return Date.now() - start;\n } catch {\n return null;\n }\n}\n\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\n const config = loadConfig();\n const token = getToken();\n const currentProjectId = getCurrentProject();\n\n const result: DoctorResult = {\n configFound: false,\n patValid: false,\n projectSelected: !!currentProjectId,\n projectAccessible: false,\n dashboardApiReachable: false,\n dashboardApiLatencyMs: null,\n email: null,\n plan: null,\n projectName: null,\n };\n\n // 1. Config file\n try {\n const fs = await import(\"node:fs\");\n result.configFound = fs.existsSync(configFilePath());\n } catch {\n result.configFound = false;\n }\n\n // 2. API reachability (independent of auth)\n const latency = await checkApiReachable(config.apiBase);\n result.dashboardApiReachable = latency !== null;\n result.dashboardApiLatencyMs = latency;\n\n // 3. PAT validity\n if (token) {\n try {\n const profile = await getProfile();\n result.patValid = true;\n result.email = profile.developer.email;\n result.plan = profile.developer.plan;\n } catch {\n result.patValid = false;\n }\n }\n\n // 4. Project accessibility\n if (currentProjectId && result.patValid) {\n try {\n const project = await getProject(currentProjectId);\n result.projectAccessible = true;\n result.projectName = project.name;\n } catch {\n result.projectAccessible = false;\n }\n }\n\n // Output\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\n\n const ok = \"✓\";\n const fail = \"✖\";\n const warn = \"⚠\";\n\n console.log(\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\n );\n\n console.log(\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\n result.dashboardApiReachable\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\n : `Unreachable — ${config.apiBase}`\n }`,\n );\n\n if (!token) {\n console.log(` ${fail} PAT Not set — run 'ub login'`);\n } else {\n console.log(\n ` ${result.patValid ? ok : fail} PAT ${\n result.patValid\n ? `Valid (${result.email}, ${result.plan})`\n : \"Invalid or expired — run 'ub login'\"\n }`,\n );\n }\n\n if (!currentProjectId) {\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\n } else {\n console.log(\n ` ${result.projectAccessible ? ok : fail} Active project ${\n result.projectAccessible\n ? `${result.projectName} (${currentProjectId})`\n : `ID ${currentProjectId} — not found or access denied`\n }`,\n );\n }\n\n console.log();\n\n // Summary\n const allGood =\n result.configFound &&\n result.patValid &&\n result.dashboardApiReachable &&\n result.projectSelected &&\n result.projectAccessible;\n\n if (allGood) {\n logger.success(\"Everything looks good.\");\n } else {\n logger.warn(\"Some checks failed. Review the output above.\");\n }\n\n console.log();\n}\n"],"mappings":"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","names":["path","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/index.ts b/sdks/urbackend-cli/index.ts deleted file mode 100644 index 84828c393..000000000 --- a/sdks/urbackend-cli/index.ts +++ /dev/null @@ -1 +0,0 @@ -import "./src/cli.js"; diff --git a/sdks/urbackend-cli/package-lock.json b/sdks/urbackend-cli/package-lock.json new file mode 100644 index 000000000..0e5296dc3 --- /dev/null +++ b/sdks/urbackend-cli/package-lock.json @@ -0,0 +1,1509 @@ +{ + "name": "@urbackend/cli", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@urbackend/cli", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "commander": "^12.1.0" + }, + "bin": { + "ub": "bin/ub.js" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsup": "^8.5.1", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.0.tgz", + "integrity": "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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/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/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/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/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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/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/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/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/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/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/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "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/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/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.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "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/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/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "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/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/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/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.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "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/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/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.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "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==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/sdks/urbackend-cli/package.json b/sdks/urbackend-cli/package.json index 35430dec1..1f4fed3ee 100644 --- a/sdks/urbackend-cli/package.json +++ b/sdks/urbackend-cli/package.json @@ -1,7 +1,7 @@ { "name": "@urbackend/cli", "version": "0.1.0", - "description": "Official CLI for urBackend", + "description": "Official CLI for urBackend — manage projects, schemas, and more from your terminal", "publishConfig": { "access": "public" }, @@ -10,44 +10,32 @@ "ub": "./bin/ub.js" }, "files": [ - "dist", "bin", - "README.md", - "LICENSE" + "dist", + "README.md" ], "scripts": { "build": "tsup --config tsup.config.ts", - "test": "vitest run", + "dev": "tsup --config tsup.config.ts --watch", "lint": "eslint src", "format": "prettier --write \"src/**/*.ts\"" }, + "dependencies": { + "commander": "^12.1.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsup": "^8.5.1", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=20.0.0" + }, "keywords": [ "urbackend", "cli", "baas" ], "author": "urBackend Contributors", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/geturbackend/urBackend", - "directory": "sdks/urbackend-cli" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@types/node": "^25.5.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", - "prettier": "^3.8.1", - "tsup": "^8.5.1", - "typescript": "^5.9.3", - "typescript-eslint": "^8.57.2", - "vitest": "^1.6.0" - }, - "dependencies": { - "chalk": "^5.6.2", - "commander": "^14.0.3", - "ora": "^9.0.0" - } + "license": "MIT" } diff --git a/sdks/urbackend-cli/src/cli.ts b/sdks/urbackend-cli/src/cli.ts deleted file mode 100644 index 95a71b7d3..000000000 --- a/sdks/urbackend-cli/src/cli.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Command } from "commander"; - -import { loginCommand } from "./commands/login.js"; -import { logoutCommand } from "./commands/logout.js"; -import { whoamiCommand } from "./commands/whoami.js"; - -const program = new Command(); - -program - .name("ub") - .description("Official CLI for urBackend") - .version("0.1.0"); - -program - .command("login") - .description( - "Authenticate using a Personal Access Token", - ) - .action(loginCommand); - -program - .command("logout") - .description( - "Remove the local authentication", - ) - .action(logoutCommand); - -program - .command("whoami") - .description( - "Display the authenticated developer", - ) - .action(whoamiCommand); - -program.parse(); diff --git a/sdks/urbackend-cli/src/commands/auth/login.ts b/sdks/urbackend-cli/src/commands/auth/login.ts new file mode 100644 index 000000000..85561d08f --- /dev/null +++ b/sdks/urbackend-cli/src/commands/auth/login.ts @@ -0,0 +1,49 @@ +import { authenticate } from "../../services/auth.service.js"; +import { saveToken } from "../../core/config.js"; +import { isValidPAT } from "../../utils/token.js"; +import { prompt } from "../../utils/prompt.js"; +import { label } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function loginCommand(): Promise { + console.log("Generate a Personal Access Token from the urBackend dashboard:"); + console.log(" Settings → Access Tokens → New Token\n"); + + const token = await prompt("Paste your Personal Access Token: "); + + if (!isValidPAT(token)) { + logger.error( + "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.", + ); + return; + } + + try { + const profile = await authenticate(token); + + saveToken(token); + + logger.success("Logged in successfully.\n"); + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); + + if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } + + if (profile.auth.scopes.length > 0) { + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Invalid or expired token. Generate a new one from the dashboard."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/auth/logout.ts b/sdks/urbackend-cli/src/commands/auth/logout.ts new file mode 100644 index 000000000..3c0a0bd49 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/auth/logout.ts @@ -0,0 +1,7 @@ +import { clearToken } from "../../core/config.js"; +import { logger } from "../../core/logger.js"; + +export async function logoutCommand(): Promise { + clearToken(); + logger.success("Logged out. Your token has been removed from this machine."); +} diff --git a/sdks/urbackend-cli/src/commands/auth/whoami.ts b/sdks/urbackend-cli/src/commands/auth/whoami.ts new file mode 100644 index 000000000..b749d234b --- /dev/null +++ b/sdks/urbackend-cli/src/commands/auth/whoami.ts @@ -0,0 +1,46 @@ +import { getProfile } from "../../services/auth.service.js"; +import { getToken, getCurrentProject } from "../../core/config.js"; +import { label, formatDate } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function whoamiCommand(): Promise { + const token = getToken(); + + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + try { + const profile = await getProfile(); + + console.log(`${label("Email")} ${profile.developer.email}`); + console.log(`${label("Plan")} ${profile.developer.plan}`); + + if (profile.developer.githubUsername) { + console.log(`${label("GitHub")} ${profile.developer.githubUsername}`); + } + + console.log(`${label("Token type")} ${profile.auth.tokenType}`); + + if (profile.auth.scopes.length > 0) { + console.log(`${label("Scopes")} ${profile.auth.scopes.join(", ")}`); + } + + const currentProject = getCurrentProject(); + if (currentProject) { + console.log(`${label("Project")} ${currentProject}`); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/collection/delete.ts b/sdks/urbackend-cli/src/commands/collection/delete.ts new file mode 100644 index 000000000..4fe82499e --- /dev/null +++ b/sdks/urbackend-cli/src/commands/collection/delete.ts @@ -0,0 +1,67 @@ +import { deleteCollection } from "../../services/collection.service.js"; +import { getProject } from "../../services/project.service.js"; +import { getToken, getCurrentProject } from "../../core/config.js"; +import { confirm } from "../../utils/prompt.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export interface CollectionDeleteOptions { + force?: boolean; + project?: string; +} + +export async function collectionDeleteCommand( + collectionName: string, + options: CollectionDeleteOptions, +): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + const projectId = options.project ?? getCurrentProject(); + if (!projectId) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + + try { + // Verify the collection actually exists before asking for confirmation + const project = await getProject(projectId); + const exists = project.collections?.some((c) => c.name === collectionName); + + if (!exists) { + logger.error( + `Collection "${collectionName}" not found in project "${project.name}".`, + ); + return; + } + + if (!options.force) { + console.log( + `\n⚠ This will permanently delete the collection "${collectionName}" and ALL its data.\n`, + ); + const ok = await confirm("Are you sure?"); + if (!ok) { + logger.info("Aborted."); + return; + } + } + + await deleteCollection(projectId, collectionName); + logger.success(`Collection "${collectionName}" deleted.`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Collection or project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/collection/list.ts b/sdks/urbackend-cli/src/commands/collection/list.ts new file mode 100644 index 000000000..79b498c95 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/collection/list.ts @@ -0,0 +1,66 @@ +import { getProject } from "../../services/project.service.js"; +import { getToken, getCurrentProject } from "../../core/config.js"; +import { label } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function collectionListCommand(projectId?: string): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + const id = projectId ?? getCurrentProject(); + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + + try { + const project = await getProject(id); + const collections = project.collections ?? []; + + if (collections.length === 0) { + logger.info(`No collections in project "${project.name}".`); + return; + } + + console.log(`\nCollections in "${project.name}" (${collections.length}):\n`); + + for (const col of collections) { + const fieldCount = col.model?.length ?? 0; + const rlsMode = col.rls?.enabled ? col.rls.mode : "disabled"; + + console.log(` ${col.name}`); + console.log(` ${label("Fields", 10)} ${fieldCount}`); + console.log(` ${label("RLS", 10)} ${rlsMode}`); + + if (col.model && col.model.length > 0) { + const fieldSummary = col.model + .slice(0, 5) + .map((f) => `${f.key}: ${f.type}${f.required ? "*" : ""}`) + .join(", "); + const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : ""; + console.log(` ${label("Schema", 10)} ${fieldSummary}${extra}`); + } + + console.log(); + } + + console.log(" * = required field"); + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found or access denied."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/doctor/index.ts b/sdks/urbackend-cli/src/commands/doctor/index.ts new file mode 100644 index 000000000..260b60af4 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/doctor/index.ts @@ -0,0 +1,156 @@ +import { getProfile } from "../../services/auth.service.js"; +import { getProject } from "../../services/project.service.js"; +import { + getToken, + getCurrentProject, + loadConfig, + configFilePath, +} from "../../core/config.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +interface DoctorResult { + configFound: boolean; + patValid: boolean; + projectSelected: boolean; + projectAccessible: boolean; + dashboardApiReachable: boolean; + dashboardApiLatencyMs: number | null; + email: string | null; + plan: string | null; + projectName: string | null; +} + +export interface DoctorOptions { + json?: boolean; +} + +async function checkApiReachable(apiBase: string): Promise { + const start = Date.now(); + try { + await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) }); + return Date.now() - start; + } catch { + return null; + } +} + +export async function doctorCommand(options: DoctorOptions = {}): Promise { + const config = loadConfig(); + const token = getToken(); + const currentProjectId = getCurrentProject(); + + const result: DoctorResult = { + configFound: false, + patValid: false, + projectSelected: !!currentProjectId, + projectAccessible: false, + dashboardApiReachable: false, + dashboardApiLatencyMs: null, + email: null, + plan: null, + projectName: null, + }; + + // 1. Config file + try { + const fs = await import("node:fs"); + result.configFound = fs.existsSync(configFilePath()); + } catch { + result.configFound = false; + } + + // 2. API reachability (independent of auth) + const latency = await checkApiReachable(config.apiBase); + result.dashboardApiReachable = latency !== null; + result.dashboardApiLatencyMs = latency; + + // 3. PAT validity + if (token) { + try { + const profile = await getProfile(); + result.patValid = true; + result.email = profile.developer.email; + result.plan = profile.developer.plan; + } catch { + result.patValid = false; + } + } + + // 4. Project accessibility + if (currentProjectId && result.patValid) { + try { + const project = await getProject(currentProjectId); + result.projectAccessible = true; + result.projectName = project.name; + } catch { + result.projectAccessible = false; + } + } + + // Output + if (options.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } + + console.log("\n── urBackend CLI Doctor ─────────────────────────\n"); + + const ok = "✓"; + const fail = "✖"; + const warn = "⚠"; + + console.log( + ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : "Not found — run 'ub login'"}`, + ); + + console.log( + ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${ + result.dashboardApiReachable + ? `Reachable (${result.dashboardApiLatencyMs}ms)` + : `Unreachable — ${config.apiBase}` + }`, + ); + + if (!token) { + console.log(` ${fail} PAT Not set — run 'ub login'`); + } else { + console.log( + ` ${result.patValid ? ok : fail} PAT ${ + result.patValid + ? `Valid (${result.email}, ${result.plan})` + : "Invalid or expired — run 'ub login'" + }`, + ); + } + + if (!currentProjectId) { + console.log(` ${warn} Active project Not selected — run 'ub project use'`); + } else { + console.log( + ` ${result.projectAccessible ? ok : fail} Active project ${ + result.projectAccessible + ? `${result.projectName} (${currentProjectId})` + : `ID ${currentProjectId} — not found or access denied` + }`, + ); + } + + console.log(); + + // Summary + const allGood = + result.configFound && + result.patValid && + result.dashboardApiReachable && + result.projectSelected && + result.projectAccessible; + + if (allGood) { + logger.success("Everything looks good."); + } else { + logger.warn("Some checks failed. Review the output above."); + } + + console.log(); +} diff --git a/sdks/urbackend-cli/src/commands/login.ts b/sdks/urbackend-cli/src/commands/login.ts deleted file mode 100644 index 31714e2a2..000000000 --- a/sdks/urbackend-cli/src/commands/login.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { createInterface } from "node:readline/promises"; -import { stdin, stdout } from "node:process"; - -import { authenticate } from "../services/auth.service.js"; -import { saveToken } from "../core/config.js"; -import { isValidPAT } from "../utils/token.js"; -import { APIError } from "../core/errors.js"; -import { logger } from "../core/logger.js"; - -export async function loginCommand() { - const rl = createInterface({ - input: stdin, - output: stdout, - }); - - const token = ( - await rl.question("Paste your Personal Access Token: ") - ).trim(); - - rl.close(); - - if (!isValidPAT(token)) { - logger.error("Invalid Personal Access Token."); - return; - } - - try { - const profile = await authenticate(token); - - saveToken(token); - - logger.success("Successfully logged in."); - - console.log(""); - - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); - - if (profile.auth.scopes.length > 0) { - console.log( - `Scopes : ${profile.auth.scopes.join(", ")}`, - ); - } - } catch (error) { - if (error instanceof APIError) { - logger.error(error.message); - return; - } - - logger.error("Unable to connect to urBackend."); - } -} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/logout.ts b/sdks/urbackend-cli/src/commands/logout.ts deleted file mode 100644 index a80efea89..000000000 --- a/sdks/urbackend-cli/src/commands/logout.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { clearToken } from "../core/config.js"; -import { logger } from "../core/logger.js"; - -export async function logoutCommand() { - clearToken(); - - logger.success("Logged out successfully."); -} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/project/info.ts b/sdks/urbackend-cli/src/commands/project/info.ts new file mode 100644 index 000000000..ef5ccf342 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/project/info.ts @@ -0,0 +1,58 @@ +import { getProject } from "../../services/project.service.js"; +import { getCurrentProject, getToken } from "../../core/config.js"; +import { formatBytes, label } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function projectInfoCommand(projectId?: string): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + const id = projectId ?? getCurrentProject(); + + if (!id) { + logger.error("No active project. Run 'ub project use' to select one."); + return; + } + + try { + const project = await getProject(id); + + console.log(`\n${project.name}\n`); + console.log(`${label("ID")} ${project._id}`); + + if (project.description) { + console.log(`${label("Description")} ${project.description}`); + } + + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + console.log(`${label("DB used")} ${formatBytes(project.databaseUsed ?? 0)}`); + console.log(`${label("Storage")} ${formatBytes(project.storageUsed ?? 0)}`); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); + + if (project.collections && project.collections.length > 0) { + console.log("\nCollections:"); + for (const col of project.collections) { + const fieldCount = col.model?.length ?? 0; + console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? "s" : ""})`); + } + } + + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else if (error.status === 404) { + logger.error("Project not found."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/project/list.ts b/sdks/urbackend-cli/src/commands/project/list.ts new file mode 100644 index 000000000..f82771b4f --- /dev/null +++ b/sdks/urbackend-cli/src/commands/project/list.ts @@ -0,0 +1,52 @@ +import { listProjects } from "../../services/project.service.js"; +import { getCurrentProject, getToken } from "../../core/config.js"; +import { formatBytes } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function projectListCommand(): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + try { + const projects = await listProjects(); + const currentProjectId = getCurrentProject(); + + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + + console.log(`\nFound ${projects.length} project(s):\n`); + + for (const project of projects) { + const active = project._id === currentProjectId ? " (active)" : ""; + const db = formatBytes(project.databaseUsed ?? 0); + const colCount = project.collections?.length ?? 0; + + console.log(` ${project.name}${active}`); + console.log(` ID ${project._id}`); + console.log(` Collections ${colCount}`); + console.log(` DB used ${db}`); + + if (project.description) { + console.log(` Description ${project.description}`); + } + + console.log(); + } + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/project/use.ts b/sdks/urbackend-cli/src/commands/project/use.ts new file mode 100644 index 000000000..e8f48fe7b --- /dev/null +++ b/sdks/urbackend-cli/src/commands/project/use.ts @@ -0,0 +1,71 @@ +import { listProjects, getProject } from "../../services/project.service.js"; +import { saveCurrentProject, getToken } from "../../core/config.js"; +import { prompt } from "../../utils/prompt.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +export async function projectUseCommand(projectIdOrName?: string): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + try { + const projects = await listProjects(); + + if (projects.length === 0) { + logger.info("No projects found. Create one at dashboard.urbackend.bitbros.in"); + return; + } + + let selectedId: string | undefined; + + if (projectIdOrName) { + // Try matching by ID or name + const match = projects.find( + (p) => + p._id === projectIdOrName || + p.name.toLowerCase() === projectIdOrName.toLowerCase(), + ); + if (!match) { + logger.error(`No project found matching "${projectIdOrName}".`); + logger.info("Run 'ub project list' to see available projects."); + return; + } + selectedId = match._id; + } else { + // Interactive selection + console.log("\nAvailable projects:\n"); + projects.forEach((p, i) => { + console.log(` [${i + 1}] ${p.name} (${p._id})`); + }); + console.log(); + + const answer = await prompt("Enter project number: "); + const index = parseInt(answer, 10) - 1; + + if (isNaN(index) || index < 0 || index >= projects.length) { + logger.error("Invalid selection."); + return; + } + + selectedId = projects[index]._id; + } + + saveCurrentProject(selectedId); + + const project = projects.find((p) => p._id === selectedId)!; + logger.success(`Switched to project: ${project.name} (${selectedId})`); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/status/index.ts b/sdks/urbackend-cli/src/commands/status/index.ts new file mode 100644 index 000000000..4f987c722 --- /dev/null +++ b/sdks/urbackend-cli/src/commands/status/index.ts @@ -0,0 +1,102 @@ +import { getGlobalStats, getRecentActivity } from "../../services/analytics.service.js"; +import { getProject } from "../../services/project.service.js"; +import { getToken, getCurrentProject } from "../../core/config.js"; +import { formatBytes, label, formatDate } from "../../utils/format.js"; +import { APIError } from "../../core/errors.js"; +import { logger } from "../../core/logger.js"; + +function statusIcon(status: number): string { + if (status < 300) return "✓"; + if (status < 400) return "→"; + if (status < 500) return "⚠"; + return "✖"; +} + +export async function statusCommand(): Promise { + const token = getToken(); + if (!token) { + logger.error("You are not logged in. Run 'ub login' first."); + return; + } + + const currentProjectId = getCurrentProject(); + + try { + // Fetch global stats (works without a current project) + const stats = await getGlobalStats(); + + console.log("\n── Account ─────────────────────────────────────"); + console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); + + if (stats.planExpiresAt) { + console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); + } + + console.log("\n── Usage ────────────────────────────────────────"); + console.log( + `${label("Projects")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`, + ); + console.log( + `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`, + ); + console.log( + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`, + ); + console.log( + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`, + ); + console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); + console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); + + // Per-project detail if a project is active + if (currentProjectId) { + try { + const project = await getProject(currentProjectId); + console.log("\n── Active project ───────────────────────────────"); + console.log(`${label("Name")} ${project.name}`); + console.log(`${label("ID")} ${project._id}`); + console.log( + `${label("Collections")} ${project.collections?.length ?? 0}`, + ); + console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); + } catch { + // Non-fatal — global stats already displayed + } + } else { + console.log( + "\nTip: Run 'ub project use' to select a project and see per-project details.", + ); + } + + // Recent activity + try { + const activity = await getRecentActivity(); + + if (activity.length > 0) { + console.log("\n── Recent activity (last 10) ────────────────────"); + for (const log of activity.slice(0, 10)) { + const icon = statusIcon(log.status); + const time = new Date(log.timestamp).toLocaleTimeString(); + console.log( + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`, + ); + } + } + } catch { + // Non-fatal — skip activity if it fails + } + + console.log(); + } catch (error) { + if (error instanceof APIError) { + if (error.status === 401) { + logger.error("Token is invalid or expired. Run 'ub login' to re-authenticate."); + } else { + logger.error(error.message); + } + return; + } + logger.error("Unable to connect to the urBackend API."); + } +} diff --git a/sdks/urbackend-cli/src/commands/whoami.ts b/sdks/urbackend-cli/src/commands/whoami.ts deleted file mode 100644 index 057f14dd8..000000000 --- a/sdks/urbackend-cli/src/commands/whoami.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { getProfile } from "../services/auth.service.js"; -import { APIError } from "../core/errors.js"; -import { logger } from "../core/logger.js"; - -export async function whoamiCommand() { - try { - const profile = await getProfile(); - - console.log(`Email : ${profile.developer.email}`); - console.log(`Plan : ${profile.developer.plan}`); - - if (profile.developer.githubUsername) { - console.log( - `GitHub : ${profile.developer.githubUsername}`, - ); - } - - console.log( - `Token Type : ${profile.auth.tokenType}`, - ); - } catch (error) { - if (error instanceof APIError) { - logger.error(error.message); - return; - } - - logger.error("Unable to connect to urBackend."); - } -} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/api.ts b/sdks/urbackend-cli/src/core/api.ts index 6c8044ef9..9f611ee1c 100644 --- a/sdks/urbackend-cli/src/core/api.ts +++ b/sdks/urbackend-cli/src/core/api.ts @@ -1,64 +1,66 @@ import { loadConfig } from "./config.js"; import { APIError } from "./errors.js"; -export interface FetchOptions - extends RequestInit { +export interface FetchOptions extends RequestInit { + /** Override the PAT for this single request (e.g. during login verification) */ token?: string; } +/** + * Central HTTP client for all CLI → dashboard-api communication. + * Automatically injects the stored PAT as a Bearer token. + * + * All dashboard-api routes are mounted under /api — this client + * prepends that prefix automatically so callers just write /user/cli/me. + */ export async function apiFetch( endpoint: string, options: FetchOptions = {}, ): Promise { const config = loadConfig(); - const headers = new Headers( - options.headers, - ); + const headers = new Headers(options.headers); - if ( - options.body && - !headers.has("Content-Type") -) { - headers.set( - "Content-Type", - "application/json", - ); -} - const token = - options.token ?? config.pat; + if (options.body && !headers.has("Content-Type")) { + headers.set("Content-Type", "application/json"); + } + const token = options.token ?? config.pat; if (token) { - headers.set( - "Authorization", - `Bearer ${token}`, - ); + headers.set("Authorization", `Bearer ${token}`); } - const response = await fetch( - `${config.apiBase}${endpoint}`, - { + // All dashboard-api routes are under /api + const url = `${config.apiBase}/api${endpoint}`; + + let response: Response; + + try { + response = await fetch(url, { ...options, headers, - }, - ); + }); + } catch { + throw new APIError( + 0, + "Unable to connect to the urBackend API. Is the server running?", + ); + } if (!response.ok) { let message = response.statusText; try { - const body = await response.json(); + const body = (await response.json()) as { + message?: string; + error?: string; + }; + message = body.message ?? body.error ?? message; + } catch { + // ignore — use statusText + } - message = - body.message ?? - body.error ?? - message; - } catch {} - - throw new APIError( - response.status, - message, - ); + throw new APIError(response.status, message); } if (response.status === 204) { diff --git a/sdks/urbackend-cli/src/core/config.ts b/sdks/urbackend-cli/src/core/config.ts index e07667bb4..8755e55f4 100644 --- a/sdks/urbackend-cli/src/core/config.ts +++ b/sdks/urbackend-cli/src/core/config.ts @@ -1,10 +1,7 @@ import fs from "node:fs"; +import path from "node:path"; import type { CLIConfig } from "../types/config.js"; -import { - CONFIG_DIR, - CONFIG_PATH, - DEFAULT_API_BASE, -} from "./constants.js"; +import { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from "./constants.js"; const DEFAULT_CONFIG: CLIConfig = { apiBase: DEFAULT_API_BASE, @@ -12,18 +9,17 @@ const DEFAULT_CONFIG: CLIConfig = { export function loadConfig(): CLIConfig { if (!fs.existsSync(CONFIG_PATH)) { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } try { const raw = fs.readFileSync(CONFIG_PATH, "utf8"); - return { ...DEFAULT_CONFIG, ...JSON.parse(raw), }; } catch { - return DEFAULT_CONFIG; + return { ...DEFAULT_CONFIG }; } } @@ -32,29 +28,41 @@ export function saveConfig(config: CLIConfig): void { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } - fs.writeFileSync( - CONFIG_PATH, - JSON.stringify(config, null, 2), - ); + // Atomic write: write to temp file then rename to avoid corruption on crash + const tmp = CONFIG_PATH + ".tmp"; + fs.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + fs.renameSync(tmp, CONFIG_PATH); } export function saveToken(token: string): void { const config = loadConfig(); - - saveConfig({ - ...config, - pat: token, - }); + saveConfig({ ...config, pat: token }); } export function clearToken(): void { const config = loadConfig(); - delete config.pat; - + delete config.currentProject; saveConfig(config); } export function getToken(): string | undefined { return loadConfig().pat; -} \ No newline at end of file +} + +export function saveCurrentProject(projectId: string): void { + const config = loadConfig(); + saveConfig({ ...config, currentProject: projectId }); +} + +export function getCurrentProject(): string | undefined { + return loadConfig().currentProject; +} + +export function configFilePath(): string { + return CONFIG_PATH; +} + +export function configDirPath(): string { + return path.dirname(CONFIG_PATH); +} diff --git a/sdks/urbackend-cli/src/core/constants.ts b/sdks/urbackend-cli/src/core/constants.ts index 7a07a5a38..4f64eb00b 100644 --- a/sdks/urbackend-cli/src/core/constants.ts +++ b/sdks/urbackend-cli/src/core/constants.ts @@ -3,22 +3,13 @@ import path from "node:path"; export const CLI_NAME = "ub"; -export const CONFIG_DIR = path.join( - os.homedir(), - ".ub", -); +export const CONFIG_DIR = path.join(os.homedir(), ".ub"); -export const CONFIG_PATH = path.join( - CONFIG_DIR, - "config.json", -); +export const CONFIG_PATH = path.join(CONFIG_DIR, "config.json"); /** - * Default production API. - * - * Can be overridden during development by - * URBACKEND_API_URL. + * Default production API base URL. + * Override via URBACKEND_API_URL env variable for local development. */ export const DEFAULT_API_BASE = - process.env.URBACKEND_API_URL ?? - "https://api.urbackend.bitbros.in"; \ No newline at end of file + process.env.URBACKEND_API_URL ?? "https://api.urbackend.bitbros.in"; diff --git a/sdks/urbackend-cli/src/core/errors.ts b/sdks/urbackend-cli/src/core/errors.ts index f32be5173..7f042ae6e 100644 --- a/sdks/urbackend-cli/src/core/errors.ts +++ b/sdks/urbackend-cli/src/core/errors.ts @@ -7,10 +7,3 @@ export class APIError extends Error { this.name = "APIError"; } } - -export class ConfigError extends Error { - constructor(message: string) { - super(message); - this.name = "ConfigError"; - } -} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/core/logger.ts b/sdks/urbackend-cli/src/core/logger.ts index e83eaa20e..cd21b9733 100644 --- a/sdks/urbackend-cli/src/core/logger.ts +++ b/sdks/urbackend-cli/src/core/logger.ts @@ -1,19 +1,17 @@ -const PREFIX = "ub"; - export const logger = { - info(message: string) { - console.log(`${PREFIX}: ${message}`); + success(message: string): void { + console.log(`✓ ${message}`); }, - success(message: string) { - console.log(`✓ ${message}`); + error(message: string): void { + console.error(`✖ ${message}`); }, - warn(message: string) { + warn(message: string): void { console.warn(`⚠ ${message}`); }, - error(message: string) { - console.error(`✖ ${message}`); + info(message: string): void { + console.log(message); }, -}; \ No newline at end of file +}; diff --git a/sdks/urbackend-cli/src/index.ts b/sdks/urbackend-cli/src/index.ts new file mode 100644 index 000000000..6370dfc2e --- /dev/null +++ b/sdks/urbackend-cli/src/index.ts @@ -0,0 +1,101 @@ +import { Command } from "commander"; +import { loginCommand } from "./commands/auth/login.js"; +import { logoutCommand } from "./commands/auth/logout.js"; +import { whoamiCommand } from "./commands/auth/whoami.js"; +import { projectListCommand } from "./commands/project/list.js"; +import { projectUseCommand } from "./commands/project/use.js"; +import { projectInfoCommand } from "./commands/project/info.js"; +import { collectionListCommand } from "./commands/collection/list.js"; +import { collectionDeleteCommand } from "./commands/collection/delete.js"; +import { statusCommand } from "./commands/status/index.js"; +import { doctorCommand } from "./commands/doctor/index.js"; + +const program = new Command(); + +program + .name("ub") + .description("Official urBackend CLI — manage projects, schemas, and more") + .version("0.1.0"); + +// ── Authentication ────────────────────────────────────────────────────────── + +program + .command("login") + .description("Authenticate with a Personal Access Token") + .action(loginCommand); + +program + .command("logout") + .description("Remove stored credentials from this machine") + .action(logoutCommand); + +program + .command("whoami") + .description("Show the currently authenticated developer") + .action(whoamiCommand); + +// ── Projects ───────────────────────────────────────────────────────────────── + +const projectCmd = program + .command("project") + .description("Manage urBackend projects"); + +projectCmd + .command("list") + .alias("ls") + .description("List all accessible projects") + .action(projectListCommand); + +projectCmd + .command("use [projectIdOrName]") + .description("Set the active project for subsequent commands") + .action(projectUseCommand); + +projectCmd + .command("info [projectId]") + .description("Show details for the active (or specified) project") + .action(projectInfoCommand); + +// ── Collections ────────────────────────────────────────────────────────────── + +const collectionCmd = program + .command("collection") + .alias("col") + .description("Manage collections inside the active project"); + +collectionCmd + .command("list") + .alias("ls") + .description("List all collections in the active project") + .option("-p, --project ", "Target a specific project ID") + .action((options) => collectionListCommand(options.project)); + +collectionCmd + .command("delete ") + .alias("rm") + .description("Delete a collection and all its data") + .option("-f, --force", "Skip confirmation prompt") + .option("-p, --project ", "Target a specific project ID") + .action((name, options) => collectionDeleteCommand(name, options)); + +// ── Status ──────────────────────────────────────────────────────────────────── + +program + .command("status") + .description("Show account usage and recent API activity") + .action(statusCommand); + +// ── Doctor ──────────────────────────────────────────────────────────────────── + +program + .command("doctor") + .description("Run diagnostic checks on your CLI setup") + .option("--json", "Output results as JSON (useful for CI/AI agents)") + .action((options) => doctorCommand(options)); + +// ── Parse ───────────────────────────────────────────────────────────────────── + +program.parseAsync(process.argv).catch((err: Error) => { + console.error(`✖ ${err.message}`); + process.exit(1); +}); diff --git a/sdks/urbackend-cli/src/services/analytics.service.ts b/sdks/urbackend-cli/src/services/analytics.service.ts new file mode 100644 index 000000000..d3fd7448b --- /dev/null +++ b/sdks/urbackend-cli/src/services/analytics.service.ts @@ -0,0 +1,56 @@ +import { apiFetch } from "../core/api.js"; +import type { GlobalStats, RecentActivityLog } from "../types/analytics.js"; + +interface GlobalStatsResponse { + data: GlobalStats; +} + +interface RecentActivityResponse { + data: RecentActivityLog[]; +} + +interface ProjectAnalyticsResponse { + data: { + totalRequests: number; + successRequests: number; + errorRequests: number; + topCollections?: { name: string; requests: number }[]; + }; +} + +/** + * Fetches global usage stats across all developer projects. + * Endpoint: GET /analytics/stats + */ +export async function getGlobalStats(): Promise { + const res = await apiFetch("/analytics/stats", { + method: "GET", + }); + return res.data; +} + +/** + * Fetches the 20 most recent API logs across all projects. + * Endpoint: GET /analytics/activity + */ +export async function getRecentActivity(): Promise { + const res = await apiFetch("/analytics/activity", { + method: "GET", + }); + return Array.isArray(res.data) ? res.data : []; +} + +/** + * Fetches per-project analytics. + * Endpoint: GET /projects/:projectId/analytics?range=last24h + */ +export async function getProjectAnalytics( + projectId: string, + range: "last1h" | "last24h" | "last7d" | "last30d" | "allTime" = "last24h", +): Promise { + const res = await apiFetch( + `/projects/${projectId}/analytics?range=${range}`, + { method: "GET" }, + ); + return res.data; +} diff --git a/sdks/urbackend-cli/src/services/auth.service.ts b/sdks/urbackend-cli/src/services/auth.service.ts index 1c02dfe96..778295ccb 100644 --- a/sdks/urbackend-cli/src/services/auth.service.ts +++ b/sdks/urbackend-cli/src/services/auth.service.ts @@ -1,17 +1,24 @@ import { apiFetch } from "../core/api.js"; import type { CLIProfile } from "../types/auth.js"; -export async function authenticate( - token: string, -): Promise { - return apiFetch("/api/user/cli/me", { +interface APIResponse { + success: boolean; + data: T; + message: string; +} + +export async function authenticate(token: string): Promise { + const res = await apiFetch>("/user/cli/me", { method: "GET", token, }); + console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); + return res.data; } export async function getProfile(): Promise { - return apiFetch("/api/user/cli/me", { + const res = await apiFetch>("/user/cli/me", { method: "GET", }); + return res.data; } \ No newline at end of file diff --git a/sdks/urbackend-cli/src/services/collection.service.ts b/sdks/urbackend-cli/src/services/collection.service.ts new file mode 100644 index 000000000..dfdb19d14 --- /dev/null +++ b/sdks/urbackend-cli/src/services/collection.service.ts @@ -0,0 +1,39 @@ +import { apiFetch } from "../core/api.js"; +import type { Collection } from "../types/project.js"; + +interface CollectionResponse { + data: Collection; + message: string; +} + +/** + * Creates a new collection inside a project. + * Endpoint: POST /projects/:projectId/collections + */ +export async function createCollection( + projectId: string, + payload: { name: string; model: Collection["model"] }, +): Promise { + const res = await apiFetch( + `/projects/${projectId}/collections`, + { + method: "POST", + body: JSON.stringify(payload), + }, + ); + return res.data; +} + +/** + * Deletes a collection from a project. + * Endpoint: DELETE /projects/:projectId/collections/:collectionName + */ +export async function deleteCollection( + projectId: string, + collectionName: string, +): Promise { + return apiFetch( + `/projects/${projectId}/collections/${collectionName}`, + { method: "DELETE" }, + ); +} diff --git a/sdks/urbackend-cli/src/services/project.service.ts b/sdks/urbackend-cli/src/services/project.service.ts new file mode 100644 index 000000000..423e837ca --- /dev/null +++ b/sdks/urbackend-cli/src/services/project.service.ts @@ -0,0 +1,39 @@ +import { apiFetch } from "../core/api.js"; +import type { Project, ProjectListItem } from "../types/project.js"; + +interface APIResponse { + success: boolean; + data: T; + message: string; +} + +export async function listProjects(): Promise { + const res = await apiFetch>("/projects", { + method: "GET", + }); + return res.data ?? []; +} + +export async function getProject(projectId: string): Promise { + const res = await apiFetch>(`/projects/${projectId}`, { + method: "GET", + }); + return res.data; +} + +export async function createProject(payload: { + name: string; + description?: string; +}): Promise { + const res = await apiFetch>("/projects", { + method: "POST", + body: JSON.stringify(payload), + }); + return res.data; +} + +export async function deleteProject(projectId: string): Promise { + return apiFetch(`/projects/${projectId}`, { + method: "DELETE", + }); +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/types/analytics.ts b/sdks/urbackend-cli/src/types/analytics.ts new file mode 100644 index 000000000..50ba008e7 --- /dev/null +++ b/sdks/urbackend-cli/src/types/analytics.ts @@ -0,0 +1,36 @@ +export interface GlobalStats { + plan: "free" | "pro"; + planExpiresAt: string | null; + limits: { + maxProjects: number; + maxCollections: number; + storageLimit: number; + databaseLimit: number; + }; + usage: { + totalProjects: number; + totalCollections: number; + totalStorageUsed: number; + totalDatabaseUsed: number; + totalRequests: number; + totalWebhooks: number; + totalUsers: number; + }; +} + +export interface RecentActivityLog { + id: string; + projectName: string; + projectId: string; + method: string; + path: string; + status: number; + timestamp: string; +} + +export interface ProjectAnalytics { + totalRequests: number; + successRequests: number; + errorRequests: number; + avgResponseTime?: number; +} diff --git a/sdks/urbackend-cli/src/types/auth.ts b/sdks/urbackend-cli/src/types/auth.ts index 3e9ac8542..914d2a6ed 100644 --- a/sdks/urbackend-cli/src/types/auth.ts +++ b/sdks/urbackend-cli/src/types/auth.ts @@ -1,18 +1,44 @@ -export interface AuthInfo { +export interface CLIDeveloper { + id: string; + email: string; + plan: "free" | "pro"; + githubUsername: string | null; + avatarUrl: string | null; +} + +export interface CLIAuth { scopes: string[]; - tokenType: string; + tokenType: "human" | "agent"; +} + +export interface CLIProfile { + developer: CLIDeveloper; + auth: CLIAuth; } -export interface DeveloperProfile { +export interface PAT { id: string; - email: string; - plan: string; - isVerified?: boolean; - githubUsername?: string | null; - avatarUrl?: string | null; + suffix: string; + label: string; + type: "human" | "agent"; + scopes: string[]; + createdAt: string; + expiresAt: string; + lastUsedAt: string | null; + lastUsedIp: string | null; } -export interface CLIProfile { - developer: DeveloperProfile; - auth: AuthInfo; -} \ No newline at end of file +export interface PATListResponse { + pats: PAT[]; +} + +export interface PATCreateResponse { + rawToken: string; + pat: { + suffix: string; + label: string; + type: string; + scopes: string[]; + expiresAt: string; + }; +} diff --git a/sdks/urbackend-cli/src/types/config.ts b/sdks/urbackend-cli/src/types/config.ts index 8cf8e0d9c..21fb68baf 100644 --- a/sdks/urbackend-cli/src/types/config.ts +++ b/sdks/urbackend-cli/src/types/config.ts @@ -2,4 +2,4 @@ export interface CLIConfig { apiBase: string; pat?: string; currentProject?: string; -} \ No newline at end of file +} diff --git a/sdks/urbackend-cli/src/types/project.ts b/sdks/urbackend-cli/src/types/project.ts new file mode 100644 index 000000000..4378fc541 --- /dev/null +++ b/sdks/urbackend-cli/src/types/project.ts @@ -0,0 +1,45 @@ +export interface CollectionField { + key: string; + type: "String" | "Number" | "Boolean" | "Date" | "Object" | "Array" | "Ref"; + required: boolean; + unique?: boolean; + ref?: string; + default?: unknown; + fields?: CollectionField[]; +} + +export interface CollectionRLS { + enabled: boolean; + mode: "public-read" | "private" | "owner-write-only"; + ownerField: string; + requireAuthForWrite: boolean; +} + +export interface Collection { + _id?: string; + name: string; + model: CollectionField[]; + rls?: CollectionRLS; +} + +export interface Project { + _id: string; + name: string; + description?: string; + publishableKey?: string; + collections: Collection[]; + isAuthEnabled?: boolean; + databaseUsed?: number; + storageUsed?: number; + updatedAt?: string; +} + +export interface ProjectListItem { + _id: string; + name: string; + description?: string; + collections: Collection[]; + databaseUsed?: number; + storageUsed?: number; + updatedAt?: string; +} diff --git a/sdks/urbackend-cli/src/utils/format.ts b/sdks/urbackend-cli/src/utils/format.ts new file mode 100644 index 000000000..e96fa6e3f --- /dev/null +++ b/sdks/urbackend-cli/src/utils/format.ts @@ -0,0 +1,29 @@ +/** + * Formats bytes into a human-readable string (KB, MB, GB). + */ +export function formatBytes(bytes: number): string { + if (bytes === 0) return "0 B"; + const units = ["B", "KB", "MB", "GB", "TB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + const value = (bytes / Math.pow(1024, i)).toFixed(2); + return `${value} ${units[i]}`; +} + +/** + * Formats an ISO date string to a locale-friendly short format. + */ +export function formatDate(iso: string | null | undefined): string { + if (!iso) return "Never"; + return new Date(iso).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); +} + +/** + * Pads a string label to a fixed width for aligned terminal output. + */ +export function label(text: string, width = 14): string { + return text.padEnd(width); +} diff --git a/sdks/urbackend-cli/src/utils/prompt.ts b/sdks/urbackend-cli/src/utils/prompt.ts new file mode 100644 index 000000000..eee3a3e28 --- /dev/null +++ b/sdks/urbackend-cli/src/utils/prompt.ts @@ -0,0 +1,22 @@ +import { createInterface } from "node:readline/promises"; +import { stdin, stdout } from "node:process"; + +/** + * Prompts the user for a single line of input. + * Automatically closes the readline interface after reading. + */ +export async function prompt(question: string): Promise { + const rl = createInterface({ input: stdin, output: stdout }); + const answer = (await rl.question(question)).trim(); + rl.close(); + return answer; +} + +/** + * Prompts for a yes/no confirmation. + * Returns true for "y" or "yes" (case-insensitive), false otherwise. + */ +export async function confirm(question: string): Promise { + const answer = await prompt(`${question} (y/n): `); + return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; +} diff --git a/sdks/urbackend-cli/src/utils/token.ts b/sdks/urbackend-cli/src/utils/token.ts index 7d0564459..6bec8199b 100644 --- a/sdks/urbackend-cli/src/utils/token.ts +++ b/sdks/urbackend-cli/src/utils/token.ts @@ -1,3 +1,7 @@ +/** + * Validates the format of a Personal Access Token. + * urBackend PATs always start with "ubpat_" followed by at least 10 characters. + */ export function isValidPAT(token: string): boolean { - return token.startsWith("ubpat_"); -} \ No newline at end of file + return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); +} diff --git a/sdks/urbackend-cli/tsconfig.json b/sdks/urbackend-cli/tsconfig.json index 2a4dbe9bf..97092a363 100644 --- a/sdks/urbackend-cli/tsconfig.json +++ b/sdks/urbackend-cli/tsconfig.json @@ -1,24 +1,20 @@ { "compilerOptions": { "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "dist", + "rootDir": "src", "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, "declaration": true, "declarationMap": true, "sourceMap": true, - "outDir": "dist", - "rootDir": ".", - "esModuleInterop": true, "resolveJsonModule": true, - "skipLibCheck": true + "forceConsistentCasingInFileNames": true }, - "include": [ - "src", - "index.ts" - ], - "exclude": [ - "dist", - "node_modules" - ] -} \ No newline at end of file + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/sdks/urbackend-cli/tsup.config.ts b/sdks/urbackend-cli/tsup.config.ts index 3f3422cf5..25b8d35e4 100644 --- a/sdks/urbackend-cli/tsup.config.ts +++ b/sdks/urbackend-cli/tsup.config.ts @@ -1,11 +1,15 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["index.ts"], + entry: ["src/index.ts"], format: ["esm", "cjs"], + target: "node20", + outDir: "dist", dts: true, sourcemap: true, clean: true, - outDir: "dist", - target: "node20" -}); \ No newline at end of file + splitting: false, + banner: { + js: "// @urbackend/cli — Official urBackend CLI", + }, +}); From 576c4cb927bafae60667aef88ce543d8f1f0624e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 20:46:50 +0530 Subject: [PATCH 06/14] final commit- official urBackend CLI --- apps/dashboard-api/src/app.js | 5 +- .../src/controllers/pat.controller.js | 1 + .../src/middlewares/authFlexible.js | 19 +++++ .../src/middlewares/authenticateCLI.js | 2 +- apps/dashboard-api/src/routes/analytics.js | 5 +- apps/dashboard-api/src/routes/projects.js | 5 +- sdks/urbackend-cli/dist/index.cjs | 56 ++++++-------- sdks/urbackend-cli/dist/index.cjs.map | 2 +- sdks/urbackend-cli/dist/index.js | 56 ++++++-------- sdks/urbackend-cli/dist/index.js.map | 2 +- .../src/commands/status/index.ts | 34 ++++----- .../src/services/analytics.service.ts | 76 +++++++++---------- .../src/services/auth.service.ts | 2 +- .../src/services/project.service.ts | 26 ++++--- sdks/urbackend-cli/src/types/analytics.ts | 17 ++--- 15 files changed, 156 insertions(+), 152 deletions(-) create mode 100644 apps/dashboard-api/src/middlewares/authFlexible.js diff --git a/apps/dashboard-api/src/app.js b/apps/dashboard-api/src/app.js index abbda5828..2c0ffcd9c 100644 --- a/apps/dashboard-api/src/app.js +++ b/apps/dashboard-api/src/app.js @@ -75,15 +75,16 @@ const csrfProtection = csurf({ } }); +// AFTER app.use((req, res, next) => { // Exclude Razorpay webhook from CSRF protection since it's an external POST request - if (req.path === '/api/billing/webhook') { + // Exclude CLI routes — CLI authenticates via Bearer PAT, not cookies + if (req.path === '/api/billing/webhook' || req.path.startsWith('/api/user/cli')) { return next(); } csrfProtection(req, res, next); }); - if (process.env.NODE_ENV !== 'test') { garbageCollect(); storageGarbageCollect(); diff --git a/apps/dashboard-api/src/controllers/pat.controller.js b/apps/dashboard-api/src/controllers/pat.controller.js index 60aebf8b1..d35492deb 100644 --- a/apps/dashboard-api/src/controllers/pat.controller.js +++ b/apps/dashboard-api/src/controllers/pat.controller.js @@ -93,3 +93,4 @@ exports.revokePAT = async (req, res, next) => { return next(new AppError(500, "An error occurred while revoking the token")); } }; + diff --git a/apps/dashboard-api/src/middlewares/authFlexible.js b/apps/dashboard-api/src/middlewares/authFlexible.js new file mode 100644 index 000000000..d1a9c702f --- /dev/null +++ b/apps/dashboard-api/src/middlewares/authFlexible.js @@ -0,0 +1,19 @@ +const authMiddleware = require('./authMiddleware'); +const authenticateCLI = require('./authenticateCLI'); + +/** + * Flexible auth middleware — accepts either: + * - Session cookie (browser dashboard) + * - Bearer PAT token (CLI) + */ +const authFlexible = (req, res, next) => { + const authHeader = req.headers.authorization; + + if (authHeader && authHeader.startsWith('Bearer ubpat_')) { + return authenticateCLI(req, res, next); + } + + return authMiddleware(req, res, next); +}; + +module.exports = authFlexible; \ No newline at end of file diff --git a/apps/dashboard-api/src/middlewares/authenticateCLI.js b/apps/dashboard-api/src/middlewares/authenticateCLI.js index 6b987aa05..676e3b99c 100644 --- a/apps/dashboard-api/src/middlewares/authenticateCLI.js +++ b/apps/dashboard-api/src/middlewares/authenticateCLI.js @@ -110,7 +110,7 @@ const authenticateCLI = async (req, res, next) => { ).catch(err => console.error("Failed to update PAT metadata:", err)); // Attach developer and PAT scopes for downstream controllers - req.user = { id: developer._id.toString() }; // Maintain compatibility with dashboard controllers + req.user = { _id: developer._id.toString(), id: developer._id.toString() }; // Maintain compatibility with dashboard controllers req.developer = developer; req.cliScopes = matchedPat.scopes; req.cliTokenType = matchedPat.type; diff --git a/apps/dashboard-api/src/routes/analytics.js b/apps/dashboard-api/src/routes/analytics.js index 6ad929e7f..fe5ce223b 100644 --- a/apps/dashboard-api/src/routes/analytics.js +++ b/apps/dashboard-api/src/routes/analytics.js @@ -2,9 +2,10 @@ const express = require("express"); const router = express.Router(); const analyticsController = require("../controllers/analytics.controller"); const authMiddleware = require("../middlewares/authMiddleware"); +const authFlexible = require("../middlewares/authFlexible"); -router.get("/stats", authMiddleware, analyticsController.getGlobalStats); -router.get("/activity", authMiddleware, analyticsController.getRecentActivity); +router.get("/stats", authFlexible, analyticsController.getGlobalStats); +router.get("/activity", authFlexible, analyticsController.getRecentActivity); // --- Metrics Stack --- router.get("/funnel", authMiddleware, analyticsController.getActivationFunnel); diff --git a/apps/dashboard-api/src/routes/projects.js b/apps/dashboard-api/src/routes/projects.js index b66377c13..917bf5a9a 100644 --- a/apps/dashboard-api/src/routes/projects.js +++ b/apps/dashboard-api/src/routes/projects.js @@ -6,6 +6,7 @@ const authorizeProject = require('../middlewares/authorizeProject'); const { verifyEmail, checkAuthEnabled, loadProjectForAdmin } = require('@urbackend/common'); const multer = require('multer'); const storage = multer.memoryStorage(); +const authFlexible = require('../middlewares/authFlexible'); const { createProject, @@ -61,8 +62,8 @@ const exportController = require('../controllers/dbExport.controller'); // POST REQ FOR CREATE PROJECT router.post('/', authMiddleware, planEnforcement.attachDeveloper, planEnforcement.checkProjectLimit, planEnforcement.checkDeveloperCapability('createProject'), createProject); -router.get('/', authMiddleware, getAllProject); -router.get('/:projectId', authMiddleware, authorizeProject(), getSingleProject); +router.get('/', authFlexible, getAllProject); +router.get('/:projectId', authFlexible, authorizeProject(), getSingleProject); router.post('/:projectId/api-key', authMiddleware, authorizeProject('admin'), verifyEmail, regenerateApiKey); router.post('/:projectId/reveal-secret-key', authMiddleware, authorizeProject('admin'), verifyEmail, revealSecretKey); diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs index cb4e2fbb9..45dcb86d7 100644 --- a/sdks/urbackend-cli/dist/index.cjs +++ b/sdks/urbackend-cli/dist/index.cjs @@ -142,7 +142,6 @@ async function authenticate(token) { method: "GET", token }); - console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); return res.data; } async function getProfile() { @@ -179,14 +178,6 @@ function formatBytes(bytes) { const value = (bytes / Math.pow(1024, i)).toFixed(2); return `${value} ${units[i]}`; } -function formatDate(iso) { - if (!iso) return "Never"; - return new Date(iso).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric" - }); -} function label(text, width = 14) { return text.padEnd(width); } @@ -286,16 +277,20 @@ async function whoamiCommand() { // src/services/project.service.ts async function listProjects() { - const res = await apiFetch("/projects", { - method: "GET" - }); + const res = await apiFetch( + "/projects", + { method: "GET" } + ); + if (Array.isArray(res)) return res; return res.data ?? []; } async function getProject(projectId) { - const res = await apiFetch(`/projects/${projectId}`, { - method: "GET" - }); - return res.data; + const res = await apiFetch( + `/projects/${projectId}`, + { method: "GET" } + ); + if ("data" in res && res.data) return res.data; + return res; } // src/commands/project/list.ts @@ -561,9 +556,10 @@ async function getGlobalStats() { return res.data; } async function getRecentActivity() { - const res = await apiFetch("/analytics/activity", { - method: "GET" - }); + const res = await apiFetch( + "/analytics/activity", + { method: "GET" } + ); return Array.isArray(res.data) ? res.data : []; } @@ -586,7 +582,7 @@ async function statusCommand() { console.log("\n\u2500\u2500 Account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); if (stats.planExpiresAt) { - console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); + console.log(`${label("Plan expires")} ${stats.planExpiresAt}`); } console.log("\n\u2500\u2500 Usage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log( @@ -596,13 +592,15 @@ async function statusCommand() { `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}` ); console.log( - `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}` + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}` ); console.log( - `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}` + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}` ); - console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); - console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log( + `${label("API requests")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today` + ); + console.log(`${label("Auth users")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`); console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); if (currentProjectId) { try { @@ -610,16 +608,12 @@ async function statusCommand() { console.log("\n\u2500\u2500 Active project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log(`${label("Name")} ${project.name}`); console.log(`${label("ID")} ${project._id}`); - console.log( - `${label("Collections")} ${project.collections?.length ?? 0}` - ); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); } catch { } } else { - console.log( - "\nTip: Run 'ub project use' to select a project and see per-project details." - ); + console.log("\nTip: Run 'ub project use' to select a project."); } try { const activity = await getRecentActivity(); @@ -629,7 +623,7 @@ async function statusCommand() { const icon = statusIcon(log.status); const time = new Date(log.timestamp).toLocaleTimeString(); console.log( - ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}` + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}` ); } } diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map index f8a6d6250..713c27f64 100644 --- a/sdks/urbackend-cli/dist/index.cjs.map +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/auth/login.js\";\nimport { logoutCommand } from \"./commands/auth/logout.js\";\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\nimport { projectListCommand } from \"./commands/project/list.js\";\nimport { projectUseCommand } from \"./commands/project/use.js\";\nimport { projectInfoCommand } from \"./commands/project/info.js\";\nimport { collectionListCommand } from \"./commands/collection/list.js\";\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\nimport { statusCommand } from \"./commands/status/index.js\";\nimport { doctorCommand } from \"./commands/doctor/index.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ub\")\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\n .version(\"0.1.0\");\n\n// ── Authentication ──────────────────────────────────────────────────────────\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with a Personal Access Token\")\n .action(loginCommand);\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials from this machine\")\n .action(logoutCommand);\n\nprogram\n .command(\"whoami\")\n .description(\"Show the currently authenticated developer\")\n .action(whoamiCommand);\n\n// ── Projects ─────────────────────────────────────────────────────────────────\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage urBackend projects\");\n\nprojectCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all accessible projects\")\n .action(projectListCommand);\n\nprojectCmd\n .command(\"use [projectIdOrName]\")\n .description(\"Set the active project for subsequent commands\")\n .action(projectUseCommand);\n\nprojectCmd\n .command(\"info [projectId]\")\n .description(\"Show details for the active (or specified) project\")\n .action(projectInfoCommand);\n\n// ── Collections ──────────────────────────────────────────────────────────────\n\nconst collectionCmd = program\n .command(\"collection\")\n .alias(\"col\")\n .description(\"Manage collections inside the active project\");\n\ncollectionCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all collections in the active project\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((options) => collectionListCommand(options.project));\n\ncollectionCmd\n .command(\"delete \")\n .alias(\"rm\")\n .description(\"Delete a collection and all its data\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((name, options) => collectionDeleteCommand(name, options));\n\n// ── Status ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"status\")\n .description(\"Show account usage and recent API activity\")\n .action(statusCommand);\n\n// ── Doctor ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"doctor\")\n .description(\"Run diagnostic checks on your CLI setup\")\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\n .action((options) => doctorCommand(options));\n\n// ── Parse ─────────────────────────────────────────────────────────────────────\n\nprogram.parseAsync(process.argv).catch((err: Error) => {\n console.error(`✖ ${err.message}`);\n process.exit(1);\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { CLIConfig } from \"../types/config.js\";\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\n\nconst DEFAULT_CONFIG: CLIConfig = {\n apiBase: DEFAULT_API_BASE,\n};\n\nexport function loadConfig(): CLIConfig {\n if (!fs.existsSync(CONFIG_PATH)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\n return {\n ...DEFAULT_CONFIG,\n ...JSON.parse(raw),\n };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(config: CLIConfig): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n\n // Atomic write: write to temp file then rename to avoid corruption on crash\n const tmp = CONFIG_PATH + \".tmp\";\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\n fs.renameSync(tmp, CONFIG_PATH);\n}\n\nexport function saveToken(token: string): void {\n const config = loadConfig();\n saveConfig({ ...config, pat: token });\n}\n\nexport function clearToken(): void {\n const config = loadConfig();\n delete config.pat;\n delete config.currentProject;\n saveConfig(config);\n}\n\nexport function getToken(): string | undefined {\n return loadConfig().pat;\n}\n\nexport function saveCurrentProject(projectId: string): void {\n const config = loadConfig();\n saveConfig({ ...config, currentProject: projectId });\n}\n\nexport function getCurrentProject(): string | undefined {\n return loadConfig().currentProject;\n}\n\nexport function configFilePath(): string {\n return CONFIG_PATH;\n}\n\nexport function configDirPath(): string {\n return path.dirname(CONFIG_PATH);\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport const CLI_NAME = \"ub\";\n\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\n\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/**\n * Default production API base URL.\n * Override via URBACKEND_API_URL env variable for local development.\n */\nexport const DEFAULT_API_BASE =\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\n","export class APIError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"APIError\";\n }\n}\n","import { loadConfig } from \"./config.js\";\nimport { APIError } from \"./errors.js\";\n\nexport interface FetchOptions extends RequestInit {\n /** Override the PAT for this single request (e.g. during login verification) */\n token?: string;\n}\n\n/**\n * Central HTTP client for all CLI → dashboard-api communication.\n * Automatically injects the stored PAT as a Bearer token.\n *\n * All dashboard-api routes are mounted under /api — this client\n * prepends that prefix automatically so callers just write /user/cli/me.\n */\nexport async function apiFetch(\n endpoint: string,\n options: FetchOptions = {},\n): Promise {\n const config = loadConfig();\n\n const headers = new Headers(options.headers);\n\n if (options.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n const token = options.token ?? config.pat;\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n\n // All dashboard-api routes are under /api\n const url = `${config.apiBase}/api${endpoint}`;\n\n let response: Response;\n\n try {\n response = await fetch(url, {\n ...options,\n headers,\n });\n } catch {\n throw new APIError(\n 0,\n \"Unable to connect to the urBackend API. Is the server running?\",\n );\n }\n\n if (!response.ok) {\n let message = response.statusText;\n\n try {\n const body = (await response.json()) as {\n message?: string;\n error?: string;\n };\n message = body.message ?? body.error ?? message;\n } catch {\n // ignore — use statusText\n }\n\n throw new APIError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}","import { apiFetch } from \"../core/api.js\";\nimport type { CLIProfile } from \"../types/auth.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function authenticate(token: string): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n token,\n });\n console.log(\"DEBUG raw response:\", JSON.stringify(res, null, 2));\n return res.data;\n}\n\nexport async function getProfile(): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n });\n return res.data;\n}","/**\n * Validates the format of a Personal Access Token.\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\n */\nexport function isValidPAT(token: string): boolean {\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\n\n/**\n * Prompts the user for a single line of input.\n * Automatically closes the readline interface after reading.\n */\nexport async function prompt(question: string): Promise {\n const rl = createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(question)).trim();\n rl.close();\n return answer;\n}\n\n/**\n * Prompts for a yes/no confirmation.\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\n */\nexport async function confirm(question: string): Promise {\n const answer = await prompt(`${question} (y/n): `);\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n","/**\n * Formats bytes into a human-readable string (KB, MB, GB).\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\n return `${value} ${units[i]}`;\n}\n\n/**\n * Formats an ISO date string to a locale-friendly short format.\n */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"Never\";\n return new Date(iso).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * Pads a string label to a fixed width for aligned terminal output.\n */\nexport function label(text: string, width = 14): string {\n return text.padEnd(width);\n}\n","export const logger = {\n success(message: string): void {\n console.log(`✓ ${message}`);\n },\n\n error(message: string): void {\n console.error(`✖ ${message}`);\n },\n\n warn(message: string): void {\n console.warn(`⚠ ${message}`);\n },\n\n info(message: string): void {\n console.log(message);\n },\n};\n","import { authenticate } from \"../../services/auth.service.js\";\nimport { saveToken } from \"../../core/config.js\";\nimport { isValidPAT } from \"../../utils/token.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function loginCommand(): Promise {\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\n console.log(\" Settings → Access Tokens → New Token\\n\");\n\n const token = await prompt(\"Paste your Personal Access Token: \");\n\n if (!isValidPAT(token)) {\n logger.error(\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\n );\n return;\n }\n\n try {\n const profile = await authenticate(token);\n\n saveToken(token);\n\n logger.success(\"Logged in successfully.\\n\");\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { clearToken } from \"../../core/config.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function logoutCommand(): Promise {\n clearToken();\n logger.success(\"Logged out. Your token has been removed from this machine.\");\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function whoamiCommand(): Promise {\n const token = getToken();\n\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const profile = await getProfile();\n\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n\n const currentProject = getCurrentProject();\n if (currentProject) {\n console.log(`${label(\"Project\")} ${currentProject}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Project, ProjectListItem } from \"../types/project.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function listProjects(): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"GET\",\n });\n return res.data ?? [];\n}\n\nexport async function getProject(projectId: string): Promise {\n const res = await apiFetch>(`/projects/${projectId}`, {\n method: \"GET\",\n });\n return res.data;\n}\n\nexport async function createProject(payload: {\n name: string;\n description?: string;\n}): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n return res.data;\n}\n\nexport async function deleteProject(projectId: string): Promise {\n return apiFetch(`/projects/${projectId}`, {\n method: \"DELETE\",\n });\n}","import { listProjects } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectListCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n const currentProjectId = getCurrentProject();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n console.log(`\\nFound ${projects.length} project(s):\\n`);\n\n for (const project of projects) {\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\n const db = formatBytes(project.databaseUsed ?? 0);\n const colCount = project.collections?.length ?? 0;\n\n console.log(` ${project.name}${active}`);\n console.log(` ID ${project._id}`);\n console.log(` Collections ${colCount}`);\n console.log(` DB used ${db}`);\n\n if (project.description) {\n console.log(` Description ${project.description}`);\n }\n\n console.log();\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { listProjects, getProject } from \"../../services/project.service.js\";\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n let selectedId: string | undefined;\n\n if (projectIdOrName) {\n // Try matching by ID or name\n const match = projects.find(\n (p) =>\n p._id === projectIdOrName ||\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\n );\n if (!match) {\n logger.error(`No project found matching \"${projectIdOrName}\".`);\n logger.info(\"Run 'ub project list' to see available projects.\");\n return;\n }\n selectedId = match._id;\n } else {\n // Interactive selection\n console.log(\"\\nAvailable projects:\\n\");\n projects.forEach((p, i) => {\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\n });\n console.log();\n\n const answer = await prompt(\"Enter project number: \");\n const index = parseInt(answer, 10) - 1;\n\n if (isNaN(index) || index < 0 || index >= projects.length) {\n logger.error(\"Invalid selection.\");\n return;\n }\n\n selectedId = projects[index]._id;\n }\n\n saveCurrentProject(selectedId);\n\n const project = projects.find((p) => p._id === selectedId)!;\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes, label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectInfoCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n\n console.log(`\\n${project.name}\\n`);\n console.log(`${label(\"ID\")} ${project._id}`);\n\n if (project.description) {\n console.log(`${label(\"Description\")} ${project.description}`);\n }\n\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\n\n if (project.collections && project.collections.length > 0) {\n console.log(\"\\nCollections:\");\n for (const col of project.collections) {\n const fieldCount = col.model?.length ?? 0;\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\n }\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function collectionListCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n const collections = project.collections ?? [];\n\n if (collections.length === 0) {\n logger.info(`No collections in project \"${project.name}\".`);\n return;\n }\n\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\n\n for (const col of collections) {\n const fieldCount = col.model?.length ?? 0;\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\n\n console.log(` ${col.name}`);\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\n\n if (col.model && col.model.length > 0) {\n const fieldSummary = col.model\n .slice(0, 5)\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\n .join(\", \");\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\n }\n\n console.log();\n }\n\n console.log(\" * = required field\");\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found or access denied.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Collection } from \"../types/project.js\";\n\ninterface CollectionResponse {\n data: Collection;\n message: string;\n}\n\n/**\n * Creates a new collection inside a project.\n * Endpoint: POST /projects/:projectId/collections\n */\nexport async function createCollection(\n projectId: string,\n payload: { name: string; model: Collection[\"model\"] },\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/collections`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n return res.data;\n}\n\n/**\n * Deletes a collection from a project.\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\n */\nexport async function deleteCollection(\n projectId: string,\n collectionName: string,\n): Promise {\n return apiFetch(\n `/projects/${projectId}/collections/${collectionName}`,\n { method: \"DELETE\" },\n );\n}\n","import { deleteCollection } from \"../../services/collection.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { confirm } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface CollectionDeleteOptions {\n force?: boolean;\n project?: string;\n}\n\nexport async function collectionDeleteCommand(\n collectionName: string,\n options: CollectionDeleteOptions,\n): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const projectId = options.project ?? getCurrentProject();\n if (!projectId) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n // Verify the collection actually exists before asking for confirmation\n const project = await getProject(projectId);\n const exists = project.collections?.some((c) => c.name === collectionName);\n\n if (!exists) {\n logger.error(\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\n );\n return;\n }\n\n if (!options.force) {\n console.log(\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\n );\n const ok = await confirm(\"Are you sure?\");\n if (!ok) {\n logger.info(\"Aborted.\");\n return;\n }\n }\n\n await deleteCollection(projectId, collectionName);\n logger.success(`Collection \"${collectionName}\" deleted.`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Collection or project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { GlobalStats, RecentActivityLog } from \"../types/analytics.js\";\n\ninterface GlobalStatsResponse {\n data: GlobalStats;\n}\n\ninterface RecentActivityResponse {\n data: RecentActivityLog[];\n}\n\ninterface ProjectAnalyticsResponse {\n data: {\n totalRequests: number;\n successRequests: number;\n errorRequests: number;\n topCollections?: { name: string; requests: number }[];\n };\n}\n\n/**\n * Fetches global usage stats across all developer projects.\n * Endpoint: GET /analytics/stats\n */\nexport async function getGlobalStats(): Promise {\n const res = await apiFetch(\"/analytics/stats\", {\n method: \"GET\",\n });\n return res.data;\n}\n\n/**\n * Fetches the 20 most recent API logs across all projects.\n * Endpoint: GET /analytics/activity\n */\nexport async function getRecentActivity(): Promise {\n const res = await apiFetch(\"/analytics/activity\", {\n method: \"GET\",\n });\n return Array.isArray(res.data) ? res.data : [];\n}\n\n/**\n * Fetches per-project analytics.\n * Endpoint: GET /projects/:projectId/analytics?range=last24h\n */\nexport async function getProjectAnalytics(\n projectId: string,\n range: \"last1h\" | \"last24h\" | \"last7d\" | \"last30d\" | \"allTime\" = \"last24h\",\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/analytics?range=${range}`,\n { method: \"GET\" },\n );\n return res.data;\n}\n","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { formatBytes, label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nfunction statusIcon(status: number): string {\n if (status < 300) return \"✓\";\n if (status < 400) return \"→\";\n if (status < 500) return \"⚠\";\n return \"✖\";\n}\n\nexport async function statusCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const currentProjectId = getCurrentProject();\n\n try {\n // Fetch global stats (works without a current project)\n const stats = await getGlobalStats();\n\n console.log(\"\\n── Account ─────────────────────────────────────\");\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\n\n if (stats.planExpiresAt) {\n console.log(`${label(\"Plan expires\")} ${formatDate(stats.planExpiresAt)}`);\n }\n\n console.log(\"\\n── Usage ────────────────────────────────────────\");\n console.log(\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\n );\n console.log(\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\n );\n console.log(\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`,\n );\n console.log(\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`,\n );\n console.log(`${label(\"Total requests\")} ${stats.usage.totalRequests.toLocaleString()}`);\n console.log(`${label(\"Total users\")} ${stats.usage.totalUsers.toLocaleString()}`);\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\n\n // Per-project detail if a project is active\n if (currentProjectId) {\n try {\n const project = await getProject(currentProjectId);\n console.log(\"\\n── Active project ───────────────────────────────\");\n console.log(`${label(\"Name\")} ${project.name}`);\n console.log(`${label(\"ID\")} ${project._id}`);\n console.log(\n `${label(\"Collections\")} ${project.collections?.length ?? 0}`,\n );\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n } catch {\n // Non-fatal — global stats already displayed\n }\n } else {\n console.log(\n \"\\nTip: Run 'ub project use' to select a project and see per-project details.\",\n );\n }\n\n // Recent activity\n try {\n const activity = await getRecentActivity();\n\n if (activity.length > 0) {\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\n for (const log of activity.slice(0, 10)) {\n const icon = statusIcon(log.status);\n const time = new Date(log.timestamp).toLocaleTimeString();\n console.log(\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`,\n );\n }\n }\n } catch {\n // Non-fatal — skip activity if it fails\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport {\n getToken,\n getCurrentProject,\n loadConfig,\n configFilePath,\n} from \"../../core/config.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\ninterface DoctorResult {\n configFound: boolean;\n patValid: boolean;\n projectSelected: boolean;\n projectAccessible: boolean;\n dashboardApiReachable: boolean;\n dashboardApiLatencyMs: number | null;\n email: string | null;\n plan: string | null;\n projectName: string | null;\n}\n\nexport interface DoctorOptions {\n json?: boolean;\n}\n\nasync function checkApiReachable(apiBase: string): Promise {\n const start = Date.now();\n try {\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\n return Date.now() - start;\n } catch {\n return null;\n }\n}\n\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\n const config = loadConfig();\n const token = getToken();\n const currentProjectId = getCurrentProject();\n\n const result: DoctorResult = {\n configFound: false,\n patValid: false,\n projectSelected: !!currentProjectId,\n projectAccessible: false,\n dashboardApiReachable: false,\n dashboardApiLatencyMs: null,\n email: null,\n plan: null,\n projectName: null,\n };\n\n // 1. Config file\n try {\n const fs = await import(\"node:fs\");\n result.configFound = fs.existsSync(configFilePath());\n } catch {\n result.configFound = false;\n }\n\n // 2. API reachability (independent of auth)\n const latency = await checkApiReachable(config.apiBase);\n result.dashboardApiReachable = latency !== null;\n result.dashboardApiLatencyMs = latency;\n\n // 3. PAT validity\n if (token) {\n try {\n const profile = await getProfile();\n result.patValid = true;\n result.email = profile.developer.email;\n result.plan = profile.developer.plan;\n } catch {\n result.patValid = false;\n }\n }\n\n // 4. Project accessibility\n if (currentProjectId && result.patValid) {\n try {\n const project = await getProject(currentProjectId);\n result.projectAccessible = true;\n result.projectName = project.name;\n } catch {\n result.projectAccessible = false;\n }\n }\n\n // Output\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\n\n const ok = \"✓\";\n const fail = \"✖\";\n const warn = \"⚠\";\n\n console.log(\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\n );\n\n console.log(\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\n result.dashboardApiReachable\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\n : `Unreachable — ${config.apiBase}`\n }`,\n );\n\n if (!token) {\n console.log(` ${fail} PAT Not set — run 'ub login'`);\n } else {\n console.log(\n ` ${result.patValid ? ok : fail} PAT ${\n result.patValid\n ? `Valid (${result.email}, ${result.plan})`\n : \"Invalid or expired — run 'ub login'\"\n }`,\n );\n }\n\n if (!currentProjectId) {\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\n } else {\n console.log(\n ` ${result.projectAccessible ? ok : fail} Active project ${\n result.projectAccessible\n ? `${result.projectName} (${currentProjectId})`\n : `ID ${currentProjectId} — not found or access denied`\n }`,\n );\n }\n\n console.log();\n\n // Summary\n const allGood =\n result.configFound &&\n result.patValid &&\n result.dashboardApiReachable &&\n result.projectSelected &&\n result.projectAccessible;\n\n if (allGood) {\n logger.success(\"Everything looks good.\");\n } else {\n logger.warn(\"Some checks failed. Review the output above.\");\n }\n\n console.log();\n}\n"],"mappings":"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","names":["import_node_path","path","os","fs","fs"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, pat: token });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const url = `${config.apiBase}/api${endpoint}`;\r\n\r\n let response: Response;\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n });\r\n } catch {\r\n throw new APIError(\r\n 0,\r\n \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await prompt(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n const index = parseInt(answer, 10) - 1;\r\n\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${collectionName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n return Array.isArray(res.data) ? res.data : [];\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":"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","names":["import_node_path","path","os","fs","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js index e29b7c200..8256b67c1 100644 --- a/sdks/urbackend-cli/dist/index.js +++ b/sdks/urbackend-cli/dist/index.js @@ -119,7 +119,6 @@ async function authenticate(token) { method: "GET", token }); - console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); return res.data; } async function getProfile() { @@ -156,14 +155,6 @@ function formatBytes(bytes) { const value = (bytes / Math.pow(1024, i)).toFixed(2); return `${value} ${units[i]}`; } -function formatDate(iso) { - if (!iso) return "Never"; - return new Date(iso).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric" - }); -} function label(text, width = 14) { return text.padEnd(width); } @@ -263,16 +254,20 @@ async function whoamiCommand() { // src/services/project.service.ts async function listProjects() { - const res = await apiFetch("/projects", { - method: "GET" - }); + const res = await apiFetch( + "/projects", + { method: "GET" } + ); + if (Array.isArray(res)) return res; return res.data ?? []; } async function getProject(projectId) { - const res = await apiFetch(`/projects/${projectId}`, { - method: "GET" - }); - return res.data; + const res = await apiFetch( + `/projects/${projectId}`, + { method: "GET" } + ); + if ("data" in res && res.data) return res.data; + return res; } // src/commands/project/list.ts @@ -538,9 +533,10 @@ async function getGlobalStats() { return res.data; } async function getRecentActivity() { - const res = await apiFetch("/analytics/activity", { - method: "GET" - }); + const res = await apiFetch( + "/analytics/activity", + { method: "GET" } + ); return Array.isArray(res.data) ? res.data : []; } @@ -563,7 +559,7 @@ async function statusCommand() { console.log("\n\u2500\u2500 Account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); if (stats.planExpiresAt) { - console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); + console.log(`${label("Plan expires")} ${stats.planExpiresAt}`); } console.log("\n\u2500\u2500 Usage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log( @@ -573,13 +569,15 @@ async function statusCommand() { `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}` ); console.log( - `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}` + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}` ); console.log( - `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}` + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}` ); - console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); - console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log( + `${label("API requests")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today` + ); + console.log(`${label("Auth users")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`); console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); if (currentProjectId) { try { @@ -587,16 +585,12 @@ async function statusCommand() { console.log("\n\u2500\u2500 Active project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); console.log(`${label("Name")} ${project.name}`); console.log(`${label("ID")} ${project._id}`); - console.log( - `${label("Collections")} ${project.collections?.length ?? 0}` - ); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); } catch { } } else { - console.log( - "\nTip: Run 'ub project use' to select a project and see per-project details." - ); + console.log("\nTip: Run 'ub project use' to select a project."); } try { const activity = await getRecentActivity(); @@ -606,7 +600,7 @@ async function statusCommand() { const icon = statusIcon(log.status); const time = new Date(log.timestamp).toLocaleTimeString(); console.log( - ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}` + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}` ); } } diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map index 9d93b94d0..66d0258af 100644 --- a/sdks/urbackend-cli/dist/index.js.map +++ b/sdks/urbackend-cli/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/auth/login.js\";\nimport { logoutCommand } from \"./commands/auth/logout.js\";\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\nimport { projectListCommand } from \"./commands/project/list.js\";\nimport { projectUseCommand } from \"./commands/project/use.js\";\nimport { projectInfoCommand } from \"./commands/project/info.js\";\nimport { collectionListCommand } from \"./commands/collection/list.js\";\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\nimport { statusCommand } from \"./commands/status/index.js\";\nimport { doctorCommand } from \"./commands/doctor/index.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ub\")\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\n .version(\"0.1.0\");\n\n// ── Authentication ──────────────────────────────────────────────────────────\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with a Personal Access Token\")\n .action(loginCommand);\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials from this machine\")\n .action(logoutCommand);\n\nprogram\n .command(\"whoami\")\n .description(\"Show the currently authenticated developer\")\n .action(whoamiCommand);\n\n// ── Projects ─────────────────────────────────────────────────────────────────\n\nconst projectCmd = program\n .command(\"project\")\n .description(\"Manage urBackend projects\");\n\nprojectCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all accessible projects\")\n .action(projectListCommand);\n\nprojectCmd\n .command(\"use [projectIdOrName]\")\n .description(\"Set the active project for subsequent commands\")\n .action(projectUseCommand);\n\nprojectCmd\n .command(\"info [projectId]\")\n .description(\"Show details for the active (or specified) project\")\n .action(projectInfoCommand);\n\n// ── Collections ──────────────────────────────────────────────────────────────\n\nconst collectionCmd = program\n .command(\"collection\")\n .alias(\"col\")\n .description(\"Manage collections inside the active project\");\n\ncollectionCmd\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all collections in the active project\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((options) => collectionListCommand(options.project));\n\ncollectionCmd\n .command(\"delete \")\n .alias(\"rm\")\n .description(\"Delete a collection and all its data\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .option(\"-p, --project \", \"Target a specific project ID\")\n .action((name, options) => collectionDeleteCommand(name, options));\n\n// ── Status ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"status\")\n .description(\"Show account usage and recent API activity\")\n .action(statusCommand);\n\n// ── Doctor ────────────────────────────────────────────────────────────────────\n\nprogram\n .command(\"doctor\")\n .description(\"Run diagnostic checks on your CLI setup\")\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\n .action((options) => doctorCommand(options));\n\n// ── Parse ─────────────────────────────────────────────────────────────────────\n\nprogram.parseAsync(process.argv).catch((err: Error) => {\n console.error(`✖ ${err.message}`);\n process.exit(1);\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { CLIConfig } from \"../types/config.js\";\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\n\nconst DEFAULT_CONFIG: CLIConfig = {\n apiBase: DEFAULT_API_BASE,\n};\n\nexport function loadConfig(): CLIConfig {\n if (!fs.existsSync(CONFIG_PATH)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\n return {\n ...DEFAULT_CONFIG,\n ...JSON.parse(raw),\n };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(config: CLIConfig): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n\n // Atomic write: write to temp file then rename to avoid corruption on crash\n const tmp = CONFIG_PATH + \".tmp\";\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\n fs.renameSync(tmp, CONFIG_PATH);\n}\n\nexport function saveToken(token: string): void {\n const config = loadConfig();\n saveConfig({ ...config, pat: token });\n}\n\nexport function clearToken(): void {\n const config = loadConfig();\n delete config.pat;\n delete config.currentProject;\n saveConfig(config);\n}\n\nexport function getToken(): string | undefined {\n return loadConfig().pat;\n}\n\nexport function saveCurrentProject(projectId: string): void {\n const config = loadConfig();\n saveConfig({ ...config, currentProject: projectId });\n}\n\nexport function getCurrentProject(): string | undefined {\n return loadConfig().currentProject;\n}\n\nexport function configFilePath(): string {\n return CONFIG_PATH;\n}\n\nexport function configDirPath(): string {\n return path.dirname(CONFIG_PATH);\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport const CLI_NAME = \"ub\";\n\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\n\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\n\n/**\n * Default production API base URL.\n * Override via URBACKEND_API_URL env variable for local development.\n */\nexport const DEFAULT_API_BASE =\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\n","export class APIError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"APIError\";\n }\n}\n","import { loadConfig } from \"./config.js\";\nimport { APIError } from \"./errors.js\";\n\nexport interface FetchOptions extends RequestInit {\n /** Override the PAT for this single request (e.g. during login verification) */\n token?: string;\n}\n\n/**\n * Central HTTP client for all CLI → dashboard-api communication.\n * Automatically injects the stored PAT as a Bearer token.\n *\n * All dashboard-api routes are mounted under /api — this client\n * prepends that prefix automatically so callers just write /user/cli/me.\n */\nexport async function apiFetch(\n endpoint: string,\n options: FetchOptions = {},\n): Promise {\n const config = loadConfig();\n\n const headers = new Headers(options.headers);\n\n if (options.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n const token = options.token ?? config.pat;\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n\n // All dashboard-api routes are under /api\n const url = `${config.apiBase}/api${endpoint}`;\n\n let response: Response;\n\n try {\n response = await fetch(url, {\n ...options,\n headers,\n });\n } catch {\n throw new APIError(\n 0,\n \"Unable to connect to the urBackend API. Is the server running?\",\n );\n }\n\n if (!response.ok) {\n let message = response.statusText;\n\n try {\n const body = (await response.json()) as {\n message?: string;\n error?: string;\n };\n message = body.message ?? body.error ?? message;\n } catch {\n // ignore — use statusText\n }\n\n throw new APIError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}","import { apiFetch } from \"../core/api.js\";\nimport type { CLIProfile } from \"../types/auth.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function authenticate(token: string): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n token,\n });\n console.log(\"DEBUG raw response:\", JSON.stringify(res, null, 2));\n return res.data;\n}\n\nexport async function getProfile(): Promise {\n const res = await apiFetch>(\"/user/cli/me\", {\n method: \"GET\",\n });\n return res.data;\n}","/**\n * Validates the format of a Personal Access Token.\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\n */\nexport function isValidPAT(token: string): boolean {\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\n\n/**\n * Prompts the user for a single line of input.\n * Automatically closes the readline interface after reading.\n */\nexport async function prompt(question: string): Promise {\n const rl = createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(question)).trim();\n rl.close();\n return answer;\n}\n\n/**\n * Prompts for a yes/no confirmation.\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\n */\nexport async function confirm(question: string): Promise {\n const answer = await prompt(`${question} (y/n): `);\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n","/**\n * Formats bytes into a human-readable string (KB, MB, GB).\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\n return `${value} ${units[i]}`;\n}\n\n/**\n * Formats an ISO date string to a locale-friendly short format.\n */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"Never\";\n return new Date(iso).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * Pads a string label to a fixed width for aligned terminal output.\n */\nexport function label(text: string, width = 14): string {\n return text.padEnd(width);\n}\n","export const logger = {\n success(message: string): void {\n console.log(`✓ ${message}`);\n },\n\n error(message: string): void {\n console.error(`✖ ${message}`);\n },\n\n warn(message: string): void {\n console.warn(`⚠ ${message}`);\n },\n\n info(message: string): void {\n console.log(message);\n },\n};\n","import { authenticate } from \"../../services/auth.service.js\";\nimport { saveToken } from \"../../core/config.js\";\nimport { isValidPAT } from \"../../utils/token.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function loginCommand(): Promise {\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\n console.log(\" Settings → Access Tokens → New Token\\n\");\n\n const token = await prompt(\"Paste your Personal Access Token: \");\n\n if (!isValidPAT(token)) {\n logger.error(\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\n );\n return;\n }\n\n try {\n const profile = await authenticate(token);\n\n saveToken(token);\n\n logger.success(\"Logged in successfully.\\n\");\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { clearToken } from \"../../core/config.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function logoutCommand(): Promise {\n clearToken();\n logger.success(\"Logged out. Your token has been removed from this machine.\");\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function whoamiCommand(): Promise {\n const token = getToken();\n\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const profile = await getProfile();\n\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\n\n if (profile.developer.githubUsername) {\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\n }\n\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\n\n if (profile.auth.scopes.length > 0) {\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\n }\n\n const currentProject = getCurrentProject();\n if (currentProject) {\n console.log(`${label(\"Project\")} ${currentProject}`);\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Project, ProjectListItem } from \"../types/project.js\";\n\ninterface APIResponse {\n success: boolean;\n data: T;\n message: string;\n}\n\nexport async function listProjects(): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"GET\",\n });\n return res.data ?? [];\n}\n\nexport async function getProject(projectId: string): Promise {\n const res = await apiFetch>(`/projects/${projectId}`, {\n method: \"GET\",\n });\n return res.data;\n}\n\nexport async function createProject(payload: {\n name: string;\n description?: string;\n}): Promise {\n const res = await apiFetch>(\"/projects\", {\n method: \"POST\",\n body: JSON.stringify(payload),\n });\n return res.data;\n}\n\nexport async function deleteProject(projectId: string): Promise {\n return apiFetch(`/projects/${projectId}`, {\n method: \"DELETE\",\n });\n}","import { listProjects } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectListCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n const currentProjectId = getCurrentProject();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n console.log(`\\nFound ${projects.length} project(s):\\n`);\n\n for (const project of projects) {\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\n const db = formatBytes(project.databaseUsed ?? 0);\n const colCount = project.collections?.length ?? 0;\n\n console.log(` ${project.name}${active}`);\n console.log(` ID ${project._id}`);\n console.log(` Collections ${colCount}`);\n console.log(` DB used ${db}`);\n\n if (project.description) {\n console.log(` Description ${project.description}`);\n }\n\n console.log();\n }\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { listProjects, getProject } from \"../../services/project.service.js\";\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\nimport { prompt } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n try {\n const projects = await listProjects();\n\n if (projects.length === 0) {\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\n return;\n }\n\n let selectedId: string | undefined;\n\n if (projectIdOrName) {\n // Try matching by ID or name\n const match = projects.find(\n (p) =>\n p._id === projectIdOrName ||\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\n );\n if (!match) {\n logger.error(`No project found matching \"${projectIdOrName}\".`);\n logger.info(\"Run 'ub project list' to see available projects.\");\n return;\n }\n selectedId = match._id;\n } else {\n // Interactive selection\n console.log(\"\\nAvailable projects:\\n\");\n projects.forEach((p, i) => {\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\n });\n console.log();\n\n const answer = await prompt(\"Enter project number: \");\n const index = parseInt(answer, 10) - 1;\n\n if (isNaN(index) || index < 0 || index >= projects.length) {\n logger.error(\"Invalid selection.\");\n return;\n }\n\n selectedId = projects[index]._id;\n }\n\n saveCurrentProject(selectedId);\n\n const project = projects.find((p) => p._id === selectedId)!;\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\nimport { formatBytes, label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function projectInfoCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n\n console.log(`\\n${project.name}\\n`);\n console.log(`${label(\"ID\")} ${project._id}`);\n\n if (project.description) {\n console.log(`${label(\"Description\")} ${project.description}`);\n }\n\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\n\n if (project.collections && project.collections.length > 0) {\n console.log(\"\\nCollections:\");\n for (const col of project.collections) {\n const fieldCount = col.model?.length ?? 0;\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\n }\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { label } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport async function collectionListCommand(projectId?: string): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const id = projectId ?? getCurrentProject();\n if (!id) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n const project = await getProject(id);\n const collections = project.collections ?? [];\n\n if (collections.length === 0) {\n logger.info(`No collections in project \"${project.name}\".`);\n return;\n }\n\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\n\n for (const col of collections) {\n const fieldCount = col.model?.length ?? 0;\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\n\n console.log(` ${col.name}`);\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\n\n if (col.model && col.model.length > 0) {\n const fieldSummary = col.model\n .slice(0, 5)\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\n .join(\", \");\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\n }\n\n console.log();\n }\n\n console.log(\" * = required field\");\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Project not found or access denied.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { Collection } from \"../types/project.js\";\n\ninterface CollectionResponse {\n data: Collection;\n message: string;\n}\n\n/**\n * Creates a new collection inside a project.\n * Endpoint: POST /projects/:projectId/collections\n */\nexport async function createCollection(\n projectId: string,\n payload: { name: string; model: Collection[\"model\"] },\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/collections`,\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n return res.data;\n}\n\n/**\n * Deletes a collection from a project.\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\n */\nexport async function deleteCollection(\n projectId: string,\n collectionName: string,\n): Promise {\n return apiFetch(\n `/projects/${projectId}/collections/${collectionName}`,\n { method: \"DELETE\" },\n );\n}\n","import { deleteCollection } from \"../../services/collection.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { confirm } from \"../../utils/prompt.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface CollectionDeleteOptions {\n force?: boolean;\n project?: string;\n}\n\nexport async function collectionDeleteCommand(\n collectionName: string,\n options: CollectionDeleteOptions,\n): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const projectId = options.project ?? getCurrentProject();\n if (!projectId) {\n logger.error(\"No active project. Run 'ub project use' to select one.\");\n return;\n }\n\n try {\n // Verify the collection actually exists before asking for confirmation\n const project = await getProject(projectId);\n const exists = project.collections?.some((c) => c.name === collectionName);\n\n if (!exists) {\n logger.error(\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\n );\n return;\n }\n\n if (!options.force) {\n console.log(\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\n );\n const ok = await confirm(\"Are you sure?\");\n if (!ok) {\n logger.info(\"Aborted.\");\n return;\n }\n }\n\n await deleteCollection(projectId, collectionName);\n logger.success(`Collection \"${collectionName}\" deleted.`);\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else if (error.status === 404) {\n logger.error(\"Collection or project not found.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { apiFetch } from \"../core/api.js\";\nimport type { GlobalStats, RecentActivityLog } from \"../types/analytics.js\";\n\ninterface GlobalStatsResponse {\n data: GlobalStats;\n}\n\ninterface RecentActivityResponse {\n data: RecentActivityLog[];\n}\n\ninterface ProjectAnalyticsResponse {\n data: {\n totalRequests: number;\n successRequests: number;\n errorRequests: number;\n topCollections?: { name: string; requests: number }[];\n };\n}\n\n/**\n * Fetches global usage stats across all developer projects.\n * Endpoint: GET /analytics/stats\n */\nexport async function getGlobalStats(): Promise {\n const res = await apiFetch(\"/analytics/stats\", {\n method: \"GET\",\n });\n return res.data;\n}\n\n/**\n * Fetches the 20 most recent API logs across all projects.\n * Endpoint: GET /analytics/activity\n */\nexport async function getRecentActivity(): Promise {\n const res = await apiFetch(\"/analytics/activity\", {\n method: \"GET\",\n });\n return Array.isArray(res.data) ? res.data : [];\n}\n\n/**\n * Fetches per-project analytics.\n * Endpoint: GET /projects/:projectId/analytics?range=last24h\n */\nexport async function getProjectAnalytics(\n projectId: string,\n range: \"last1h\" | \"last24h\" | \"last7d\" | \"last30d\" | \"allTime\" = \"last24h\",\n): Promise {\n const res = await apiFetch(\n `/projects/${projectId}/analytics?range=${range}`,\n { method: \"GET\" },\n );\n return res.data;\n}\n","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\nimport { formatBytes, label, formatDate } from \"../../utils/format.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\nfunction statusIcon(status: number): string {\n if (status < 300) return \"✓\";\n if (status < 400) return \"→\";\n if (status < 500) return \"⚠\";\n return \"✖\";\n}\n\nexport async function statusCommand(): Promise {\n const token = getToken();\n if (!token) {\n logger.error(\"You are not logged in. Run 'ub login' first.\");\n return;\n }\n\n const currentProjectId = getCurrentProject();\n\n try {\n // Fetch global stats (works without a current project)\n const stats = await getGlobalStats();\n\n console.log(\"\\n── Account ─────────────────────────────────────\");\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\n\n if (stats.planExpiresAt) {\n console.log(`${label(\"Plan expires\")} ${formatDate(stats.planExpiresAt)}`);\n }\n\n console.log(\"\\n── Usage ────────────────────────────────────────\");\n console.log(\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\n );\n console.log(\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\n );\n console.log(\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`,\n );\n console.log(\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`,\n );\n console.log(`${label(\"Total requests\")} ${stats.usage.totalRequests.toLocaleString()}`);\n console.log(`${label(\"Total users\")} ${stats.usage.totalUsers.toLocaleString()}`);\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\n\n // Per-project detail if a project is active\n if (currentProjectId) {\n try {\n const project = await getProject(currentProjectId);\n console.log(\"\\n── Active project ───────────────────────────────\");\n console.log(`${label(\"Name\")} ${project.name}`);\n console.log(`${label(\"ID\")} ${project._id}`);\n console.log(\n `${label(\"Collections\")} ${project.collections?.length ?? 0}`,\n );\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\n } catch {\n // Non-fatal — global stats already displayed\n }\n } else {\n console.log(\n \"\\nTip: Run 'ub project use' to select a project and see per-project details.\",\n );\n }\n\n // Recent activity\n try {\n const activity = await getRecentActivity();\n\n if (activity.length > 0) {\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\n for (const log of activity.slice(0, 10)) {\n const icon = statusIcon(log.status);\n const time = new Date(log.timestamp).toLocaleTimeString();\n console.log(\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`,\n );\n }\n }\n } catch {\n // Non-fatal — skip activity if it fails\n }\n\n console.log();\n } catch (error) {\n if (error instanceof APIError) {\n if (error.status === 401) {\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\n } else {\n logger.error(error.message);\n }\n return;\n }\n logger.error(\"Unable to connect to the urBackend API.\");\n }\n}\n","import { getProfile } from \"../../services/auth.service.js\";\nimport { getProject } from \"../../services/project.service.js\";\nimport {\n getToken,\n getCurrentProject,\n loadConfig,\n configFilePath,\n} from \"../../core/config.js\";\nimport { APIError } from \"../../core/errors.js\";\nimport { logger } from \"../../core/logger.js\";\n\ninterface DoctorResult {\n configFound: boolean;\n patValid: boolean;\n projectSelected: boolean;\n projectAccessible: boolean;\n dashboardApiReachable: boolean;\n dashboardApiLatencyMs: number | null;\n email: string | null;\n plan: string | null;\n projectName: string | null;\n}\n\nexport interface DoctorOptions {\n json?: boolean;\n}\n\nasync function checkApiReachable(apiBase: string): Promise {\n const start = Date.now();\n try {\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\n return Date.now() - start;\n } catch {\n return null;\n }\n}\n\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\n const config = loadConfig();\n const token = getToken();\n const currentProjectId = getCurrentProject();\n\n const result: DoctorResult = {\n configFound: false,\n patValid: false,\n projectSelected: !!currentProjectId,\n projectAccessible: false,\n dashboardApiReachable: false,\n dashboardApiLatencyMs: null,\n email: null,\n plan: null,\n projectName: null,\n };\n\n // 1. Config file\n try {\n const fs = await import(\"node:fs\");\n result.configFound = fs.existsSync(configFilePath());\n } catch {\n result.configFound = false;\n }\n\n // 2. API reachability (independent of auth)\n const latency = await checkApiReachable(config.apiBase);\n result.dashboardApiReachable = latency !== null;\n result.dashboardApiLatencyMs = latency;\n\n // 3. PAT validity\n if (token) {\n try {\n const profile = await getProfile();\n result.patValid = true;\n result.email = profile.developer.email;\n result.plan = profile.developer.plan;\n } catch {\n result.patValid = false;\n }\n }\n\n // 4. Project accessibility\n if (currentProjectId && result.patValid) {\n try {\n const project = await getProject(currentProjectId);\n result.projectAccessible = true;\n result.projectName = project.name;\n } catch {\n result.projectAccessible = false;\n }\n }\n\n // Output\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\n\n const ok = \"✓\";\n const fail = \"✖\";\n const warn = \"⚠\";\n\n console.log(\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\n );\n\n console.log(\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\n result.dashboardApiReachable\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\n : `Unreachable — ${config.apiBase}`\n }`,\n );\n\n if (!token) {\n console.log(` ${fail} PAT Not set — run 'ub login'`);\n } else {\n console.log(\n ` ${result.patValid ? ok : fail} PAT ${\n result.patValid\n ? `Valid (${result.email}, ${result.plan})`\n : \"Invalid or expired — run 'ub login'\"\n }`,\n );\n }\n\n if (!currentProjectId) {\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\n } else {\n console.log(\n ` ${result.projectAccessible ? ok : fail} Active project ${\n result.projectAccessible\n ? `${result.projectName} (${currentProjectId})`\n : `ID ${currentProjectId} — not found or access denied`\n }`,\n );\n }\n\n console.log();\n\n // Summary\n const allGood =\n result.configFound &&\n result.patValid &&\n result.dashboardApiReachable &&\n result.projectSelected &&\n result.projectAccessible;\n\n if (allGood) {\n logger.success(\"Everything looks good.\");\n } else {\n logger.warn(\"Some checks failed. Review the output above.\");\n }\n\n console.log();\n}\n"],"mappings":"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","names":["path","fs"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, pat: token });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const url = `${config.apiBase}/api${endpoint}`;\r\n\r\n let response: Response;\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n });\r\n } catch {\r\n throw new APIError(\r\n 0,\r\n \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await prompt(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n const index = parseInt(answer, 10) - 1;\r\n\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${collectionName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n return Array.isArray(res.data) ? res.data : [];\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":";;;AAAA,SAAS,eAAe;;;ACAxB,OAAO,QAAQ;AACf,OAAOA,WAAU;;;ACDjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAIV,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,KAAK;AAEhD,IAAM,cAAc,KAAK,KAAK,YAAY,aAAa;AAMvD,IAAM,mBACX,QAAQ,IAAI,qBAAqB;;;ADTnC,IAAM,iBAA4B;AAAA,EAChC,SAAS;AACX;AAEO,SAAS,aAAwB;AACtC,MAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B,WAAO,EAAE,GAAG,eAAe;AAAA,EAC7B;AAEA,MAAI;AACF,UAAM,MAAM,GAAG,aAAa,aAAa,MAAM;AAC/C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG,KAAK,MAAM,GAAG;AAAA,IACnB;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,GAAG,eAAe;AAAA,EAC7B;AACF;AAEO,SAAS,WAAW,QAAyB;AAClD,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,OAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AAGA,QAAM,MAAM,cAAc;AAC1B,KAAG,cAAc,KAAK,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,MAAM;AAC7D,KAAG,WAAW,KAAK,WAAW;AAChC;AAEO,SAAS,UAAU,OAAqB;AAC7C,QAAM,SAAS,WAAW;AAC1B,aAAW,EAAE,GAAG,QAAQ,KAAK,MAAM,CAAC;AACtC;AAEO,SAAS,aAAmB;AACjC,QAAM,SAAS,WAAW;AAC1B,SAAO,OAAO;AACd,SAAO,OAAO;AACd,aAAW,MAAM;AACnB;AAEO,SAAS,WAA+B;AAC7C,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,mBAAmB,WAAyB;AAC1D,QAAM,SAAS,WAAW;AAC1B,aAAW,EAAE,GAAG,QAAQ,gBAAgB,UAAU,CAAC;AACrD;AAEO,SAAS,oBAAwC;AACtD,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,iBAAyB;AACvC,SAAO;AACT;;;AE/DO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YACkB,QAChB,SACA;AACA,UAAM,OAAO;AAHG;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EALkB;AAMpB;;;ACOA,eAAsB,SACpB,UACA,UAAwB,CAAC,GACb;AACZ,QAAM,SAAS,WAAW;AAE1B,QAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAE3C,MAAI,QAAQ,QAAQ,CAAC,QAAQ,IAAI,cAAc,GAAG;AAChD,YAAQ,IAAI,gBAAgB,kBAAkB;AAAA,EAChD;AAEA,QAAM,QAAQ,QAAQ,SAAS,OAAO;AACtC,MAAI,OAAO;AACT,YAAQ,IAAI,iBAAiB,UAAU,KAAK,EAAE;AAAA,EAChD;AAGA,QAAM,MAAM,GAAG,OAAO,OAAO,OAAO,QAAQ;AAE5C,MAAI;AAEJ,MAAI;AACF,eAAW,MAAM,MAAM,KAAK;AAAA,MAC1B,GAAG;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,UAAU,SAAS;AAEvB,QAAI;AACF,YAAM,OAAQ,MAAM,SAAS,KAAK;AAIlC,gBAAU,KAAK,WAAW,KAAK,SAAS;AAAA,IAC1C,QAAQ;AAAA,IAER;AAEA,UAAM,IAAI,SAAS,SAAS,QAAQ,OAAO;AAAA,EAC7C;AAEA,MAAI,SAAS,WAAW,KAAK;AAC3B,WAAO;AAAA,EACT;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;;;AC7DA,eAAsB,aAAa,OAAoC;AACrE,QAAM,MAAM,MAAM,SAAkC,gBAAgB;AAAA,IAClE,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO,IAAI;AACb;AAEA,eAAsB,aAAkC;AACtD,QAAM,MAAM,MAAM,SAAkC,gBAAgB;AAAA,IAClE,QAAQ;AAAA,EACV,CAAC;AACD,SAAO,IAAI;AACb;;;ACnBO,SAAS,WAAW,OAAwB;AACjD,SAAO,OAAO,UAAU,YAAY,4BAA4B,KAAK,KAAK;AAC5E;;;ACNA,SAAS,uBAAuB;AAChC,SAAS,OAAO,cAAc;AAM9B,eAAsB,OAAO,UAAmC;AAC9D,QAAM,KAAK,gBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AAC3D,QAAM,UAAU,MAAM,GAAG,SAAS,QAAQ,GAAG,KAAK;AAClD,KAAG,MAAM;AACT,SAAO;AACT;AAMA,eAAsB,QAAQ,UAAoC;AAChE,QAAM,SAAS,MAAM,OAAO,GAAG,QAAQ,UAAU;AACjD,SAAO,OAAO,YAAY,MAAM,OAAO,OAAO,YAAY,MAAM;AAClE;;;AClBO,SAAS,YAAY,OAAuB;AACjD,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,MAAM,IAAI;AAC1C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AACrD,QAAM,SAAS,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC;AACnD,SAAO,GAAG,KAAK,IAAI,MAAM,CAAC,CAAC;AAC7B;AAiBO,SAAS,MAAM,MAAc,QAAQ,IAAY;AACtD,SAAO,KAAK,OAAO,KAAK;AAC1B;;;AC5BO,IAAM,SAAS;AAAA,EACpB,QAAQ,SAAuB;AAC7B,YAAQ,IAAI,UAAK,OAAO,EAAE;AAAA,EAC5B;AAAA,EAEA,MAAM,SAAuB;AAC3B,YAAQ,MAAM,UAAK,OAAO,EAAE;AAAA,EAC9B;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,KAAK,UAAK,OAAO,EAAE;AAAA,EAC7B;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,IAAI,OAAO;AAAA,EACrB;AACF;;;ACRA,eAAsB,eAA8B;AAClD,UAAQ,IAAI,gEAAgE;AAC5E,UAAQ,IAAI,oDAA0C;AAEtD,QAAM,QAAQ,MAAM,OAAO,oCAAoC;AAE/D,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,WAAO;AAAA,MACL;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,aAAa,KAAK;AAExC,cAAU,KAAK;AAEf,WAAO,QAAQ,2BAA2B;AAC1C,YAAQ,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,QAAQ,UAAU,KAAK,EAAE;AAC1D,YAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,QAAQ,UAAU,IAAI,EAAE;AAExD,QAAI,QAAQ,UAAU,gBAAgB;AACpC,cAAQ,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,QAAQ,UAAU,cAAc,EAAE;AAAA,IACtE;AAEA,QAAI,QAAQ,KAAK,OAAO,SAAS,GAAG;AAClC,cAAQ,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACpE;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,kEAAkE;AAAA,MACjF,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;AC7CA,eAAsB,gBAA+B;AACnD,aAAW;AACX,SAAO,QAAQ,4DAA4D;AAC7E;;;ACAA,eAAsB,gBAA+B;AACnD,QAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,WAAW;AAEjC,YAAQ,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,QAAQ,UAAU,KAAK,EAAE;AAC1D,YAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,QAAQ,UAAU,IAAI,EAAE;AAExD,QAAI,QAAQ,UAAU,gBAAgB;AACpC,cAAQ,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,QAAQ,UAAU,cAAc,EAAE;AAAA,IACtE;AAEA,YAAQ,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,QAAQ,KAAK,SAAS,EAAE;AAE9D,QAAI,QAAQ,KAAK,OAAO,SAAS,GAAG;AAClC,cAAQ,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACpE;AAEA,UAAM,iBAAiB,kBAAkB;AACzC,QAAI,gBAAgB;AAClB,cAAQ,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,cAAc,EAAE;AAAA,IACrD;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;ACpCA,eAAsB,eAA2C;AAC/D,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,EAAE,QAAQ,MAAM;AAAA,EAClB;AAEA,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO;AAC/B,SAAQ,IAAuC,QAAQ,CAAC;AAC1D;AAEA,eAAsB,WAAW,WAAqC;AACpE,QAAM,MAAM,MAAM;AAAA,IAChB,aAAa,SAAS;AAAA,IACtB,EAAE,QAAQ,MAAM;AAAA,EAClB;AACA,MAAI,UAAU,OAAO,IAAI,KAAM,QAAQ,IAA6B;AACpE,SAAO;AACT;;;ACpBA,eAAsB,qBAAoC;AACxD,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,aAAa;AACpC,UAAM,mBAAmB,kBAAkB;AAE3C,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,KAAK,iEAAiE;AAC7E;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,QAAW,SAAS,MAAM;AAAA,CAAgB;AAEtD,eAAW,WAAW,UAAU;AAC9B,YAAM,SAAS,QAAQ,QAAQ,mBAAmB,cAAc;AAChE,YAAM,KAAK,YAAY,QAAQ,gBAAgB,CAAC;AAChD,YAAM,WAAW,QAAQ,aAAa,UAAU;AAEhD,cAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG,MAAM,EAAE;AACxC,cAAQ,IAAI,mBAAmB,QAAQ,GAAG,EAAE;AAC5C,cAAQ,IAAI,mBAAmB,QAAQ,EAAE;AACzC,cAAQ,IAAI,mBAAmB,EAAE,EAAE;AAEnC,UAAI,QAAQ,aAAa;AACvB,gBAAQ,IAAI,mBAAmB,QAAQ,WAAW,EAAE;AAAA,MACtD;AAEA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;AC7CA,eAAsB,kBAAkB,iBAAyC;AAC/E,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,aAAa;AAEpC,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,KAAK,iEAAiE;AAC7E;AAAA,IACF;AAEA,QAAI;AAEJ,QAAI,iBAAiB;AAEnB,YAAM,QAAQ,SAAS;AAAA,QACrB,CAAC,MACC,EAAE,QAAQ,mBACV,EAAE,KAAK,YAAY,MAAM,gBAAgB,YAAY;AAAA,MACzD;AACA,UAAI,CAAC,OAAO;AACV,eAAO,MAAM,8BAA8B,eAAe,IAAI;AAC9D,eAAO,KAAK,kDAAkD;AAC9D;AAAA,MACF;AACA,mBAAa,MAAM;AAAA,IACrB,OAAO;AAEL,cAAQ,IAAI,yBAAyB;AACrC,eAAS,QAAQ,CAAC,GAAG,MAAM;AACzB,gBAAQ,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG;AAAA,MACjD,CAAC;AACD,cAAQ,IAAI;AAEZ,YAAM,SAAS,MAAM,OAAO,wBAAwB;AACpD,YAAM,QAAQ,SAAS,QAAQ,EAAE,IAAI;AAErC,UAAI,MAAM,KAAK,KAAK,QAAQ,KAAK,SAAS,SAAS,QAAQ;AACzD,eAAO,MAAM,oBAAoB;AACjC;AAAA,MACF;AAEA,mBAAa,SAAS,KAAK,EAAE;AAAA,IAC/B;AAEA,uBAAmB,UAAU;AAE7B,UAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU;AACzD,WAAO,QAAQ,wBAAwB,QAAQ,IAAI,KAAK,UAAU,GAAG;AAAA,EACvE,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;AChEA,eAAsB,mBAAmB,WAAmC;AAC1E,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,QAAM,KAAK,aAAa,kBAAkB;AAE1C,MAAI,CAAC,IAAI;AACP,WAAO,MAAM,wDAAwD;AACrE;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,WAAW,EAAE;AAEnC,YAAQ,IAAI;AAAA,EAAK,QAAQ,IAAI;AAAA,CAAI;AACjC,YAAQ,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,QAAQ,GAAG,EAAE;AAE3C,QAAI,QAAQ,aAAa;AACvB,cAAQ,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,QAAQ,WAAW,EAAE;AAAA,IAC9D;AAEA,YAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,QAAQ,gBAAgB,YAAY,UAAU,EAAE;AAChF,YAAQ,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,YAAY,QAAQ,gBAAgB,CAAC,CAAC,EAAE;AAC3E,YAAQ,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,YAAY,QAAQ,eAAe,CAAC,CAAC,EAAE;AAC1E,YAAQ,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,QAAQ,aAAa,UAAU,CAAC,EAAE;AAEzE,QAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,GAAG;AACzD,cAAQ,IAAI,gBAAgB;AAC5B,iBAAW,OAAO,QAAQ,aAAa;AACrC,cAAM,aAAa,IAAI,OAAO,UAAU;AACxC,gBAAQ,IAAI,YAAO,IAAI,IAAI,KAAK,UAAU,SAAS,eAAe,IAAI,MAAM,EAAE,GAAG;AAAA,MACnF;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,EACd,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,WAAW,MAAM,WAAW,KAAK;AAC/B,eAAO,MAAM,oBAAoB;AAAA,MACnC,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;ACnDA,eAAsB,sBAAsB,WAAmC;AAC7E,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,QAAM,KAAK,aAAa,kBAAkB;AAC1C,MAAI,CAAC,IAAI;AACP,WAAO,MAAM,wDAAwD;AACrE;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,WAAW,EAAE;AACnC,UAAM,cAAc,QAAQ,eAAe,CAAC;AAE5C,QAAI,YAAY,WAAW,GAAG;AAC5B,aAAO,KAAK,8BAA8B,QAAQ,IAAI,IAAI;AAC1D;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,kBAAqB,QAAQ,IAAI,MAAM,YAAY,MAAM;AAAA,CAAM;AAE3E,eAAW,OAAO,aAAa;AAC7B,YAAM,aAAa,IAAI,OAAO,UAAU;AACxC,YAAM,UAAU,IAAI,KAAK,UAAU,IAAI,IAAI,OAAO;AAElD,cAAQ,IAAI,KAAK,IAAI,IAAI,EAAE;AAC3B,cAAQ,IAAI,OAAO,MAAM,UAAU,EAAE,CAAC,IAAI,UAAU,EAAE;AACtD,cAAQ,IAAI,OAAO,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,EAAE;AAEhD,UAAI,IAAI,SAAS,IAAI,MAAM,SAAS,GAAG;AACrC,cAAM,eAAe,IAAI,MACtB,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,GAAG,EAAE,WAAW,MAAM,EAAE,EAAE,EACxD,KAAK,IAAI;AACZ,cAAM,QAAQ,IAAI,MAAM,SAAS,IAAI,KAAK,IAAI,MAAM,SAAS,CAAC,UAAU;AACxE,gBAAQ,IAAI,OAAO,MAAM,UAAU,EAAE,CAAC,IAAI,YAAY,GAAG,KAAK,EAAE;AAAA,MAClE;AAEA,cAAQ,IAAI;AAAA,IACd;AAEA,YAAQ,IAAI,sBAAsB;AAClC,YAAQ,IAAI;AAAA,EACd,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,WAAW,MAAM,WAAW,KAAK;AAC/B,eAAO,MAAM,qCAAqC;AAAA,MACpD,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;ACnCA,eAAsB,iBACpB,WACA,gBACe;AACf,SAAO;AAAA,IACL,aAAa,SAAS,gBAAgB,cAAc;AAAA,IACpD,EAAE,QAAQ,SAAS;AAAA,EACrB;AACF;;;AC1BA,eAAsB,wBACpB,gBACA,SACe;AACf,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,WAAW,kBAAkB;AACvD,MAAI,CAAC,WAAW;AACd,WAAO,MAAM,wDAAwD;AACrE;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,UAAU,MAAM,WAAW,SAAS;AAC1C,UAAM,SAAS,QAAQ,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc;AAEzE,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL,eAAe,cAAc,2BAA2B,QAAQ,IAAI;AAAA,MACtE;AACA;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ;AAAA,QACN;AAAA,uDAAqD,cAAc;AAAA;AAAA,MACrE;AACA,YAAM,KAAK,MAAM,QAAQ,eAAe;AACxC,UAAI,CAAC,IAAI;AACP,eAAO,KAAK,UAAU;AACtB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,iBAAiB,WAAW,cAAc;AAChD,WAAO,QAAQ,eAAe,cAAc,YAAY;AAAA,EAC1D,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,WAAW,MAAM,WAAW,KAAK;AAC/B,eAAO,MAAM,kCAAkC;AAAA,MACjD,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;AC1BA,eAAsB,iBAAuC;AAC3D,QAAM,MAAM,MAAM,SAAmC,oBAAoB;AAAA,IACvE,QAAQ;AAAA,EACV,CAAC;AACD,SAAO,IAAI;AACb;AAEA,eAAsB,oBAAkD;AACtE,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,EAAE,QAAQ,MAAM;AAAA,EAClB;AACA,SAAO,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC;AAC/C;;;AC9CA,SAAS,WAAW,QAAwB;AAC1C,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,SAAO;AACT;AAEA,eAAsB,gBAA+B;AACnD,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO;AACV,WAAO,MAAM,8CAA8C;AAC3D;AAAA,EACF;AAEA,QAAM,mBAAmB,kBAAkB;AAE3C,MAAI;AACF,UAAM,QAAQ,MAAM,eAAe;AAEnC,YAAQ,IAAI,uPAAoD;AAChE,YAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,KAAK,YAAY,CAAC,EAAE;AAE1D,QAAI,MAAM,eAAe;AACvB,cAAQ,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,MAAM,aAAa,EAAE;AAAA,IAC/D;AAEA,YAAQ,IAAI,uQAAqD;AACjE,YAAQ;AAAA,MACN,GAAG,MAAM,UAAU,CAAC,IAAI,MAAM,MAAM,aAAa,MAAM,MAAM,OAAO,WAAW;AAAA,IACjF;AACA,YAAQ;AAAA,MACN,GAAG,MAAM,aAAa,CAAC,IAAI,MAAM,MAAM,gBAAgB,MAAM,MAAM,OAAO,cAAc;AAAA,IAC1F;AACA,YAAQ;AAAA,MACN,GAAG,MAAM,UAAU,CAAC,IAAI,YAAY,MAAM,MAAM,iBAAiB,CAAC,MAAM,YAAY,MAAM,OAAO,UAAU,CAAC;AAAA,IAC9G;AACA,YAAQ;AAAA,MACN,GAAG,MAAM,SAAS,CAAC,IAAI,YAAY,MAAM,MAAM,gBAAgB,CAAC,MAAM,YAAY,MAAM,OAAO,YAAY,CAAC;AAAA,IAC9G;AACA,YAAQ;AAAA,MACN,GAAG,MAAM,cAAc,CAAC,IAAI,MAAM,MAAM,cAAc,eAAe,CAAC,MAAM,MAAM,OAAO,UAAU,eAAe,CAAC;AAAA,IACrH;AACA,YAAQ,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,MAAM,MAAM,UAAU,MAAM,MAAM,OAAO,cAAc,EAAE;AAC/F,YAAQ,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,MAAM,MAAM,aAAa,EAAE;AAE/D,QAAI,kBAAkB;AACpB,UAAI;AACF,cAAM,UAAU,MAAM,WAAW,gBAAgB;AACjD,gBAAQ,IAAI,0NAAqD;AACjE,gBAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,QAAQ,IAAI,EAAE;AAC9C,gBAAQ,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,QAAQ,GAAG,EAAE;AAC3C,gBAAQ,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,QAAQ,aAAa,UAAU,CAAC,EAAE;AACzE,gBAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,QAAQ,gBAAgB,YAAY,UAAU,EAAE;AAAA,MAClF,QAAQ;AAAA,MAER;AAAA,IACF,OAAO;AACL,cAAQ,IAAI,kDAAkD;AAAA,IAChE;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkB;AACzC,UAAI,SAAS,SAAS,GAAG;AACvB,gBAAQ,IAAI,mKAAqD;AACjE,mBAAW,OAAO,SAAS,MAAM,GAAG,EAAE,GAAG;AACvC,gBAAM,OAAO,WAAW,IAAI,MAAM;AAClC,gBAAM,OAAO,IAAI,KAAK,IAAI,SAAS,EAAE,mBAAmB;AACxD,kBAAQ;AAAA,YACN,KAAK,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,OAAO,OAAO,CAAC,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC,IAAI,IAAI;AAAA,UAClF;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,YAAQ,IAAI;AAAA,EACd,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO,MAAM,iEAAiE;AAAA,MAChF,OAAO;AACL,eAAO,MAAM,MAAM,OAAO;AAAA,MAC5B;AACA;AAAA,IACF;AACA,WAAO,MAAM,yCAAyC;AAAA,EACxD;AACF;;;ACpEA,eAAe,kBAAkB,SAAyC;AACxE,QAAM,QAAQ,KAAK,IAAI;AACvB,MAAI;AACF,UAAM,MAAM,GAAG,OAAO,WAAW,EAAE,QAAQ,YAAY,QAAQ,GAAI,EAAE,CAAC;AACtE,WAAO,KAAK,IAAI,IAAI;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,cAAc,UAAyB,CAAC,GAAkB;AAC9E,QAAM,SAAS,WAAW;AAC1B,QAAM,QAAQ,SAAS;AACvB,QAAM,mBAAmB,kBAAkB;AAE3C,QAAM,SAAuB;AAAA,IAC3B,aAAa;AAAA,IACb,UAAU;AAAA,IACV,iBAAiB,CAAC,CAAC;AAAA,IACnB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,IACvB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAGA,MAAI;AACF,UAAMC,MAAK,MAAM,OAAO,IAAS;AACjC,WAAO,cAAcA,IAAG,WAAW,eAAe,CAAC;AAAA,EACrD,QAAQ;AACN,WAAO,cAAc;AAAA,EACvB;AAGA,QAAM,UAAU,MAAM,kBAAkB,OAAO,OAAO;AACtD,SAAO,wBAAwB,YAAY;AAC3C,SAAO,wBAAwB;AAG/B,MAAI,OAAO;AACT,QAAI;AACF,YAAM,UAAU,MAAM,WAAW;AACjC,aAAO,WAAW;AAClB,aAAO,QAAQ,QAAQ,UAAU;AACjC,aAAO,OAAO,QAAQ,UAAU;AAAA,IAClC,QAAQ;AACN,aAAO,WAAW;AAAA,IACpB;AAAA,EACF;AAGA,MAAI,oBAAoB,OAAO,UAAU;AACvC,QAAI;AACF,YAAM,UAAU,MAAM,WAAW,gBAAgB;AACjD,aAAO,oBAAoB;AAC3B,aAAO,cAAc,QAAQ;AAAA,IAC/B,QAAQ;AACN,aAAO,oBAAoB;AAAA,IAC7B;AAAA,EACF;AAGA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,EACF;AAEA,UAAQ,IAAI,8LAAuD;AAEnE,QAAM,KAAK;AACX,QAAM,OAAO;AACb,QAAM,OAAO;AAEb,UAAQ;AAAA,IACN,KAAK,OAAO,cAAc,KAAK,IAAI,2BAA2B,OAAO,cAAc,eAAe,IAAI,iCAA4B;AAAA,EACpI;AAEA,UAAQ;AAAA,IACN,KAAK,OAAO,wBAAwB,KAAK,IAAI,2BAC3C,OAAO,wBACH,cAAc,OAAO,qBAAqB,QAC1C,sBAAiB,OAAO,OAAO,EACrC;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,IAAI,KAAK,IAAI,uDAAkD;AAAA,EACzE,OAAO;AACL,YAAQ;AAAA,MACN,KAAK,OAAO,WAAW,KAAK,IAAI,2BAC9B,OAAO,WACH,UAAU,OAAO,KAAK,KAAK,OAAO,IAAI,MACtC,0CACN;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,kBAAkB;AACrB,YAAQ,IAAI,KAAK,IAAI,kEAA6D;AAAA,EACpF,OAAO;AACL,YAAQ;AAAA,MACN,KAAK,OAAO,oBAAoB,KAAK,IAAI,2BACvC,OAAO,oBACH,GAAG,OAAO,WAAW,KAAK,gBAAgB,MAC1C,MAAM,gBAAgB,oCAC5B;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI;AAGZ,QAAM,UACJ,OAAO,eACP,OAAO,YACP,OAAO,yBACP,OAAO,mBACP,OAAO;AAET,MAAI,SAAS;AACX,WAAO,QAAQ,wBAAwB;AAAA,EACzC,OAAO;AACL,WAAO,KAAK,8CAA8C;AAAA,EAC5D;AAEA,UAAQ,IAAI;AACd;;;AtB/IA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,kEAA6D,EACzE,QAAQ,OAAO;AAIlB,QACG,QAAQ,OAAO,EACf,YAAY,2CAA2C,EACvD,OAAO,YAAY;AAEtB,QACG,QAAQ,QAAQ,EAChB,YAAY,6CAA6C,EACzD,OAAO,aAAa;AAEvB,QACG,QAAQ,QAAQ,EAChB,YAAY,4CAA4C,EACxD,OAAO,aAAa;AAIvB,IAAM,aAAa,QAChB,QAAQ,SAAS,EACjB,YAAY,2BAA2B;AAE1C,WACG,QAAQ,MAAM,EACd,MAAM,IAAI,EACV,YAAY,8BAA8B,EAC1C,OAAO,kBAAkB;AAE5B,WACG,QAAQ,uBAAuB,EAC/B,YAAY,gDAAgD,EAC5D,OAAO,iBAAiB;AAE3B,WACG,QAAQ,kBAAkB,EAC1B,YAAY,oDAAoD,EAChE,OAAO,kBAAkB;AAI5B,IAAM,gBAAgB,QACnB,QAAQ,YAAY,EACpB,MAAM,KAAK,EACX,YAAY,8CAA8C;AAE7D,cACG,QAAQ,MAAM,EACd,MAAM,IAAI,EACV,YAAY,4CAA4C,EACxD,OAAO,6BAA6B,8BAA8B,EAClE,OAAO,CAAC,YAAY,sBAAsB,QAAQ,OAAO,CAAC;AAE7D,cACG,QAAQ,yBAAyB,EACjC,MAAM,IAAI,EACV,YAAY,sCAAsC,EAClD,OAAO,eAAe,0BAA0B,EAChD,OAAO,6BAA6B,8BAA8B,EAClE,OAAO,CAAC,MAAM,YAAY,wBAAwB,MAAM,OAAO,CAAC;AAInE,QACG,QAAQ,QAAQ,EAChB,YAAY,4CAA4C,EACxD,OAAO,aAAa;AAIvB,QACG,QAAQ,QAAQ,EAChB,YAAY,yCAAyC,EACrD,OAAO,UAAU,kDAAkD,EACnE,OAAO,CAAC,YAAY,cAAc,OAAO,CAAC;AAI7C,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAe;AACrD,UAAQ,MAAM,UAAK,IAAI,OAAO,EAAE;AAChC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["path","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/status/index.ts b/sdks/urbackend-cli/src/commands/status/index.ts index 4f987c722..719dd0c5c 100644 --- a/sdks/urbackend-cli/src/commands/status/index.ts +++ b/sdks/urbackend-cli/src/commands/status/index.ts @@ -1,7 +1,7 @@ import { getGlobalStats, getRecentActivity } from "../../services/analytics.service.js"; import { getProject } from "../../services/project.service.js"; import { getToken, getCurrentProject } from "../../core/config.js"; -import { formatBytes, label, formatDate } from "../../utils/format.js"; +import { formatBytes, label } from "../../utils/format.js"; import { APIError } from "../../core/errors.js"; import { logger } from "../../core/logger.js"; @@ -22,14 +22,13 @@ export async function statusCommand(): Promise { const currentProjectId = getCurrentProject(); try { - // Fetch global stats (works without a current project) const stats = await getGlobalStats(); console.log("\n── Account ─────────────────────────────────────"); console.log(`${label("Plan")} ${stats.plan.toUpperCase()}`); if (stats.planExpiresAt) { - console.log(`${label("Plan expires")} ${formatDate(stats.planExpiresAt)}`); + console.log(`${label("Plan expires")} ${stats.planExpiresAt}`); } console.log("\n── Usage ────────────────────────────────────────"); @@ -40,51 +39,46 @@ export async function statusCommand(): Promise { `${label("Collections")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`, ); console.log( - `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.databaseLimit ?? 0)}`, + `${label("Database")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`, ); console.log( - `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageLimit ?? 0)}`, + `${label("Storage")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`, ); - console.log(`${label("Total requests")} ${stats.usage.totalRequests.toLocaleString()}`); - console.log(`${label("Total users")} ${stats.usage.totalUsers.toLocaleString()}`); + console.log( + `${label("API requests")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`, + ); + console.log(`${label("Auth users")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`); console.log(`${label("Webhooks")} ${stats.usage.totalWebhooks}`); - // Per-project detail if a project is active if (currentProjectId) { try { const project = await getProject(currentProjectId); console.log("\n── Active project ───────────────────────────────"); console.log(`${label("Name")} ${project.name}`); console.log(`${label("ID")} ${project._id}`); - console.log( - `${label("Collections")} ${project.collections?.length ?? 0}`, - ); + console.log(`${label("Collections")} ${project.collections?.length ?? 0}`); console.log(`${label("Auth")} ${project.isAuthEnabled ? "Enabled" : "Disabled"}`); } catch { - // Non-fatal — global stats already displayed + // non-fatal } } else { - console.log( - "\nTip: Run 'ub project use' to select a project and see per-project details.", - ); + console.log("\nTip: Run 'ub project use' to select a project."); } - // Recent activity try { const activity = await getRecentActivity(); - if (activity.length > 0) { console.log("\n── Recent activity (last 10) ────────────────────"); for (const log of activity.slice(0, 10)) { const icon = statusIcon(log.status); const time = new Date(log.timestamp).toLocaleTimeString(); console.log( - ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${log.projectName} ${time}`, + ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`, ); } } } catch { - // Non-fatal — skip activity if it fails + // non-fatal } console.log(); @@ -99,4 +93,4 @@ export async function statusCommand(): Promise { } logger.error("Unable to connect to the urBackend API."); } -} +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/services/analytics.service.ts b/sdks/urbackend-cli/src/services/analytics.service.ts index d3fd7448b..60b8d5115 100644 --- a/sdks/urbackend-cli/src/services/analytics.service.ts +++ b/sdks/urbackend-cli/src/services/analytics.service.ts @@ -1,56 +1,54 @@ import { apiFetch } from "../core/api.js"; -import type { GlobalStats, RecentActivityLog } from "../types/analytics.js"; -interface GlobalStatsResponse { - data: GlobalStats; +interface APIResponse { + success: boolean; + data: T; + message: string; } -interface RecentActivityResponse { - data: RecentActivityLog[]; -} - -interface ProjectAnalyticsResponse { - data: { +export interface GlobalStats { + plan: string; + planExpiresAt: string | null; + limits: { + maxProjects: number; + maxCollections: number; + reqPerDay: number; + storageBytes: number; + mongoBytes: number; + authUsersLimit: number; + }; + usage: { + totalProjects: number; + totalCollections: number; + totalStorageUsed: number; + totalDatabaseUsed: number; totalRequests: number; - successRequests: number; - errorRequests: number; - topCollections?: { name: string; requests: number }[]; + totalWebhooks: number; + totalUsers: number; }; } -/** - * Fetches global usage stats across all developer projects. - * Endpoint: GET /analytics/stats - */ +export interface RecentActivityLog { + id: string; + projectName: string; + projectId: string; + method: string; + path: string; + status: number; + timestamp: string; +} + export async function getGlobalStats(): Promise { - const res = await apiFetch("/analytics/stats", { + const res = await apiFetch>("/analytics/stats", { method: "GET", }); return res.data; } -/** - * Fetches the 20 most recent API logs across all projects. - * Endpoint: GET /analytics/activity - */ export async function getRecentActivity(): Promise { - const res = await apiFetch("/analytics/activity", { - method: "GET", - }); - return Array.isArray(res.data) ? res.data : []; -} - -/** - * Fetches per-project analytics. - * Endpoint: GET /projects/:projectId/analytics?range=last24h - */ -export async function getProjectAnalytics( - projectId: string, - range: "last1h" | "last24h" | "last7d" | "last30d" | "allTime" = "last24h", -): Promise { - const res = await apiFetch( - `/projects/${projectId}/analytics?range=${range}`, + const res = await apiFetch>( + "/analytics/activity", { method: "GET" }, ); - return res.data; -} + return Array.isArray(res.data) ? res.data : []; +} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/services/auth.service.ts b/sdks/urbackend-cli/src/services/auth.service.ts index 778295ccb..925456d57 100644 --- a/sdks/urbackend-cli/src/services/auth.service.ts +++ b/sdks/urbackend-cli/src/services/auth.service.ts @@ -12,7 +12,7 @@ export async function authenticate(token: string): Promise { method: "GET", token, }); - console.log("DEBUG raw response:", JSON.stringify(res, null, 2)); + return res.data; } diff --git a/sdks/urbackend-cli/src/services/project.service.ts b/sdks/urbackend-cli/src/services/project.service.ts index 423e837ca..3b8aa1f6a 100644 --- a/sdks/urbackend-cli/src/services/project.service.ts +++ b/sdks/urbackend-cli/src/services/project.service.ts @@ -8,28 +8,34 @@ interface APIResponse { } export async function listProjects(): Promise { - const res = await apiFetch>("/projects", { - method: "GET", - }); - return res.data ?? []; + const res = await apiFetch>( + "/projects", + { method: "GET" }, + ); + // Backend returns raw array, not wrapped in { data: [...] } + if (Array.isArray(res)) return res; + return (res as APIResponse).data ?? []; } export async function getProject(projectId: string): Promise { - const res = await apiFetch>(`/projects/${projectId}`, { - method: "GET", - }); - return res.data; + const res = await apiFetch>( + `/projects/${projectId}`, + { method: "GET" }, + ); + if ("data" in res && res.data) return (res as APIResponse).data; + return res as Project; } export async function createProject(payload: { name: string; description?: string; }): Promise { - const res = await apiFetch>("/projects", { + const res = await apiFetch>("/projects", { method: "POST", body: JSON.stringify(payload), }); - return res.data; + if ("data" in res && res.data) return (res as APIResponse).data; + return res as Project; } export async function deleteProject(projectId: string): Promise { diff --git a/sdks/urbackend-cli/src/types/analytics.ts b/sdks/urbackend-cli/src/types/analytics.ts index 50ba008e7..a6bceb226 100644 --- a/sdks/urbackend-cli/src/types/analytics.ts +++ b/sdks/urbackend-cli/src/types/analytics.ts @@ -1,11 +1,13 @@ export interface GlobalStats { - plan: "free" | "pro"; + plan: string; planExpiresAt: string | null; limits: { maxProjects: number; maxCollections: number; - storageLimit: number; - databaseLimit: number; + reqPerDay: number; + storageBytes: number; + mongoBytes: number; + authUsersLimit: number; }; usage: { totalProjects: number; @@ -26,11 +28,4 @@ export interface RecentActivityLog { path: string; status: number; timestamp: string; -} - -export interface ProjectAnalytics { - totalRequests: number; - successRequests: number; - errorRequests: number; - avgResponseTime?: number; -} +} \ No newline at end of file From ad8a7d0c4a927e9ef1fe289fb3886921e57f47d1 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 21:17:02 +0530 Subject: [PATCH 07/14] ci: add REDIS_URL env for dashboard-api test job --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2151320b0..fdccca739 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,8 @@ jobs: - name: Run Dashboard API Tests run: npm run test --workspace=dashboard-api + env: + REDIS_URL: redis://localhost:6379 public-api-tests: runs-on: ubuntu-latest From bd145a9e0a435dd9a80dbf235d4b9409b4234183 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 21:23:38 +0530 Subject: [PATCH 08/14] test: mock authenticateCLI and authFlexible in routes.user.test --- .../src/__tests__/routes.user.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/dashboard-api/src/__tests__/routes.user.test.js b/apps/dashboard-api/src/__tests__/routes.user.test.js index c8ccd5d85..e0a43e3ba 100644 --- a/apps/dashboard-api/src/__tests__/routes.user.test.js +++ b/apps/dashboard-api/src/__tests__/routes.user.test.js @@ -48,6 +48,21 @@ jest.mock('../controllers/pat.controller', () => ({ revokePAT: jest.fn((_req, res) => res.json({ success: true })) })); +jest.mock('../middlewares/authenticateCLI', () => + jest.fn((req, _res, next) => { + req.user = { _id: 'mock_user_id', id: 'mock_user_id' }; + req.cliScopes = ['read', 'write']; + next(); + }) +); + +jest.mock('../middlewares/authFlexible', () => + jest.fn((req, _res, next) => { + req.user = { _id: 'mock_user_id', id: 'mock_user_id' }; + next(); + }) +); + const express = require('express'); const request = require('supertest'); const userRouter = require('../routes/user'); From cf0598c29c100c1723a40e583811ae4337104af2 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 21:27:40 +0530 Subject: [PATCH 09/14] test: mock cli.controller to prevent ESM marked import in Jest --- apps/dashboard-api/src/__tests__/routes.user.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/dashboard-api/src/__tests__/routes.user.test.js b/apps/dashboard-api/src/__tests__/routes.user.test.js index e0a43e3ba..f2dc36f4c 100644 --- a/apps/dashboard-api/src/__tests__/routes.user.test.js +++ b/apps/dashboard-api/src/__tests__/routes.user.test.js @@ -63,6 +63,10 @@ jest.mock('../middlewares/authFlexible', () => }) ); +jest.mock('../controllers/cli.controller', () => ({ + getCLIProfile: jest.fn((_req, res) => res.json({ success: true, data: {} })) +})); + const express = require('express'); const request = require('supertest'); const userRouter = require('../routes/user'); From ca1c70b84b6a00e3f27c4777fe2ce7d57fde8b4d Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 22:16:30 +0530 Subject: [PATCH 10/14] proposed fix by coderabbit --- apps/dashboard-api/src/app.js | 4 +++- .../src/controllers/cli.controller.js | 2 +- apps/dashboard-api/src/routes/projects.js | 2 +- sdks/urbackend-cli/src/commands/auth/login.ts | 11 ++++++++++- sdks/urbackend-cli/src/commands/auth/whoami.ts | 3 +++ .../urbackend-cli/src/commands/doctor/index.ts | 7 ++++++- sdks/urbackend-cli/src/commands/project/use.ts | 7 +++++-- sdks/urbackend-cli/src/core/api.ts | 18 +++++++++++++++--- sdks/urbackend-cli/src/core/config.ts | 15 ++++++++++++--- .../src/services/analytics.service.ts | 5 ++++- sdks/urbackend-cli/src/utils/token.ts | 2 +- 11 files changed, 61 insertions(+), 15 deletions(-) diff --git a/apps/dashboard-api/src/app.js b/apps/dashboard-api/src/app.js index 2c0ffcd9c..77c7c95a7 100644 --- a/apps/dashboard-api/src/app.js +++ b/apps/dashboard-api/src/app.js @@ -79,7 +79,9 @@ const csrfProtection = csurf({ app.use((req, res, next) => { // Exclude Razorpay webhook from CSRF protection since it's an external POST request // Exclude CLI routes — CLI authenticates via Bearer PAT, not cookies - if (req.path === '/api/billing/webhook' || req.path.startsWith('/api/user/cli')) { + const isCliRoute = + req.path === '/api/user/cli' || req.path.startsWith('/api/user/cli/'); + if (req.path === '/api/billing/webhook' || isCliRoute) { return next(); } csrfProtection(req, res, next); diff --git a/apps/dashboard-api/src/controllers/cli.controller.js b/apps/dashboard-api/src/controllers/cli.controller.js index f61373790..af1a9b1b1 100644 --- a/apps/dashboard-api/src/controllers/cli.controller.js +++ b/apps/dashboard-api/src/controllers/cli.controller.js @@ -2,7 +2,7 @@ const { Developer, ApiResponse, AppError } = require("@urbackend/common"); module.exports.getCLIProfile = async (req, res, next) => { try { - const developer = await Developer.findById(req.developer._id) + const developer = await Developer.findById(req.user._id) .select("email plan githubUsername avatarUrl"); if (!developer) { diff --git a/apps/dashboard-api/src/routes/projects.js b/apps/dashboard-api/src/routes/projects.js index 917bf5a9a..7dc8988f4 100644 --- a/apps/dashboard-api/src/routes/projects.js +++ b/apps/dashboard-api/src/routes/projects.js @@ -70,7 +70,7 @@ router.post('/:projectId/reveal-secret-key', authMiddleware, authorizeProject('a router.post('/:projectId/collections', authMiddleware, authorizeProject('admin'), planEnforcement.attachDeveloper, planEnforcement.checkCollectionLimit, createCollection); // DELETE REQ FOR COLLECTION -router.delete('/:projectId/collections/:collectionName', authMiddleware, authorizeProject('admin'), verifyEmail, deleteCollection); +router.delete('/:projectId/collections/:collectionName', authFlexible, authorizeProject('admin'), verifyEmail, deleteCollection); // GET REQ FOR DATA router.get('/:projectId/collections/:collectionName/data', authMiddleware, authorizeProject(), getData); diff --git a/sdks/urbackend-cli/src/commands/auth/login.ts b/sdks/urbackend-cli/src/commands/auth/login.ts index 85561d08f..fe750453a 100644 --- a/sdks/urbackend-cli/src/commands/auth/login.ts +++ b/sdks/urbackend-cli/src/commands/auth/login.ts @@ -16,13 +16,20 @@ export async function loginCommand(): Promise { logger.error( "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.", ); + process.exitCode = 1; return; } try { const profile = await authenticate(token); - saveToken(token); + try { + saveToken(token); + } catch { + logger.error("Authenticated, but failed to persist the token locally."); + process.exitCode = 1; + return; + } logger.success("Logged in successfully.\n"); console.log(`${label("Email")} ${profile.developer.email}`); @@ -42,8 +49,10 @@ export async function loginCommand(): Promise { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } diff --git a/sdks/urbackend-cli/src/commands/auth/whoami.ts b/sdks/urbackend-cli/src/commands/auth/whoami.ts index b749d234b..ff025d9a1 100644 --- a/sdks/urbackend-cli/src/commands/auth/whoami.ts +++ b/sdks/urbackend-cli/src/commands/auth/whoami.ts @@ -9,6 +9,7 @@ export async function whoamiCommand(): Promise { if (!token) { logger.error("You are not logged in. Run 'ub login' first."); + process.exitCode = 1; return; } @@ -39,8 +40,10 @@ export async function whoamiCommand(): Promise { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } diff --git a/sdks/urbackend-cli/src/commands/doctor/index.ts b/sdks/urbackend-cli/src/commands/doctor/index.ts index 260b60af4..d244d8f2c 100644 --- a/sdks/urbackend-cli/src/commands/doctor/index.ts +++ b/sdks/urbackend-cli/src/commands/doctor/index.ts @@ -28,7 +28,12 @@ export interface DoctorOptions { async function checkApiReachable(apiBase: string): Promise { const start = Date.now(); try { - await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) }); + const response = await fetch(`${apiBase}/health`, { + signal: AbortSignal.timeout(5000), + }); + if (!response.ok) { + return null; + } return Date.now() - start; } catch { return null; diff --git a/sdks/urbackend-cli/src/commands/project/use.ts b/sdks/urbackend-cli/src/commands/project/use.ts index e8f48fe7b..58b42c4fe 100644 --- a/sdks/urbackend-cli/src/commands/project/use.ts +++ b/sdks/urbackend-cli/src/commands/project/use.ts @@ -43,8 +43,11 @@ export async function projectUseCommand(projectIdOrName?: string): Promise console.log(); const answer = await prompt("Enter project number: "); - const index = parseInt(answer, 10) - 1; - + if (!/^\d+$/.test(answer)) { + logger.error("Invalid selection."); + return; + } + const index = Number(answer) - 1; if (isNaN(index) || index < 0 || index >= projects.length) { logger.error("Invalid selection."); return; diff --git a/sdks/urbackend-cli/src/core/api.ts b/sdks/urbackend-cli/src/core/api.ts index 9f611ee1c..e42ff83af 100644 --- a/sdks/urbackend-cli/src/core/api.ts +++ b/sdks/urbackend-cli/src/core/api.ts @@ -31,20 +31,32 @@ export async function apiFetch( } // All dashboard-api routes are under /api - const url = `${config.apiBase}/api${endpoint}`; + const base = config.apiBase.replace(/\/+$/, ""); + const apiPath = endpoint.startsWith("/") ? endpoint : `/${endpoint}`; + const url = `${base}/api${apiPath}`; let response: Response; + const controller = new AbortController(); + const signal = options.signal ?? controller.signal; + const timeoutId = + options.signal ? undefined : setTimeout(() => controller.abort(), 15_000); + try { response = await fetch(url, { ...options, headers, + signal, }); - } catch { + } catch (error) { throw new APIError( 0, - "Unable to connect to the urBackend API. Is the server running?", + error instanceof Error && error.name === "AbortError" + ? "The urBackend API request timed out." + : "Unable to connect to the urBackend API. Is the server running?", ); + } finally { + if (timeoutId) clearTimeout(timeoutId); } if (!response.ok) { diff --git a/sdks/urbackend-cli/src/core/config.ts b/sdks/urbackend-cli/src/core/config.ts index 8755e55f4..4ab5aaaa5 100644 --- a/sdks/urbackend-cli/src/core/config.ts +++ b/sdks/urbackend-cli/src/core/config.ts @@ -25,18 +25,27 @@ export function loadConfig(): CLIConfig { export function saveConfig(config: CLIConfig): void { if (!fs.existsSync(CONFIG_DIR)) { - fs.mkdirSync(CONFIG_DIR, { recursive: true }); + fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); } // Atomic write: write to temp file then rename to avoid corruption on crash const tmp = CONFIG_PATH + ".tmp"; - fs.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + fs.writeFileSync(tmp, JSON.stringify(config, null, 2), { + encoding: "utf8", + mode: 0o600, + }); fs.renameSync(tmp, CONFIG_PATH); + fs.chmodSync(CONFIG_DIR, 0o700); + fs.chmodSync(CONFIG_PATH, 0o600); } export function saveToken(token: string): void { const config = loadConfig(); - saveConfig({ ...config, pat: token }); + const nextConfig: CLIConfig = { ...config, pat: token }; + if (config.pat && config.pat !== token) { + delete nextConfig.currentProject; + } + saveConfig(nextConfig); } export function clearToken(): void { diff --git a/sdks/urbackend-cli/src/services/analytics.service.ts b/sdks/urbackend-cli/src/services/analytics.service.ts index 60b8d5115..339b27331 100644 --- a/sdks/urbackend-cli/src/services/analytics.service.ts +++ b/sdks/urbackend-cli/src/services/analytics.service.ts @@ -50,5 +50,8 @@ export async function getRecentActivity(): Promise { "/analytics/activity", { method: "GET" }, ); - return Array.isArray(res.data) ? res.data : []; + if (!Array.isArray(res.data)) { + throw new Error("Invalid response from /analytics/activity"); + } + return res.data; } \ No newline at end of file diff --git a/sdks/urbackend-cli/src/utils/token.ts b/sdks/urbackend-cli/src/utils/token.ts index 6bec8199b..7b1382587 100644 --- a/sdks/urbackend-cli/src/utils/token.ts +++ b/sdks/urbackend-cli/src/utils/token.ts @@ -3,5 +3,5 @@ * urBackend PATs always start with "ubpat_" followed by at least 10 characters. */ export function isValidPAT(token: string): boolean { - return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); + return typeof token === "string" && /^ubpat_\S{10,}$/.test(token); } From 9f20781d5ca2f5981f914de6d4a276e759705c08 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 22:28:16 +0530 Subject: [PATCH 11/14] fixed ci.yml --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdccca739..74b8c8ae1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,16 @@ jobs: dashboard-api-tests: runs-on: ubuntu-latest + services: + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 permissions: contents: read steps: From f36ecc6c5df7f4928203733d077b9d9c6d5bc82d Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Sun, 28 Jun 2026 23:03:24 +0530 Subject: [PATCH 12/14] coderabbit agentic fix --- sdks/urbackend-cli/dist/index.cjs | 90 ++++++++++++++++--- sdks/urbackend-cli/dist/index.cjs.map | 2 +- sdks/urbackend-cli/dist/index.js | 90 ++++++++++++++++--- sdks/urbackend-cli/dist/index.js.map | 2 +- sdks/urbackend-cli/src/commands/auth/login.ts | 4 +- .../src/commands/doctor/index.ts | 32 +++++-- .../src/services/collection.service.ts | 3 +- sdks/urbackend-cli/src/utils/prompt.ts | 30 +++++++ 8 files changed, 212 insertions(+), 41 deletions(-) diff --git a/sdks/urbackend-cli/dist/index.cjs b/sdks/urbackend-cli/dist/index.cjs index 45dcb86d7..8aeef2c1f 100644 --- a/sdks/urbackend-cli/dist/index.cjs +++ b/sdks/urbackend-cli/dist/index.cjs @@ -57,15 +57,24 @@ function loadConfig() { } function saveConfig(config) { if (!import_node_fs.default.existsSync(CONFIG_DIR)) { - import_node_fs.default.mkdirSync(CONFIG_DIR, { recursive: true }); + import_node_fs.default.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 }); } const tmp = CONFIG_PATH + ".tmp"; - import_node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + import_node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), { + encoding: "utf8", + mode: 384 + }); import_node_fs.default.renameSync(tmp, CONFIG_PATH); + import_node_fs.default.chmodSync(CONFIG_DIR, 448); + import_node_fs.default.chmodSync(CONFIG_PATH, 384); } function saveToken(token) { const config = loadConfig(); - saveConfig({ ...config, pat: token }); + const nextConfig = { ...config, pat: token }; + if (config.pat && config.pat !== token) { + delete nextConfig.currentProject; + } + saveConfig(nextConfig); } function clearToken() { const config = loadConfig(); @@ -108,18 +117,26 @@ async function apiFetch(endpoint, options = {}) { if (token) { headers.set("Authorization", `Bearer ${token}`); } - const url = `${config.apiBase}/api${endpoint}`; + const base = config.apiBase.replace(/\/+$/, ""); + const apiPath = endpoint.startsWith("/") ? endpoint : `/${endpoint}`; + const url = `${base}/api${apiPath}`; let response; + const controller = new AbortController(); + const signal = options.signal ?? controller.signal; + const timeoutId = options.signal ? void 0 : setTimeout(() => controller.abort(), 15e3); try { response = await fetch(url, { ...options, - headers + headers, + signal }); - } catch { + } catch (error) { throw new APIError( 0, - "Unable to connect to the urBackend API. Is the server running?" + error instanceof Error && error.name === "AbortError" ? "The urBackend API request timed out." : "Unable to connect to the urBackend API. Is the server running?" ); + } finally { + if (timeoutId) clearTimeout(timeoutId); } if (!response.ok) { let message = response.statusText; @@ -153,7 +170,7 @@ async function getProfile() { // src/utils/token.ts function isValidPAT(token) { - return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); + return typeof token === "string" && /^ubpat_\S{10,}$/.test(token); } // src/utils/prompt.ts @@ -165,6 +182,26 @@ async function prompt(question) { rl.close(); return answer; } +async function promptSecret(question) { + const rl = (0, import_promises.createInterface)({ + input: import_node_process.stdin, + output: import_node_process.stdout + }); + import_node_process.stdout.write(question); + const oldWrite = rl._writeToOutput; + rl._writeToOutput = function _writeToOutput(stringToWrite) { + if (stringToWrite === "\r" || stringToWrite === "\n" || stringToWrite === "\r\n") { + oldWrite.call(rl, stringToWrite); + } else if (stringToWrite === question) { + oldWrite.call(rl, stringToWrite); + } else { + } + }; + const answer = (await rl.question("")).trim(); + rl._writeToOutput = oldWrite; + rl.close(); + return answer; +} async function confirm(question) { const answer = await prompt(`${question} (y/n): `); return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; @@ -202,16 +239,23 @@ var logger = { async function loginCommand() { console.log("Generate a Personal Access Token from the urBackend dashboard:"); console.log(" Settings \u2192 Access Tokens \u2192 New Token\n"); - const token = await prompt("Paste your Personal Access Token: "); + const token = await promptSecret("Paste your Personal Access Token: "); if (!isValidPAT(token)) { logger.error( "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters." ); + process.exitCode = 1; return; } try { const profile = await authenticate(token); - saveToken(token); + try { + saveToken(token); + } catch { + logger.error("Authenticated, but failed to persist the token locally."); + process.exitCode = 1; + return; + } logger.success("Logged in successfully.\n"); console.log(`${label("Email")} ${profile.developer.email}`); console.log(`${label("Plan")} ${profile.developer.plan}`); @@ -228,9 +272,11 @@ async function loginCommand() { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } @@ -245,6 +291,7 @@ async function whoamiCommand() { const token = getToken(); if (!token) { logger.error("You are not logged in. Run 'ub login' first."); + process.exitCode = 1; return; } try { @@ -269,9 +316,11 @@ async function whoamiCommand() { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } @@ -367,7 +416,11 @@ async function projectUseCommand(projectIdOrName) { }); console.log(); const answer = await prompt("Enter project number: "); - const index = parseInt(answer, 10) - 1; + if (!/^\d+$/.test(answer)) { + logger.error("Invalid selection."); + return; + } + const index = Number(answer) - 1; if (isNaN(index) || index < 0 || index >= projects.length) { logger.error("Invalid selection."); return; @@ -492,8 +545,9 @@ Collections in "${project.name}" (${collections.length}): // src/services/collection.service.ts async function deleteCollection(projectId, collectionName) { + const encodedName = encodeURIComponent(collectionName); return apiFetch( - `/projects/${projectId}/collections/${collectionName}`, + `/projects/${projectId}/collections/${encodedName}`, { method: "DELETE" } ); } @@ -560,7 +614,10 @@ async function getRecentActivity() { "/analytics/activity", { method: "GET" } ); - return Array.isArray(res.data) ? res.data : []; + if (!Array.isArray(res.data)) { + throw new Error("Invalid response from /analytics/activity"); + } + return res.data; } // src/commands/status/index.ts @@ -647,7 +704,12 @@ async function statusCommand() { async function checkApiReachable(apiBase) { const start = Date.now(); try { - await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5e3) }); + const response = await fetch(`${apiBase}/health`, { + signal: AbortSignal.timeout(5e3) + }); + if (!response.ok) { + return null; + } return Date.now() - start; } catch { return null; diff --git a/sdks/urbackend-cli/dist/index.cjs.map b/sdks/urbackend-cli/dist/index.cjs.map index 713c27f64..c0edc963d 100644 --- a/sdks/urbackend-cli/dist/index.cjs.map +++ b/sdks/urbackend-cli/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, pat: token });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const url = `${config.apiBase}/api${endpoint}`;\r\n\r\n let response: Response;\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n });\r\n } catch {\r\n throw new APIError(\r\n 0,\r\n \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await prompt(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n const index = parseInt(answer, 10) - 1;\r\n\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${collectionName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n return Array.isArray(res.data) ? res.data : [];\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":"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","names":["import_node_path","path","os","fs","fs"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), {\r\n encoding: \"utf8\",\r\n mode: 0o600,\r\n });\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n fs.chmodSync(CONFIG_DIR, 0o700);\r\n fs.chmodSync(CONFIG_PATH, 0o600);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n const nextConfig: CLIConfig = { ...config, pat: token };\r\n if (config.pat && config.pat !== token) {\r\n delete nextConfig.currentProject;\r\n }\r\n saveConfig(nextConfig);\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const base = config.apiBase.replace(/\\/+$/, \"\");\r\n const apiPath = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\r\n const url = `${base}/api${apiPath}`;\r\n\r\n let response: Response;\r\n\r\n const controller = new AbortController();\r\n const signal = options.signal ?? controller.signal;\r\n const timeoutId =\r\n options.signal ? undefined : setTimeout(() => controller.abort(), 15_000);\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n signal,\r\n });\r\n } catch (error) {\r\n throw new APIError(\r\n 0,\r\n error instanceof Error && error.name === \"AbortError\"\r\n ? \"The urBackend API request timed out.\"\r\n : \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n } finally {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_\\S{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts the user for secret input (like a password/token) without echoing characters.\r\n */\r\nexport async function promptSecret(question: string): Promise {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n // Write the question first\r\n stdout.write(question);\r\n\r\n // Standard Node.js readline secret pattern using internal _writeToOutput override:\r\n const oldWrite = (rl as any)._writeToOutput;\r\n (rl as any)._writeToOutput = function _writeToOutput(stringToWrite: string) {\r\n if (stringToWrite === \"\\r\" || stringToWrite === \"\\n\" || stringToWrite === \"\\r\\n\") {\r\n oldWrite.call(rl, stringToWrite);\r\n } else if (stringToWrite === question) {\r\n oldWrite.call(rl, stringToWrite);\r\n } else {\r\n // Echo nothing or a mask (nothing is standard for non-echoing Unix style secret prompts)\r\n }\r\n };\r\n\r\n const answer = (await rl.question(\"\")).trim();\r\n (rl as any)._writeToOutput = oldWrite;\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { promptSecret } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await promptSecret(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n try {\r\n saveToken(token);\r\n } catch {\r\n logger.error(\"Authenticated, but failed to persist the token locally.\");\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n process.exitCode = 1;\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n process.exitCode = 1;\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n if (!/^\\d+$/.test(answer)) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n const index = Number(answer) - 1;\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n const encodedName = encodeURIComponent(collectionName);\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${encodedName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n if (!Array.isArray(res.data)) {\r\n throw new Error(\"Invalid response from /analytics/activity\");\r\n }\r\n return res.data;\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n const response = await fetch(`${apiBase}/health`, {\r\n signal: AbortSignal.timeout(5000),\r\n });\r\n if (!response.ok) {\r\n return null;\r\n }\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":"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","names":["import_node_path","path","os","fs","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/dist/index.js b/sdks/urbackend-cli/dist/index.js index 8256b67c1..08dc5be52 100644 --- a/sdks/urbackend-cli/dist/index.js +++ b/sdks/urbackend-cli/dist/index.js @@ -34,15 +34,24 @@ function loadConfig() { } function saveConfig(config) { if (!fs.existsSync(CONFIG_DIR)) { - fs.mkdirSync(CONFIG_DIR, { recursive: true }); + fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 }); } const tmp = CONFIG_PATH + ".tmp"; - fs.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf8"); + fs.writeFileSync(tmp, JSON.stringify(config, null, 2), { + encoding: "utf8", + mode: 384 + }); fs.renameSync(tmp, CONFIG_PATH); + fs.chmodSync(CONFIG_DIR, 448); + fs.chmodSync(CONFIG_PATH, 384); } function saveToken(token) { const config = loadConfig(); - saveConfig({ ...config, pat: token }); + const nextConfig = { ...config, pat: token }; + if (config.pat && config.pat !== token) { + delete nextConfig.currentProject; + } + saveConfig(nextConfig); } function clearToken() { const config = loadConfig(); @@ -85,18 +94,26 @@ async function apiFetch(endpoint, options = {}) { if (token) { headers.set("Authorization", `Bearer ${token}`); } - const url = `${config.apiBase}/api${endpoint}`; + const base = config.apiBase.replace(/\/+$/, ""); + const apiPath = endpoint.startsWith("/") ? endpoint : `/${endpoint}`; + const url = `${base}/api${apiPath}`; let response; + const controller = new AbortController(); + const signal = options.signal ?? controller.signal; + const timeoutId = options.signal ? void 0 : setTimeout(() => controller.abort(), 15e3); try { response = await fetch(url, { ...options, - headers + headers, + signal }); - } catch { + } catch (error) { throw new APIError( 0, - "Unable to connect to the urBackend API. Is the server running?" + error instanceof Error && error.name === "AbortError" ? "The urBackend API request timed out." : "Unable to connect to the urBackend API. Is the server running?" ); + } finally { + if (timeoutId) clearTimeout(timeoutId); } if (!response.ok) { let message = response.statusText; @@ -130,7 +147,7 @@ async function getProfile() { // src/utils/token.ts function isValidPAT(token) { - return typeof token === "string" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token); + return typeof token === "string" && /^ubpat_\S{10,}$/.test(token); } // src/utils/prompt.ts @@ -142,6 +159,26 @@ async function prompt(question) { rl.close(); return answer; } +async function promptSecret(question) { + const rl = createInterface({ + input: stdin, + output: stdout + }); + stdout.write(question); + const oldWrite = rl._writeToOutput; + rl._writeToOutput = function _writeToOutput(stringToWrite) { + if (stringToWrite === "\r" || stringToWrite === "\n" || stringToWrite === "\r\n") { + oldWrite.call(rl, stringToWrite); + } else if (stringToWrite === question) { + oldWrite.call(rl, stringToWrite); + } else { + } + }; + const answer = (await rl.question("")).trim(); + rl._writeToOutput = oldWrite; + rl.close(); + return answer; +} async function confirm(question) { const answer = await prompt(`${question} (y/n): `); return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; @@ -179,16 +216,23 @@ var logger = { async function loginCommand() { console.log("Generate a Personal Access Token from the urBackend dashboard:"); console.log(" Settings \u2192 Access Tokens \u2192 New Token\n"); - const token = await prompt("Paste your Personal Access Token: "); + const token = await promptSecret("Paste your Personal Access Token: "); if (!isValidPAT(token)) { logger.error( "Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters." ); + process.exitCode = 1; return; } try { const profile = await authenticate(token); - saveToken(token); + try { + saveToken(token); + } catch { + logger.error("Authenticated, but failed to persist the token locally."); + process.exitCode = 1; + return; + } logger.success("Logged in successfully.\n"); console.log(`${label("Email")} ${profile.developer.email}`); console.log(`${label("Plan")} ${profile.developer.plan}`); @@ -205,9 +249,11 @@ async function loginCommand() { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } @@ -222,6 +268,7 @@ async function whoamiCommand() { const token = getToken(); if (!token) { logger.error("You are not logged in. Run 'ub login' first."); + process.exitCode = 1; return; } try { @@ -246,9 +293,11 @@ async function whoamiCommand() { } else { logger.error(error.message); } + process.exitCode = 1; return; } logger.error("Unable to connect to the urBackend API."); + process.exitCode = 1; } } @@ -344,7 +393,11 @@ async function projectUseCommand(projectIdOrName) { }); console.log(); const answer = await prompt("Enter project number: "); - const index = parseInt(answer, 10) - 1; + if (!/^\d+$/.test(answer)) { + logger.error("Invalid selection."); + return; + } + const index = Number(answer) - 1; if (isNaN(index) || index < 0 || index >= projects.length) { logger.error("Invalid selection."); return; @@ -469,8 +522,9 @@ Collections in "${project.name}" (${collections.length}): // src/services/collection.service.ts async function deleteCollection(projectId, collectionName) { + const encodedName = encodeURIComponent(collectionName); return apiFetch( - `/projects/${projectId}/collections/${collectionName}`, + `/projects/${projectId}/collections/${encodedName}`, { method: "DELETE" } ); } @@ -537,7 +591,10 @@ async function getRecentActivity() { "/analytics/activity", { method: "GET" } ); - return Array.isArray(res.data) ? res.data : []; + if (!Array.isArray(res.data)) { + throw new Error("Invalid response from /analytics/activity"); + } + return res.data; } // src/commands/status/index.ts @@ -624,7 +681,12 @@ async function statusCommand() { async function checkApiReachable(apiBase) { const start = Date.now(); try { - await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5e3) }); + const response = await fetch(`${apiBase}/health`, { + signal: AbortSignal.timeout(5e3) + }); + if (!response.ok) { + return null; + } return Date.now() - start; } catch { return null; diff --git a/sdks/urbackend-cli/dist/index.js.map b/sdks/urbackend-cli/dist/index.js.map index 66d0258af..33770149e 100644 --- a/sdks/urbackend-cli/dist/index.js.map +++ b/sdks/urbackend-cli/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), \"utf8\");\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, pat: token });\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const url = `${config.apiBase}/api${endpoint}`;\r\n\r\n let response: Response;\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n });\r\n } catch {\r\n throw new APIError(\r\n 0,\r\n \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_[a-zA-Z0-9_]{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await prompt(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n saveToken(token);\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n const index = parseInt(answer, 10) - 1;\r\n\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${collectionName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n return Array.isArray(res.data) ? res.data : [];\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n await fetch(`${apiBase}/health`, { signal: AbortSignal.timeout(5000) });\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":"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","names":["path","fs"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/constants.ts","../src/core/errors.ts","../src/core/api.ts","../src/services/auth.service.ts","../src/utils/token.ts","../src/utils/prompt.ts","../src/utils/format.ts","../src/core/logger.ts","../src/commands/auth/login.ts","../src/commands/auth/logout.ts","../src/commands/auth/whoami.ts","../src/services/project.service.ts","../src/commands/project/list.ts","../src/commands/project/use.ts","../src/commands/project/info.ts","../src/commands/collection/list.ts","../src/services/collection.service.ts","../src/commands/collection/delete.ts","../src/services/analytics.service.ts","../src/commands/status/index.ts","../src/commands/doctor/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loginCommand } from \"./commands/auth/login.js\";\r\nimport { logoutCommand } from \"./commands/auth/logout.js\";\r\nimport { whoamiCommand } from \"./commands/auth/whoami.js\";\r\nimport { projectListCommand } from \"./commands/project/list.js\";\r\nimport { projectUseCommand } from \"./commands/project/use.js\";\r\nimport { projectInfoCommand } from \"./commands/project/info.js\";\r\nimport { collectionListCommand } from \"./commands/collection/list.js\";\r\nimport { collectionDeleteCommand } from \"./commands/collection/delete.js\";\r\nimport { statusCommand } from \"./commands/status/index.js\";\r\nimport { doctorCommand } from \"./commands/doctor/index.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"ub\")\r\n .description(\"Official urBackend CLI — manage projects, schemas, and more\")\r\n .version(\"0.1.0\");\r\n\r\n// ── Authentication ──────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"login\")\r\n .description(\"Authenticate with a Personal Access Token\")\r\n .action(loginCommand);\r\n\r\nprogram\r\n .command(\"logout\")\r\n .description(\"Remove stored credentials from this machine\")\r\n .action(logoutCommand);\r\n\r\nprogram\r\n .command(\"whoami\")\r\n .description(\"Show the currently authenticated developer\")\r\n .action(whoamiCommand);\r\n\r\n// ── Projects ─────────────────────────────────────────────────────────────────\r\n\r\nconst projectCmd = program\r\n .command(\"project\")\r\n .description(\"Manage urBackend projects\");\r\n\r\nprojectCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all accessible projects\")\r\n .action(projectListCommand);\r\n\r\nprojectCmd\r\n .command(\"use [projectIdOrName]\")\r\n .description(\"Set the active project for subsequent commands\")\r\n .action(projectUseCommand);\r\n\r\nprojectCmd\r\n .command(\"info [projectId]\")\r\n .description(\"Show details for the active (or specified) project\")\r\n .action(projectInfoCommand);\r\n\r\n// ── Collections ──────────────────────────────────────────────────────────────\r\n\r\nconst collectionCmd = program\r\n .command(\"collection\")\r\n .alias(\"col\")\r\n .description(\"Manage collections inside the active project\");\r\n\r\ncollectionCmd\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all collections in the active project\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((options) => collectionListCommand(options.project));\r\n\r\ncollectionCmd\r\n .command(\"delete \")\r\n .alias(\"rm\")\r\n .description(\"Delete a collection and all its data\")\r\n .option(\"-f, --force\", \"Skip confirmation prompt\")\r\n .option(\"-p, --project \", \"Target a specific project ID\")\r\n .action((name, options) => collectionDeleteCommand(name, options));\r\n\r\n// ── Status ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"status\")\r\n .description(\"Show account usage and recent API activity\")\r\n .action(statusCommand);\r\n\r\n// ── Doctor ────────────────────────────────────────────────────────────────────\r\n\r\nprogram\r\n .command(\"doctor\")\r\n .description(\"Run diagnostic checks on your CLI setup\")\r\n .option(\"--json\", \"Output results as JSON (useful for CI/AI agents)\")\r\n .action((options) => doctorCommand(options));\r\n\r\n// ── Parse ─────────────────────────────────────────────────────────────────────\r\n\r\nprogram.parseAsync(process.argv).catch((err: Error) => {\r\n console.error(`✖ ${err.message}`);\r\n process.exit(1);\r\n});\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { CLIConfig } from \"../types/config.js\";\r\nimport { CONFIG_DIR, CONFIG_PATH, DEFAULT_API_BASE } from \"./constants.js\";\r\n\r\nconst DEFAULT_CONFIG: CLIConfig = {\r\n apiBase: DEFAULT_API_BASE,\r\n};\r\n\r\nexport function loadConfig(): CLIConfig {\r\n if (!fs.existsSync(CONFIG_PATH)) {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CONFIG_PATH, \"utf8\");\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...JSON.parse(raw),\r\n };\r\n } catch {\r\n return { ...DEFAULT_CONFIG };\r\n }\r\n}\r\n\r\nexport function saveConfig(config: CLIConfig): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\r\n }\r\n\r\n // Atomic write: write to temp file then rename to avoid corruption on crash\r\n const tmp = CONFIG_PATH + \".tmp\";\r\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2), {\r\n encoding: \"utf8\",\r\n mode: 0o600,\r\n });\r\n fs.renameSync(tmp, CONFIG_PATH);\r\n fs.chmodSync(CONFIG_DIR, 0o700);\r\n fs.chmodSync(CONFIG_PATH, 0o600);\r\n}\r\n\r\nexport function saveToken(token: string): void {\r\n const config = loadConfig();\r\n const nextConfig: CLIConfig = { ...config, pat: token };\r\n if (config.pat && config.pat !== token) {\r\n delete nextConfig.currentProject;\r\n }\r\n saveConfig(nextConfig);\r\n}\r\n\r\nexport function clearToken(): void {\r\n const config = loadConfig();\r\n delete config.pat;\r\n delete config.currentProject;\r\n saveConfig(config);\r\n}\r\n\r\nexport function getToken(): string | undefined {\r\n return loadConfig().pat;\r\n}\r\n\r\nexport function saveCurrentProject(projectId: string): void {\r\n const config = loadConfig();\r\n saveConfig({ ...config, currentProject: projectId });\r\n}\r\n\r\nexport function getCurrentProject(): string | undefined {\r\n return loadConfig().currentProject;\r\n}\r\n\r\nexport function configFilePath(): string {\r\n return CONFIG_PATH;\r\n}\r\n\r\nexport function configDirPath(): string {\r\n return path.dirname(CONFIG_PATH);\r\n}\r\n","import os from \"node:os\";\r\nimport path from \"node:path\";\r\n\r\nexport const CLI_NAME = \"ub\";\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), \".ub\");\r\n\r\nexport const CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\n/**\r\n * Default production API base URL.\r\n * Override via URBACKEND_API_URL env variable for local development.\r\n */\r\nexport const DEFAULT_API_BASE =\r\n process.env.URBACKEND_API_URL ?? \"https://api.urbackend.bitbros.in\";\r\n","export class APIError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"APIError\";\r\n }\r\n}\r\n","import { loadConfig } from \"./config.js\";\r\nimport { APIError } from \"./errors.js\";\r\n\r\nexport interface FetchOptions extends RequestInit {\r\n /** Override the PAT for this single request (e.g. during login verification) */\r\n token?: string;\r\n}\r\n\r\n/**\r\n * Central HTTP client for all CLI → dashboard-api communication.\r\n * Automatically injects the stored PAT as a Bearer token.\r\n *\r\n * All dashboard-api routes are mounted under /api — this client\r\n * prepends that prefix automatically so callers just write /user/cli/me.\r\n */\r\nexport async function apiFetch(\r\n endpoint: string,\r\n options: FetchOptions = {},\r\n): Promise {\r\n const config = loadConfig();\r\n\r\n const headers = new Headers(options.headers);\r\n\r\n if (options.body && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const token = options.token ?? config.pat;\r\n if (token) {\r\n headers.set(\"Authorization\", `Bearer ${token}`);\r\n }\r\n\r\n // All dashboard-api routes are under /api\r\n const base = config.apiBase.replace(/\\/+$/, \"\");\r\n const apiPath = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\r\n const url = `${base}/api${apiPath}`;\r\n\r\n let response: Response;\r\n\r\n const controller = new AbortController();\r\n const signal = options.signal ?? controller.signal;\r\n const timeoutId =\r\n options.signal ? undefined : setTimeout(() => controller.abort(), 15_000);\r\n\r\n try {\r\n response = await fetch(url, {\r\n ...options,\r\n headers,\r\n signal,\r\n });\r\n } catch (error) {\r\n throw new APIError(\r\n 0,\r\n error instanceof Error && error.name === \"AbortError\"\r\n ? \"The urBackend API request timed out.\"\r\n : \"Unable to connect to the urBackend API. Is the server running?\",\r\n );\r\n } finally {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n }\r\n\r\n if (!response.ok) {\r\n let message = response.statusText;\r\n\r\n try {\r\n const body = (await response.json()) as {\r\n message?: string;\r\n error?: string;\r\n };\r\n message = body.message ?? body.error ?? message;\r\n } catch {\r\n // ignore — use statusText\r\n }\r\n\r\n throw new APIError(response.status, message);\r\n }\r\n\r\n if (response.status === 204) {\r\n return undefined as T;\r\n }\r\n\r\n return (await response.json()) as T;\r\n}","import { apiFetch } from \"../core/api.js\";\r\nimport type { CLIProfile } from \"../types/auth.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function authenticate(token: string): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n token,\r\n });\r\n \r\n return res.data;\r\n}\r\n\r\nexport async function getProfile(): Promise {\r\n const res = await apiFetch>(\"/user/cli/me\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}","/**\r\n * Validates the format of a Personal Access Token.\r\n * urBackend PATs always start with \"ubpat_\" followed by at least 10 characters.\r\n */\r\nexport function isValidPAT(token: string): boolean {\r\n return typeof token === \"string\" && /^ubpat_\\S{10,}$/.test(token);\r\n}\r\n","import { createInterface } from \"node:readline/promises\";\r\nimport { stdin, stdout } from \"node:process\";\r\n\r\n/**\r\n * Prompts the user for a single line of input.\r\n * Automatically closes the readline interface after reading.\r\n */\r\nexport async function prompt(question: string): Promise {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n const answer = (await rl.question(question)).trim();\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts the user for secret input (like a password/token) without echoing characters.\r\n */\r\nexport async function promptSecret(question: string): Promise {\r\n const rl = createInterface({\r\n input: stdin,\r\n output: stdout,\r\n });\r\n\r\n // Write the question first\r\n stdout.write(question);\r\n\r\n // Standard Node.js readline secret pattern using internal _writeToOutput override:\r\n const oldWrite = (rl as any)._writeToOutput;\r\n (rl as any)._writeToOutput = function _writeToOutput(stringToWrite: string) {\r\n if (stringToWrite === \"\\r\" || stringToWrite === \"\\n\" || stringToWrite === \"\\r\\n\") {\r\n oldWrite.call(rl, stringToWrite);\r\n } else if (stringToWrite === question) {\r\n oldWrite.call(rl, stringToWrite);\r\n } else {\r\n // Echo nothing or a mask (nothing is standard for non-echoing Unix style secret prompts)\r\n }\r\n };\r\n\r\n const answer = (await rl.question(\"\")).trim();\r\n (rl as any)._writeToOutput = oldWrite;\r\n rl.close();\r\n return answer;\r\n}\r\n\r\n/**\r\n * Prompts for a yes/no confirmation.\r\n * Returns true for \"y\" or \"yes\" (case-insensitive), false otherwise.\r\n */\r\nexport async function confirm(question: string): Promise {\r\n const answer = await prompt(`${question} (y/n): `);\r\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\r\n}\r\n","/**\r\n * Formats bytes into a human-readable string (KB, MB, GB).\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n const value = (bytes / Math.pow(1024, i)).toFixed(2);\r\n return `${value} ${units[i]}`;\r\n}\r\n\r\n/**\r\n * Formats an ISO date string to a locale-friendly short format.\r\n */\r\nexport function formatDate(iso: string | null | undefined): string {\r\n if (!iso) return \"Never\";\r\n return new Date(iso).toLocaleDateString(\"en-US\", {\r\n year: \"numeric\",\r\n month: \"short\",\r\n day: \"numeric\",\r\n });\r\n}\r\n\r\n/**\r\n * Pads a string label to a fixed width for aligned terminal output.\r\n */\r\nexport function label(text: string, width = 14): string {\r\n return text.padEnd(width);\r\n}\r\n","export const logger = {\r\n success(message: string): void {\r\n console.log(`✓ ${message}`);\r\n },\r\n\r\n error(message: string): void {\r\n console.error(`✖ ${message}`);\r\n },\r\n\r\n warn(message: string): void {\r\n console.warn(`⚠ ${message}`);\r\n },\r\n\r\n info(message: string): void {\r\n console.log(message);\r\n },\r\n};\r\n","import { authenticate } from \"../../services/auth.service.js\";\r\nimport { saveToken } from \"../../core/config.js\";\r\nimport { isValidPAT } from \"../../utils/token.js\";\r\nimport { promptSecret } from \"../../utils/prompt.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function loginCommand(): Promise {\r\n console.log(\"Generate a Personal Access Token from the urBackend dashboard:\");\r\n console.log(\" Settings → Access Tokens → New Token\\n\");\r\n\r\n const token = await promptSecret(\"Paste your Personal Access Token: \");\r\n\r\n if (!isValidPAT(token)) {\r\n logger.error(\r\n \"Invalid token format. urBackend PATs start with 'ubpat_' followed by at least 10 characters.\",\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await authenticate(token);\r\n\r\n try {\r\n saveToken(token);\r\n } catch {\r\n logger.error(\"Authenticated, but failed to persist the token locally.\");\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n logger.success(\"Logged in successfully.\\n\");\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Invalid or expired token. Generate a new one from the dashboard.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n process.exitCode = 1;\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import { clearToken } from \"../../core/config.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function logoutCommand(): Promise {\r\n clearToken();\r\n logger.success(\"Logged out. Your token has been removed from this machine.\");\r\n}\r\n","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label, formatDate } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function whoamiCommand(): Promise {\r\n const token = getToken();\r\n\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n try {\r\n const profile = await getProfile();\r\n\r\n console.log(`${label(\"Email\")} ${profile.developer.email}`);\r\n console.log(`${label(\"Plan\")} ${profile.developer.plan}`);\r\n\r\n if (profile.developer.githubUsername) {\r\n console.log(`${label(\"GitHub\")} ${profile.developer.githubUsername}`);\r\n }\r\n\r\n console.log(`${label(\"Token type\")} ${profile.auth.tokenType}`);\r\n\r\n if (profile.auth.scopes.length > 0) {\r\n console.log(`${label(\"Scopes\")} ${profile.auth.scopes.join(\", \")}`);\r\n }\r\n\r\n const currentProject = getCurrentProject();\r\n if (currentProject) {\r\n console.log(`${label(\"Project\")} ${currentProject}`);\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n process.exitCode = 1;\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Project, ProjectListItem } from \"../types/project.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport async function listProjects(): Promise {\r\n const res = await apiFetch>(\r\n \"/projects\",\r\n { method: \"GET\" },\r\n );\r\n // Backend returns raw array, not wrapped in { data: [...] }\r\n if (Array.isArray(res)) return res;\r\n return (res as APIResponse).data ?? [];\r\n}\r\n\r\nexport async function getProject(projectId: string): Promise {\r\n const res = await apiFetch>(\r\n `/projects/${projectId}`,\r\n { method: \"GET\" },\r\n );\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function createProject(payload: {\r\n name: string;\r\n description?: string;\r\n}): Promise {\r\n const res = await apiFetch>(\"/projects\", {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n });\r\n if (\"data\" in res && res.data) return (res as APIResponse).data;\r\n return res as Project;\r\n}\r\n\r\nexport async function deleteProject(projectId: string): Promise {\r\n return apiFetch(`/projects/${projectId}`, {\r\n method: \"DELETE\",\r\n });\r\n}","import { listProjects } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectListCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n const currentProjectId = getCurrentProject();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n console.log(`\\nFound ${projects.length} project(s):\\n`);\r\n\r\n for (const project of projects) {\r\n const active = project._id === currentProjectId ? \" (active)\" : \"\";\r\n const db = formatBytes(project.databaseUsed ?? 0);\r\n const colCount = project.collections?.length ?? 0;\r\n\r\n console.log(` ${project.name}${active}`);\r\n console.log(` ID ${project._id}`);\r\n console.log(` Collections ${colCount}`);\r\n console.log(` DB used ${db}`);\r\n\r\n if (project.description) {\r\n console.log(` Description ${project.description}`);\r\n }\r\n\r\n console.log();\r\n }\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { listProjects, getProject } from \"../../services/project.service.js\";\r\nimport { saveCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { prompt } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectUseCommand(projectIdOrName?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const projects = await listProjects();\r\n\r\n if (projects.length === 0) {\r\n logger.info(\"No projects found. Create one at dashboard.urbackend.bitbros.in\");\r\n return;\r\n }\r\n\r\n let selectedId: string | undefined;\r\n\r\n if (projectIdOrName) {\r\n // Try matching by ID or name\r\n const match = projects.find(\r\n (p) =>\r\n p._id === projectIdOrName ||\r\n p.name.toLowerCase() === projectIdOrName.toLowerCase(),\r\n );\r\n if (!match) {\r\n logger.error(`No project found matching \"${projectIdOrName}\".`);\r\n logger.info(\"Run 'ub project list' to see available projects.\");\r\n return;\r\n }\r\n selectedId = match._id;\r\n } else {\r\n // Interactive selection\r\n console.log(\"\\nAvailable projects:\\n\");\r\n projects.forEach((p, i) => {\r\n console.log(` [${i + 1}] ${p.name} (${p._id})`);\r\n });\r\n console.log();\r\n\r\n const answer = await prompt(\"Enter project number: \");\r\n if (!/^\\d+$/.test(answer)) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n const index = Number(answer) - 1;\r\n if (isNaN(index) || index < 0 || index >= projects.length) {\r\n logger.error(\"Invalid selection.\");\r\n return;\r\n }\r\n\r\n selectedId = projects[index]._id;\r\n }\r\n\r\n saveCurrentProject(selectedId);\r\n\r\n const project = projects.find((p) => p._id === selectedId)!;\r\n logger.success(`Switched to project: ${project.name} (${selectedId})`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getCurrentProject, getToken } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function projectInfoCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n\r\n console.log(`\\n${project.name}\\n`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n\r\n if (project.description) {\r\n console.log(`${label(\"Description\")} ${project.description}`);\r\n }\r\n\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n console.log(`${label(\"DB used\")} ${formatBytes(project.databaseUsed ?? 0)}`);\r\n console.log(`${label(\"Storage\")} ${formatBytes(project.storageUsed ?? 0)}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n\r\n if (project.collections && project.collections.length > 0) {\r\n console.log(\"\\nCollections:\");\r\n for (const col of project.collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n console.log(` • ${col.name} (${fieldCount} field${fieldCount !== 1 ? \"s\" : \"\"})`);\r\n }\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport async function collectionListCommand(projectId?: string): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const id = projectId ?? getCurrentProject();\r\n if (!id) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n const project = await getProject(id);\r\n const collections = project.collections ?? [];\r\n\r\n if (collections.length === 0) {\r\n logger.info(`No collections in project \"${project.name}\".`);\r\n return;\r\n }\r\n\r\n console.log(`\\nCollections in \"${project.name}\" (${collections.length}):\\n`);\r\n\r\n for (const col of collections) {\r\n const fieldCount = col.model?.length ?? 0;\r\n const rlsMode = col.rls?.enabled ? col.rls.mode : \"disabled\";\r\n\r\n console.log(` ${col.name}`);\r\n console.log(` ${label(\"Fields\", 10)} ${fieldCount}`);\r\n console.log(` ${label(\"RLS\", 10)} ${rlsMode}`);\r\n\r\n if (col.model && col.model.length > 0) {\r\n const fieldSummary = col.model\r\n .slice(0, 5)\r\n .map((f) => `${f.key}: ${f.type}${f.required ? \"*\" : \"\"}`)\r\n .join(\", \");\r\n const extra = col.model.length > 5 ? ` +${col.model.length - 5} more` : \"\";\r\n console.log(` ${label(\"Schema\", 10)} ${fieldSummary}${extra}`);\r\n }\r\n\r\n console.log();\r\n }\r\n\r\n console.log(\" * = required field\");\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Project not found or access denied.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\nimport type { Collection } from \"../types/project.js\";\r\n\r\ninterface CollectionResponse {\r\n data: Collection;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Creates a new collection inside a project.\r\n * Endpoint: POST /projects/:projectId/collections\r\n */\r\nexport async function createCollection(\r\n projectId: string,\r\n payload: { name: string; model: Collection[\"model\"] },\r\n): Promise {\r\n const res = await apiFetch(\r\n `/projects/${projectId}/collections`,\r\n {\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n },\r\n );\r\n return res.data;\r\n}\r\n\r\n/**\r\n * Deletes a collection from a project.\r\n * Endpoint: DELETE /projects/:projectId/collections/:collectionName\r\n */\r\nexport async function deleteCollection(\r\n projectId: string,\r\n collectionName: string,\r\n): Promise {\r\n const encodedName = encodeURIComponent(collectionName);\r\n return apiFetch(\r\n `/projects/${projectId}/collections/${encodedName}`,\r\n { method: \"DELETE\" },\r\n );\r\n}\r\n","import { deleteCollection } from \"../../services/collection.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { confirm } from \"../../utils/prompt.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nexport interface CollectionDeleteOptions {\r\n force?: boolean;\r\n project?: string;\r\n}\r\n\r\nexport async function collectionDeleteCommand(\r\n collectionName: string,\r\n options: CollectionDeleteOptions,\r\n): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const projectId = options.project ?? getCurrentProject();\r\n if (!projectId) {\r\n logger.error(\"No active project. Run 'ub project use' to select one.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Verify the collection actually exists before asking for confirmation\r\n const project = await getProject(projectId);\r\n const exists = project.collections?.some((c) => c.name === collectionName);\r\n\r\n if (!exists) {\r\n logger.error(\r\n `Collection \"${collectionName}\" not found in project \"${project.name}\".`,\r\n );\r\n return;\r\n }\r\n\r\n if (!options.force) {\r\n console.log(\r\n `\\n⚠ This will permanently delete the collection \"${collectionName}\" and ALL its data.\\n`,\r\n );\r\n const ok = await confirm(\"Are you sure?\");\r\n if (!ok) {\r\n logger.info(\"Aborted.\");\r\n return;\r\n }\r\n }\r\n\r\n await deleteCollection(projectId, collectionName);\r\n logger.success(`Collection \"${collectionName}\" deleted.`);\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else if (error.status === 404) {\r\n logger.error(\"Collection or project not found.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}\r\n","import { apiFetch } from \"../core/api.js\";\r\n\r\ninterface APIResponse {\r\n success: boolean;\r\n data: T;\r\n message: string;\r\n}\r\n\r\nexport interface GlobalStats {\r\n plan: string;\r\n planExpiresAt: string | null;\r\n limits: {\r\n maxProjects: number;\r\n maxCollections: number;\r\n reqPerDay: number;\r\n storageBytes: number;\r\n mongoBytes: number;\r\n authUsersLimit: number;\r\n };\r\n usage: {\r\n totalProjects: number;\r\n totalCollections: number;\r\n totalStorageUsed: number;\r\n totalDatabaseUsed: number;\r\n totalRequests: number;\r\n totalWebhooks: number;\r\n totalUsers: number;\r\n };\r\n}\r\n\r\nexport interface RecentActivityLog {\r\n id: string;\r\n projectName: string;\r\n projectId: string;\r\n method: string;\r\n path: string;\r\n status: number;\r\n timestamp: string;\r\n}\r\n\r\nexport async function getGlobalStats(): Promise {\r\n const res = await apiFetch>(\"/analytics/stats\", {\r\n method: \"GET\",\r\n });\r\n return res.data;\r\n}\r\n\r\nexport async function getRecentActivity(): Promise {\r\n const res = await apiFetch>(\r\n \"/analytics/activity\",\r\n { method: \"GET\" },\r\n );\r\n if (!Array.isArray(res.data)) {\r\n throw new Error(\"Invalid response from /analytics/activity\");\r\n }\r\n return res.data;\r\n}","import { getGlobalStats, getRecentActivity } from \"../../services/analytics.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport { getToken, getCurrentProject } from \"../../core/config.js\";\r\nimport { formatBytes, label } from \"../../utils/format.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\nfunction statusIcon(status: number): string {\r\n if (status < 300) return \"✓\";\r\n if (status < 400) return \"→\";\r\n if (status < 500) return \"⚠\";\r\n return \"✖\";\r\n}\r\n\r\nexport async function statusCommand(): Promise {\r\n const token = getToken();\r\n if (!token) {\r\n logger.error(\"You are not logged in. Run 'ub login' first.\");\r\n return;\r\n }\r\n\r\n const currentProjectId = getCurrentProject();\r\n\r\n try {\r\n const stats = await getGlobalStats();\r\n\r\n console.log(\"\\n── Account ─────────────────────────────────────\");\r\n console.log(`${label(\"Plan\")} ${stats.plan.toUpperCase()}`);\r\n\r\n if (stats.planExpiresAt) {\r\n console.log(`${label(\"Plan expires\")} ${stats.planExpiresAt}`);\r\n }\r\n\r\n console.log(\"\\n── Usage ────────────────────────────────────────\");\r\n console.log(\r\n `${label(\"Projects\")} ${stats.usage.totalProjects} / ${stats.limits.maxProjects}`,\r\n );\r\n console.log(\r\n `${label(\"Collections\")} ${stats.usage.totalCollections} / ${stats.limits.maxCollections}`,\r\n );\r\n console.log(\r\n `${label(\"Database\")} ${formatBytes(stats.usage.totalDatabaseUsed)} / ${formatBytes(stats.limits.mongoBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"Storage\")} ${formatBytes(stats.usage.totalStorageUsed)} / ${formatBytes(stats.limits.storageBytes)}`,\r\n );\r\n console.log(\r\n `${label(\"API requests\")} ${stats.usage.totalRequests.toLocaleString()} / ${stats.limits.reqPerDay.toLocaleString()} today`,\r\n );\r\n console.log(`${label(\"Auth users\")} ${stats.usage.totalUsers} / ${stats.limits.authUsersLimit}`);\r\n console.log(`${label(\"Webhooks\")} ${stats.usage.totalWebhooks}`);\r\n\r\n if (currentProjectId) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n console.log(\"\\n── Active project ───────────────────────────────\");\r\n console.log(`${label(\"Name\")} ${project.name}`);\r\n console.log(`${label(\"ID\")} ${project._id}`);\r\n console.log(`${label(\"Collections\")} ${project.collections?.length ?? 0}`);\r\n console.log(`${label(\"Auth\")} ${project.isAuthEnabled ? \"Enabled\" : \"Disabled\"}`);\r\n } catch {\r\n // non-fatal\r\n }\r\n } else {\r\n console.log(\"\\nTip: Run 'ub project use' to select a project.\");\r\n }\r\n\r\n try {\r\n const activity = await getRecentActivity();\r\n if (activity.length > 0) {\r\n console.log(\"\\n── Recent activity (last 10) ────────────────────\");\r\n for (const log of activity.slice(0, 10)) {\r\n const icon = statusIcon(log.status);\r\n const time = new Date(log.timestamp).toLocaleTimeString();\r\n console.log(\r\n ` ${icon} [${log.status}] ${log.method.padEnd(6)} ${log.path.padEnd(32)} ${time}`,\r\n );\r\n }\r\n }\r\n } catch {\r\n // non-fatal\r\n }\r\n\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof APIError) {\r\n if (error.status === 401) {\r\n logger.error(\"Token is invalid or expired. Run 'ub login' to re-authenticate.\");\r\n } else {\r\n logger.error(error.message);\r\n }\r\n return;\r\n }\r\n logger.error(\"Unable to connect to the urBackend API.\");\r\n }\r\n}","import { getProfile } from \"../../services/auth.service.js\";\r\nimport { getProject } from \"../../services/project.service.js\";\r\nimport {\r\n getToken,\r\n getCurrentProject,\r\n loadConfig,\r\n configFilePath,\r\n} from \"../../core/config.js\";\r\nimport { APIError } from \"../../core/errors.js\";\r\nimport { logger } from \"../../core/logger.js\";\r\n\r\ninterface DoctorResult {\r\n configFound: boolean;\r\n patValid: boolean;\r\n projectSelected: boolean;\r\n projectAccessible: boolean;\r\n dashboardApiReachable: boolean;\r\n dashboardApiLatencyMs: number | null;\r\n email: string | null;\r\n plan: string | null;\r\n projectName: string | null;\r\n}\r\n\r\nexport interface DoctorOptions {\r\n json?: boolean;\r\n}\r\n\r\nasync function checkApiReachable(apiBase: string): Promise {\r\n const start = Date.now();\r\n try {\r\n const response = await fetch(`${apiBase}/health`, {\r\n signal: AbortSignal.timeout(5000),\r\n });\r\n if (!response.ok) {\r\n return null;\r\n }\r\n return Date.now() - start;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport async function doctorCommand(options: DoctorOptions = {}): Promise {\r\n const config = loadConfig();\r\n const token = getToken();\r\n const currentProjectId = getCurrentProject();\r\n\r\n const result: DoctorResult = {\r\n configFound: false,\r\n patValid: false,\r\n projectSelected: !!currentProjectId,\r\n projectAccessible: false,\r\n dashboardApiReachable: false,\r\n dashboardApiLatencyMs: null,\r\n email: null,\r\n plan: null,\r\n projectName: null,\r\n };\r\n\r\n // 1. Config file\r\n try {\r\n const fs = await import(\"node:fs\");\r\n result.configFound = fs.existsSync(configFilePath());\r\n } catch {\r\n result.configFound = false;\r\n }\r\n\r\n // 2. API reachability (independent of auth)\r\n const latency = await checkApiReachable(config.apiBase);\r\n result.dashboardApiReachable = latency !== null;\r\n result.dashboardApiLatencyMs = latency;\r\n\r\n // 3. PAT validity\r\n if (token) {\r\n try {\r\n const profile = await getProfile();\r\n result.patValid = true;\r\n result.email = profile.developer.email;\r\n result.plan = profile.developer.plan;\r\n } catch {\r\n result.patValid = false;\r\n }\r\n }\r\n\r\n // 4. Project accessibility\r\n if (currentProjectId && result.patValid) {\r\n try {\r\n const project = await getProject(currentProjectId);\r\n result.projectAccessible = true;\r\n result.projectName = project.name;\r\n } catch {\r\n result.projectAccessible = false;\r\n }\r\n }\r\n\r\n // Output\r\n if (options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n return;\r\n }\r\n\r\n console.log(\"\\n── urBackend CLI Doctor ─────────────────────────\\n\");\r\n\r\n const ok = \"✓\";\r\n const fail = \"✖\";\r\n const warn = \"⚠\";\r\n\r\n console.log(\r\n ` ${result.configFound ? ok : fail} Config file ${result.configFound ? configFilePath() : \"Not found — run 'ub login'\"}`,\r\n );\r\n\r\n console.log(\r\n ` ${result.dashboardApiReachable ? ok : fail} Dashboard API ${\r\n result.dashboardApiReachable\r\n ? `Reachable (${result.dashboardApiLatencyMs}ms)`\r\n : `Unreachable — ${config.apiBase}`\r\n }`,\r\n );\r\n\r\n if (!token) {\r\n console.log(` ${fail} PAT Not set — run 'ub login'`);\r\n } else {\r\n console.log(\r\n ` ${result.patValid ? ok : fail} PAT ${\r\n result.patValid\r\n ? `Valid (${result.email}, ${result.plan})`\r\n : \"Invalid or expired — run 'ub login'\"\r\n }`,\r\n );\r\n }\r\n\r\n if (!currentProjectId) {\r\n console.log(` ${warn} Active project Not selected — run 'ub project use'`);\r\n } else {\r\n console.log(\r\n ` ${result.projectAccessible ? ok : fail} Active project ${\r\n result.projectAccessible\r\n ? `${result.projectName} (${currentProjectId})`\r\n : `ID ${currentProjectId} — not found or access denied`\r\n }`,\r\n );\r\n }\r\n\r\n console.log();\r\n\r\n // Summary\r\n const allGood =\r\n result.configFound &&\r\n result.patValid &&\r\n result.dashboardApiReachable &&\r\n result.projectSelected &&\r\n result.projectAccessible;\r\n\r\n if (allGood) {\r\n logger.success(\"Everything looks good.\");\r\n } else {\r\n logger.warn(\"Some checks failed. Review the output above.\");\r\n }\r\n\r\n console.log();\r\n}\r\n"],"mappings":"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","names":["path","fs"]} \ No newline at end of file diff --git a/sdks/urbackend-cli/src/commands/auth/login.ts b/sdks/urbackend-cli/src/commands/auth/login.ts index fe750453a..0427fd330 100644 --- a/sdks/urbackend-cli/src/commands/auth/login.ts +++ b/sdks/urbackend-cli/src/commands/auth/login.ts @@ -1,7 +1,7 @@ import { authenticate } from "../../services/auth.service.js"; import { saveToken } from "../../core/config.js"; import { isValidPAT } from "../../utils/token.js"; -import { prompt } from "../../utils/prompt.js"; +import { promptSecret } from "../../utils/prompt.js"; import { label } from "../../utils/format.js"; import { APIError } from "../../core/errors.js"; import { logger } from "../../core/logger.js"; @@ -10,7 +10,7 @@ export async function loginCommand(): Promise { console.log("Generate a Personal Access Token from the urBackend dashboard:"); console.log(" Settings → Access Tokens → New Token\n"); - const token = await prompt("Paste your Personal Access Token: "); + const token = await promptSecret("Paste your Personal Access Token: "); if (!isValidPAT(token)) { logger.error( diff --git a/sdks/urbackend-cli/src/commands/doctor/index.ts b/sdks/urbackend-cli/src/commands/doctor/index.ts index d244d8f2c..94b2be7c7 100644 --- a/sdks/urbackend-cli/src/commands/doctor/index.ts +++ b/sdks/urbackend-cli/src/commands/doctor/index.ts @@ -11,9 +11,9 @@ import { logger } from "../../core/logger.js"; interface DoctorResult { configFound: boolean; - patValid: boolean; + patValid: boolean | null; projectSelected: boolean; - projectAccessible: boolean; + projectAccessible: boolean | null; dashboardApiReachable: boolean; dashboardApiLatencyMs: number | null; email: string | null; @@ -77,8 +77,12 @@ export async function doctorCommand(options: DoctorOptions = {}): Promise result.patValid = true; result.email = profile.developer.email; result.plan = profile.developer.plan; - } catch { - result.patValid = false; + } catch (error) { + if (error instanceof APIError && error.status === 401) { + result.patValid = false; + } else { + result.patValid = null; + } } } @@ -88,8 +92,12 @@ export async function doctorCommand(options: DoctorOptions = {}): Promise const project = await getProject(currentProjectId); result.projectAccessible = true; result.projectName = project.name; - } catch { - result.projectAccessible = false; + } catch (error) { + if (error instanceof APIError && (error.status === 401 || error.status === 403)) { + result.projectAccessible = false; + } else { + result.projectAccessible = null; + } } } @@ -119,6 +127,10 @@ export async function doctorCommand(options: DoctorOptions = {}): Promise if (!token) { console.log(` ${fail} PAT Not set — run 'ub login'`); + } else if (result.patValid === null) { + console.log( + ` ${warn} PAT Unable to validate — API unreachable or backend error`, + ); } else { console.log( ` ${result.patValid ? ok : fail} PAT ${ @@ -131,6 +143,10 @@ export async function doctorCommand(options: DoctorOptions = {}): Promise if (!currentProjectId) { console.log(` ${warn} Active project Not selected — run 'ub project use'`); + } else if (result.projectAccessible === null) { + console.log( + ` ${warn} Active project Unable to validate — API unreachable or backend error`, + ); } else { console.log( ` ${result.projectAccessible ? ok : fail} Active project ${ @@ -146,10 +162,10 @@ export async function doctorCommand(options: DoctorOptions = {}): Promise // Summary const allGood = result.configFound && - result.patValid && + result.patValid === true && result.dashboardApiReachable && result.projectSelected && - result.projectAccessible; + result.projectAccessible === true; if (allGood) { logger.success("Everything looks good."); diff --git a/sdks/urbackend-cli/src/services/collection.service.ts b/sdks/urbackend-cli/src/services/collection.service.ts index dfdb19d14..bd11020da 100644 --- a/sdks/urbackend-cli/src/services/collection.service.ts +++ b/sdks/urbackend-cli/src/services/collection.service.ts @@ -32,8 +32,9 @@ export async function deleteCollection( projectId: string, collectionName: string, ): Promise { + const encodedName = encodeURIComponent(collectionName); return apiFetch( - `/projects/${projectId}/collections/${collectionName}`, + `/projects/${projectId}/collections/${encodedName}`, { method: "DELETE" }, ); } diff --git a/sdks/urbackend-cli/src/utils/prompt.ts b/sdks/urbackend-cli/src/utils/prompt.ts index eee3a3e28..342d26c2b 100644 --- a/sdks/urbackend-cli/src/utils/prompt.ts +++ b/sdks/urbackend-cli/src/utils/prompt.ts @@ -12,6 +12,36 @@ export async function prompt(question: string): Promise { return answer; } +/** + * Prompts the user for secret input (like a password/token) without echoing characters. + */ +export async function promptSecret(question: string): Promise { + const rl = createInterface({ + input: stdin, + output: stdout, + }); + + // Write the question first + stdout.write(question); + + // Standard Node.js readline secret pattern using internal _writeToOutput override: + const oldWrite = (rl as any)._writeToOutput; + (rl as any)._writeToOutput = function _writeToOutput(stringToWrite: string) { + if (stringToWrite === "\r" || stringToWrite === "\n" || stringToWrite === "\r\n") { + oldWrite.call(rl, stringToWrite); + } else if (stringToWrite === question) { + oldWrite.call(rl, stringToWrite); + } else { + // Echo nothing or a mask (nothing is standard for non-echoing Unix style secret prompts) + } + }; + + const answer = (await rl.question("")).trim(); + (rl as any)._writeToOutput = oldWrite; + rl.close(); + return answer; +} + /** * Prompts for a yes/no confirmation. * Returns true for "y" or "yes" (case-insensitive), false otherwise. From f9ab23f7c368505ea32c8b39a70a263cd3e4df71 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Mon, 29 Jun 2026 00:09:30 +0530 Subject: [PATCH 13/14] revert: restore NODE_ENV guard in authMiddleware console.error --- apps/dashboard-api/src/middlewares/authMiddleware.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/dashboard-api/src/middlewares/authMiddleware.js b/apps/dashboard-api/src/middlewares/authMiddleware.js index 9c46f33f2..7241914bd 100644 --- a/apps/dashboard-api/src/middlewares/authMiddleware.js +++ b/apps/dashboard-api/src/middlewares/authMiddleware.js @@ -31,9 +31,10 @@ module.exports = function (req, res, next) { // Proceed to the next middleware or route handler next(); - } catch (err) { - console.error("JWT ERROR:", err); - + } catch (err) { + if (process.env.NODE_ENV !== 'test') { + console.error(err); + } return next(new AppError(401, "Invalid Token")); } }; From 13b672c3132a367c7a42951dfa24ccbd90007af8 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Mon, 29 Jun 2026 00:17:44 +0530 Subject: [PATCH 14/14] removed extra landing space in ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74b8c8ae1..3b11fa493 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: dashboard-api-tests: runs-on: ubuntu-latest - services: + services: redis: image: redis:7-alpine ports: