From a53679840bec1b40f7cb8f4da407927c79682cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Ayhan?= Date: Sun, 6 Jul 2025 01:20:05 +0300 Subject: [PATCH 1/8] doc: contributing file update --- CONTRIBUTING.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30730f4..7ee9989 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,6 +89,90 @@ Thank you for your interest in contributing to Firebase Tools CLI! This guide wi - **Detailed description**: Explain the changes and why they're needed - **Link issues**: Reference any related issues in your description. Non-issue PRs will not be merged. +## Pull Request Strategy + +### Squash and Merge (Recommended) + +To keep commit history clean: + +1. **Enable "Squash and merge"** in your GitHub repository settings +2. **Disable "Create a merge commit"** and "Rebase and merge" +3. **Write clear commit message** in the squash commit title + +#### Example: + +``` +PR Title: Add new Firestore export format +Multiple commits in PR: +- WIP: working on export +- fix typo +- add tests +- update docs + +Squash commit message: "Add new export format for Firestore" +``` + +This way, only ONE commit with a clear message reaches main! + +### Branch Protection Rules + +Set up branch protection for `main`: + +- ✅ Require pull request reviews +- ✅ Require status checks to pass +- ✅ Require branches to be up to date +- ✅ Restrict pushes to main branch + +## Release Process + +This project uses a **manual release process** where maintainers have full control over when and what gets released. + +### How Releases Work + +1. **Development**: Work happens in feature branches and gets merged to `main` via PRs +2. **Manual Release**: When ready, maintainers create a GitHub release manually +3. **Automatic Publishing**: GitHub Actions automatically publishes to npm based on the release + +### Creating a Release + +1. **Go to GitHub Releases**: Navigate to the releases page +2. **Create new release**: Click "Create a new release" +3. **Choose tag**: Create a new tag (e.g., `v1.2.3`, `v2.0.0`) +4. **Write release notes**: Describe what changed in this release +5. **Publish release**: Click "Publish release" +6. **Automatic npm publish**: GitHub Actions will automatically: + - Run quality checks and tests + - Update package.json with the release version + - Build the project + - Publish to npm + - Update the release with npm package info + +### Version Naming + +Use semantic versioning for tags: + +- **Patch** (v1.0.1): Bug fixes, small improvements +- **Minor** (v1.1.0): New features, backwards compatible +- **Major** (v2.0.0): Breaking changes + +Example: + +``` +v1.0.1 - Bug fix release +v1.1.0 - New feature release +v2.0.0 - Breaking changes release +``` + +## Development Workflow + +1. **Create feature branch** from main +2. **Make changes** and commit +3. **Open pull request** to main +4. **Code review** and approval +5. **Squash and merge** to main +6. **When ready for release**: Create GitHub release manually +7. **Automatic publishing** to npm happens via GitHub Actions + ## Code Review Process 1. **Maintainers will review** your pull request From d8b4933c7323d19dcf8cc74b07018e195d34d2d2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 13:55:32 +0300 Subject: [PATCH 2/8] fix: 7 security & reliability weakspots identified via project analysis (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * chore: initial analysis - no code changes yet Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix: resolve 7 security/reliability/dependency issues found during analysis Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix: apply reviewer feedback on error handling, shape validation, and file permissions Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: null-safety and variable-shadow in promptServiceAccountFile; fix duplicate block in firestore-import Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * chore: format utils --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> Co-authored-by: Ömer Ayhan Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- npm-shrinkwrap.json | 685 ++++++++++++++++++---- src/actions/auth/login.ts | 49 +- src/actions/firestore/firestore-import.ts | 23 +- src/actions/rtdb/rtdb-export.ts | 2 +- src/actions/rtdb/rtdb-import.ts | 8 +- src/hooks/init.ts | 68 ++- src/utils.ts | 10 +- yarn.lock | 391 ++++++++---- 8 files changed, 968 insertions(+), 268 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1e9f343..0f7e659 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "firebase-tools-cli", - "version": "0.5.1", + "version": "0.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebase-tools-cli", - "version": "0.5.1", + "version": "0.5.3", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -234,6 +234,70 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/darwin-arm64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", @@ -250,6 +314,326 @@ "node": ">=18" } }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@fastify/busboy": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", @@ -342,9 +726,9 @@ } }, "node_modules/@google-cloud/firestore": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.1.tgz", - "integrity": "sha512-ZxOdH8Wr01hBDvKCQfMWqwUcfNcN3JY19k1LtS1fTFhEyorYPLsbWN+VxIRL46pOYGHTPkU3Or5HbT/SLQM5nA==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz", + "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -393,9 +777,9 @@ } }, "node_modules/@google-cloud/storage": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.16.0.tgz", - "integrity": "sha512-7/5LRgykyOfQENcm6hDKP8SX/u9XxE5YOiWOkgkwcoO+cG8xT/cyOvp9wwN3IxfdYgpHs8CE7Nq2PKX2lNaEXw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz", + "integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -405,7 +789,7 @@ "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "duplexify": "^4.1.3", - "fast-xml-parser": "^4.4.1", + "fast-xml-parser": "^5.3.4", "gaxios": "^6.0.2", "google-auth-library": "^9.6.3", "html-entities": "^2.5.2", @@ -475,6 +859,43 @@ "node": ">=6" } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -991,23 +1412,23 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -1023,12 +1444,41 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1114,9 +1564,9 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "license": "MIT" }, "node_modules/cli-cursor": { @@ -1535,39 +1985,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -1601,20 +2051,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/farmhash-modern": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", @@ -1631,10 +2067,23 @@ "license": "MIT", "optional": true }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.2.tgz", + "integrity": "sha512-pw/6pIl4k0CSpElPEJhDppLzaixDEuWui2CUQQBH/ECDf7+y6YwA4Gf7Tyb0Rfe4DIMuZipYj4AEL0nACKglvQ==", "funding": [ { "type": "github", @@ -1644,7 +2093,8 @@ "license": "MIT", "optional": true, "dependencies": { - "strnum": "^1.1.1" + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" @@ -1757,15 +2207,16 @@ } }, "node_modules/form-data": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", - "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "license": "MIT", "optional": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, @@ -2133,16 +2584,16 @@ "license": "ISC" }, "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", "license": "MIT", "dependencies": { + "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", - "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", @@ -2315,12 +2766,12 @@ } }, "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -2353,12 +2804,12 @@ } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -2368,9 +2819,9 @@ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -2593,9 +3044,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -2700,15 +3151,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2815,12 +3257,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -2839,16 +3281,45 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -3190,9 +3661,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", "funding": [ { "type": "github", @@ -3258,18 +3729,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", diff --git a/src/actions/auth/login.ts b/src/actions/auth/login.ts index 011df24..dcad3ec 100644 --- a/src/actions/auth/login.ts +++ b/src/actions/auth/login.ts @@ -9,6 +9,15 @@ import path from 'path'; import { CREDENTIALS_FILE, OAUTH_CONFIG } from '@/constants'; import { loadConfig, saveConfig, saveCredentials } from '@/utils'; +function escapeHtml(value: unknown): string { + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + interface ErrorWithCode extends Error { code?: string; } @@ -56,9 +65,9 @@ async function authenticateWithOAuth(): Promise { if (error) { const errorMsg = error_description || error; res.send(` -

❌ Authentication Error

-

Error: ${error}

-

Description: ${errorMsg}

+

❌ Authentication Error

+

Error: ${escapeHtml(error)}

+

Description: ${escapeHtml(errorMsg)}

Please check the console for troubleshooting steps.

`); server.close(); @@ -133,8 +142,8 @@ async function authenticateWithOAuth(): Promise { console.error('Token exchange error:', errorMessage); res.send(` -

❌ Authentication Failed

-

Error: ${errorMessage}

+

❌ Authentication Failed

+

Error: ${escapeHtml(errorMessage)}

Please check the console for more details.

`); server.close(); @@ -278,26 +287,32 @@ async function promptServiceAccountFile() { name: 'serviceAccountPath', message: 'Enter path to service account JSON file:', filter: (input) => { - const path = input.trim(); + const filePath = input.trim(); - if (!path) { + if (!filePath) { throw new Error('Please enter a valid file path'); } - if (!fs.existsSync(path)) { - throw new Error(`File not found: ${path}`); + if (!fs.existsSync(filePath)) { + throw new Error(`File not found: ${filePath}`); } + let content: unknown; try { - const content = JSON.parse(fs.readFileSync(input.trim(), 'utf8')); - if (!content.type || content.type !== 'service_account') { - throw new Error('Invalid service account file format'); - } - } catch (error) { + content = JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch { throw new Error('Invalid JSON file'); } + if ( + typeof content !== 'object' || + content === null || + Array.isArray(content) || + (content as Record).type !== 'service_account' + ) { + throw new Error('Invalid service account file format'); + } - return path; + return filePath; }, }, ]); @@ -369,7 +384,9 @@ const loginAction = async (options: LoginActionType) => { console.log(chalk.blue('🔑 Service Account Authentication\n')); const serviceAccountPath = await promptServiceAccountFile(); - const serviceAccount = require(path.resolve(serviceAccountPath)); + const serviceAccount = JSON.parse( + fs.readFileSync(path.resolve(serviceAccountPath), 'utf8') + ); console.log(chalk.green('✅ Service account loaded successfully!')); console.log(chalk.gray(` └── Project: ${serviceAccount.project_id}`)); diff --git a/src/actions/firestore/firestore-import.ts b/src/actions/firestore/firestore-import.ts index 55ffb5a..6c2df6f 100644 --- a/src/actions/firestore/firestore-import.ts +++ b/src/actions/firestore/firestore-import.ts @@ -22,7 +22,28 @@ export async function importCollections( } const rawData = fs.readFileSync(file, 'utf8'); - const importData = JSON.parse(rawData); + let parsedData: unknown; + try { + parsedData = JSON.parse(rawData); + } catch { + console.error(chalk.red('❌ Import file contains invalid JSON')); + process.exit(1); + } + + if ( + parsedData === null || + typeof parsedData !== 'object' || + Array.isArray(parsedData) + ) { + console.error( + chalk.red( + '❌ Invalid import data format: file must contain a JSON object, not an array, null, or primitive value' + ) + ); + process.exit(1); + } + + const importData = parsedData as Record; let totalImported = 0; const batchSize = options.batchSize || 500; diff --git a/src/actions/rtdb/rtdb-export.ts b/src/actions/rtdb/rtdb-export.ts index e471fec..509f0f7 100644 --- a/src/actions/rtdb/rtdb-export.ts +++ b/src/actions/rtdb/rtdb-export.ts @@ -46,7 +46,7 @@ export async function exportRealtimeDatabase(options: ExportRTDBOptionsType) { chalk.yellow(`⏭️ Excluding paths: ${options.exclude.join(', ')}`) ); for (const excludePath of options.exclude) { - if (allData[excludePath]) { + if (Object.prototype.hasOwnProperty.call(allData, excludePath)) { delete allData[excludePath]; console.log(chalk.gray(` └── Excluded: ${excludePath}`)); } diff --git a/src/actions/rtdb/rtdb-import.ts b/src/actions/rtdb/rtdb-import.ts index 223cd0a..eb0537b 100644 --- a/src/actions/rtdb/rtdb-import.ts +++ b/src/actions/rtdb/rtdb-import.ts @@ -23,7 +23,13 @@ export async function importRealtimeDatabase( const rtdb = rtdbApp.database(); const rawData = fs.readFileSync(file, 'utf8'); - const importData = JSON.parse(rawData); + let importData: Record; + try { + importData = JSON.parse(rawData); + } catch { + console.error(chalk.red('❌ Import file contains invalid JSON')); + process.exit(1); + } if (!importData || typeof importData !== 'object') { console.error(chalk.red('❌ Invalid import data format')); diff --git a/src/hooks/init.ts b/src/hooks/init.ts index 03bdaaa..d0d65bc 100644 --- a/src/hooks/init.ts +++ b/src/hooks/init.ts @@ -24,14 +24,20 @@ async function promptServiceAccountFile() { throw new Error(`File not found: ${path}`); } + let content: unknown; try { - const content = JSON.parse(fs.readFileSync(path, 'utf8')); - if (!content.type || content.type !== 'service_account') { - throw new Error('Invalid service account file format'); - } - } catch (error) { + content = JSON.parse(fs.readFileSync(path, 'utf8')); + } catch { throw new Error('Invalid JSON file'); } + if ( + typeof content !== 'object' || + content === null || + Array.isArray(content) || + (content as Record).type !== 'service_account' + ) { + throw new Error('Invalid service account file format'); + } return path; }, @@ -45,7 +51,41 @@ async function configureAdminServiceAccount( serviceAccountPath: string, projectId: string ) { - const serviceAccount = require(path.resolve(serviceAccountPath)); + const resolvedPath = path.resolve(serviceAccountPath); + + let fileContents: string; + try { + fileContents = fs.readFileSync(resolvedPath, 'utf8'); + } catch (err: any) { + if (err && err.code === 'ENOENT') { + throw new Error( + `Service account file not found at "${resolvedPath}". ` + + 'Please check the path or re-run the CLI with a valid --service-account file.' + ); + } + throw err; + } + + let serviceAccount: any; + try { + serviceAccount = JSON.parse(fileContents); + } catch { + throw new Error( + `Failed to parse service account JSON file at "${resolvedPath}". ` + + 'Please ensure the file contains valid JSON for a Firebase service account key.' + ); + } + + if ( + typeof serviceAccount !== 'object' || + serviceAccount === null || + !serviceAccount.type || + serviceAccount.type !== 'service_account' + ) { + throw new Error( + 'Invalid service account file format. Expected a Firebase service account key JSON.' + ); + } const credential = admin.credential.cert(serviceAccount); const projectIdValue = projectId || serviceAccount.project_id; @@ -120,7 +160,21 @@ async function initializeFirebase(thisCommand: Command) { const serviceAccountPath = await promptServiceAccountFile(); options.serviceAccount = serviceAccountPath; - const serviceAccount = require(path.resolve(serviceAccountPath)); + const resolvedServiceAccountPath = path.resolve(serviceAccountPath); + const serviceAccountFileContents = fs.readFileSync( + resolvedServiceAccountPath, + 'utf8' + ); + let serviceAccount; + try { + serviceAccount = JSON.parse(serviceAccountFileContents); + } catch (parseError) { + const parseMessage = + parseError instanceof Error ? parseError.message : String(parseError); + throw new Error( + `Invalid service account JSON at "${resolvedServiceAccountPath}": ${parseMessage}` + ); + } const { db, projectId } = await configureAdminServiceAccount( serviceAccountPath, projectIdValue || serviceAccount.project_id || config.defaultProject diff --git a/src/utils.ts b/src/utils.ts index 9b147dd..2a62aee 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -81,7 +81,10 @@ function ensureConfigDir() { // Save configuration function saveConfig(config: ConfigType) { ensureConfigDir(); - fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); + fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { + mode: 0o600, + }); + fs.chmodSync(CONFIG_FILE, 0o600); } // Load configuration @@ -99,7 +102,10 @@ function loadConfig() { function saveCredentials(credentials: Credentials) { ensureConfigDir(); - fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2)); + fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { + mode: 0o600, + }); + fs.chmodSync(CREDENTIALS_FILE, 0o600); } export { diff --git a/yarn.lock b/yarn.lock index 696359b..f9f1994 100644 --- a/yarn.lock +++ b/yarn.lock @@ -118,11 +118,131 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@esbuild/aix-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz" + integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA== + +"@esbuild/android-arm@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz" + integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA== + +"@esbuild/android-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz" + integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg== + +"@esbuild/android-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz" + integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw== + "@esbuild/darwin-arm64@0.25.5": version "0.25.5" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz" integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== +"@esbuild/darwin-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz" + integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ== + +"@esbuild/freebsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz" + integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw== + +"@esbuild/freebsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz" + integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw== + +"@esbuild/linux-arm@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz" + integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw== + +"@esbuild/linux-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz" + integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg== + +"@esbuild/linux-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz" + integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA== + +"@esbuild/linux-loong64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz" + integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg== + +"@esbuild/linux-mips64el@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz" + integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg== + +"@esbuild/linux-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz" + integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ== + +"@esbuild/linux-riscv64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz" + integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA== + +"@esbuild/linux-s390x@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz" + integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ== + +"@esbuild/linux-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz" + integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw== + +"@esbuild/netbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz" + integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw== + +"@esbuild/netbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz" + integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ== + +"@esbuild/openbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz" + integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw== + +"@esbuild/openbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz" + integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg== + +"@esbuild/sunos-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz" + integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA== + +"@esbuild/win32-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz" + integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw== + +"@esbuild/win32-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz" + integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ== + +"@esbuild/win32-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz" + integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== + "@fastify/busboy@^3.0.0": version "3.1.1" resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz" @@ -199,9 +319,9 @@ tslib "^2.1.0" "@google-cloud/firestore@^7.7.0": - version "7.11.1" - resolved "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.1.tgz" - integrity sha512-ZxOdH8Wr01hBDvKCQfMWqwUcfNcN3JY19k1LtS1fTFhEyorYPLsbWN+VxIRL46pOYGHTPkU3Or5HbT/SLQM5nA== + version "7.11.6" + resolved "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz" + integrity sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw== dependencies: "@opentelemetry/api" "^1.3.0" fast-deep-equal "^3.1.1" @@ -228,9 +348,9 @@ integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== "@google-cloud/storage@^7.7.0": - version "7.16.0" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.16.0.tgz" - integrity sha512-7/5LRgykyOfQENcm6hDKP8SX/u9XxE5YOiWOkgkwcoO+cG8xT/cyOvp9wwN3IxfdYgpHs8CE7Nq2PKX2lNaEXw== + version "7.19.0" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz" + integrity sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ== dependencies: "@google-cloud/paginator" "^5.0.0" "@google-cloud/projectify" "^4.0.0" @@ -238,7 +358,7 @@ abort-controller "^3.0.0" async-retry "^1.3.3" duplexify "^4.1.3" - fast-xml-parser "^4.4.1" + fast-xml-parser "^5.3.4" gaxios "^6.0.2" google-auth-library "^9.6.3" html-entities "^2.5.2" @@ -266,6 +386,14 @@ protobufjs "^7.2.5" yargs "^17.7.2" +"@inquirer/external-editor@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz" + integrity sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA== + dependencies: + chardet "^2.1.1" + iconv-lite "^0.7.0" + "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" @@ -454,7 +582,7 @@ resolved "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz" integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== -"@types/node@*", "@types/node@^20.11.0", "@types/node@>=13.7.0": +"@types/node@*", "@types/node@^20.11.0", "@types/node@>=13.7.0", "@types/node@>=18": version "20.19.0" resolved "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz" integrity sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q== @@ -604,23 +732,23 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== +body-parser@~1.20.3: + version "1.20.4" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== dependencies: - bytes "3.1.2" + bytes "~3.1.2" content-type "~1.0.5" debug "2.6.9" depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.14.0" + raw-body "~2.5.3" type-is "~1.6.18" - unpipe "1.0.0" + unpipe "~1.0.0" buffer-equal-constant-time@^1.0.1: version "1.0.1" @@ -635,7 +763,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bytes@3.1.2: +bytes@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -664,10 +792,10 @@ chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chardet@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz" + integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== cli-cursor@^3.1.0: version "3.1.0" @@ -724,7 +852,7 @@ commander@^11.0.0: resolved "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== -content-disposition@0.5.4: +content-disposition@~0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -736,12 +864,12 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -cookie-signature@1.0.6: +cookie-signature@~1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.7.1: +cookie@~0.7.1: version "0.7.1" resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== @@ -777,12 +905,12 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@2.0.0: +depd@~2.0.0, depd@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@1.2.0: +destroy@~1.2.0, destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -924,38 +1052,38 @@ event-target-shim@^5.0.0: integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== express@^4.18.2: - version "4.21.2" - resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + version "4.22.1" + resolved "https://registry.npmjs.org/express/-/express-4.22.1.tgz" + integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" + body-parser "~1.20.3" + content-disposition "~0.5.4" content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" + cookie "~0.7.1" + cookie-signature "~1.0.6" debug "2.6.9" depd "2.0.0" encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" + finalhandler "~1.3.1" + fresh "~0.5.2" + http-errors "~2.0.0" merge-descriptors "1.0.3" methods "~1.1.2" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.12" + path-to-regexp "~0.1.12" proxy-addr "~2.0.7" - qs "6.13.0" + qs "~6.14.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" + send "~0.19.0" + serve-static "~1.16.2" setprototypeof "1.2.0" - statuses "2.0.1" + statuses "~2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -965,15 +1093,6 @@ extend@^3.0.2: resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - farmhash-modern@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz" @@ -984,12 +1103,18 @@ fast-deep-equal@^3.1.1: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-xml-parser@^4.4.1: - version "4.5.3" - resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz" - integrity sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig== +fast-xml-builder@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz" + integrity sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ== + +fast-xml-parser@^5.3.4: + version "5.4.2" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.2.tgz" + integrity sha512-pw/6pIl4k0CSpElPEJhDppLzaixDEuWui2CUQQBH/ECDf7+y6YwA4Gf7Tyb0Rfe4DIMuZipYj4AEL0nACKglvQ== dependencies: - strnum "^1.1.1" + fast-xml-builder "^1.0.0" + strnum "^2.1.2" faye-websocket@0.11.4: version "0.11.4" @@ -1005,7 +1130,7 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -finalhandler@1.3.1: +finalhandler@~1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz" integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== @@ -1037,13 +1162,14 @@ firebase-admin@^12.0.0: "@google-cloud/storage" "^7.7.0" form-data@^2.5.0: - version "2.5.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz" - integrity sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ== + version "2.5.5" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" es-set-tostringtag "^2.1.0" + hasown "^2.0.2" mime-types "^2.1.35" safe-buffer "^5.2.1" @@ -1052,7 +1178,7 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fresh@0.5.2: +fresh@~0.5.2, fresh@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -1198,7 +1324,7 @@ html-entities@^2.5.2: resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz" integrity sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ== -http-errors@2.0.0: +http-errors@~2.0.0, http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== @@ -1209,6 +1335,17 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-parser-js@>=0.5.1: version "0.5.10" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" @@ -1239,7 +1376,14 @@ https-proxy-agent@^7.0.1: agent-base "^7.1.2" debug "4" -iconv-lite@^0.4.24, iconv-lite@0.4.24: +iconv-lite@^0.7.0: + version "0.7.2" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@~0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -1251,21 +1395,21 @@ ieee754@^1.1.13: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -inherits@^2.0.3, inherits@^2.0.4, inherits@2.0.4: +inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inquirer@^8.2.6: - version "8.2.6" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz" - integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== + version "8.2.7" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz" + integrity sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA== dependencies: + "@inquirer/external-editor" "^1.0.0" ansi-escapes "^4.2.1" chalk "^4.1.1" cli-cursor "^3.1.0" cli-width "^3.0.0" - external-editor "^3.0.3" figures "^3.0.0" lodash "^4.17.21" mute-stream "0.0.8" @@ -1362,7 +1506,7 @@ jsonwebtoken@^9.0.0: ms "^2.1.1" semver "^7.5.4" -jwa@^1.4.1: +jwa@^1.4.2: version "1.4.2" resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz" integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== @@ -1371,7 +1515,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jwa@^2.0.0: +jwa@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz" integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== @@ -1393,19 +1537,19 @@ jwks-rsa@^3.1.0: lru-memoizer "^2.2.0" jws@^3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + version "3.2.3" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz" + integrity sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g== dependencies: - jwa "^1.4.1" + jwa "^1.4.2" safe-buffer "^5.0.1" jws@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + version "4.0.1" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz" + integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== dependencies: - jwa "^2.0.0" + jwa "^2.0.1" safe-buffer "^5.0.1" limiter@^1.1.5: @@ -1459,9 +1603,9 @@ lodash.once@^4.0.0: integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + version "4.17.23" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.1.0: version "4.1.0" @@ -1566,9 +1710,9 @@ node-fetch@^2.6.9, node-fetch@^2.7.0: whatwg-url "^5.0.0" node-forge@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + version "1.3.3" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz" + integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg== object-hash@^3.0.0: version "3.0.0" @@ -1580,7 +1724,7 @@ object-inspect@^1.13.3: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== -on-finished@2.4.1: +on-finished@~2.4.1, on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -1625,11 +1769,6 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== - p-limit@^3.0.1: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" @@ -1642,7 +1781,7 @@ parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-to-regexp@0.1.12: +path-to-regexp@~0.1.12: version "0.1.12" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== @@ -1690,27 +1829,27 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -qs@6.13.0: - version "6.13.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== +qs@~6.14.0: + version "6.14.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz" + integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" range-parser@~1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.2" @@ -1765,7 +1904,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -1775,7 +1914,7 @@ semver@^7.5.4: resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== -send@0.19.0: +send@~0.19.0, send@0.19.0: version "0.19.0" resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== @@ -1794,7 +1933,7 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" -serve-static@1.16.2: +serve-static@~1.16.2: version "1.16.2" resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== @@ -1804,7 +1943,7 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" -setprototypeof@1.2.0: +setprototypeof@~1.2.0, setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -1838,7 +1977,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.0.6: +side-channel@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== @@ -1859,11 +1998,16 @@ source-map@^0.5.0: resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -statuses@2.0.1: +statuses@~2.0.1, statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + stream-events@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" @@ -1899,10 +2043,10 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strnum@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz" - integrity sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA== +strnum@^2.1.2: + version "2.2.0" + resolved "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz" + integrity sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg== stubs@^3.0.0: version "3.0.0" @@ -1932,19 +2076,12 @@ through@^2.3.6: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -toidentifier@1.0.1: +toidentifier@~1.0.1, toidentifier@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -1982,7 +2119,7 @@ undici-types@~6.21.0: resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== From a8db9151c9934c429619431c51c4595fb68d15ac Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:33:06 +0300 Subject: [PATCH 3/8] ci: add lenient conventional commits and PR title checks (#55) * Initial plan * ci: enforce conventional commits and PR titles - Add .github/copilot-instructions.md with conventions and checklists - Add .github/workflows/conventional-pr-title.yml for PR title enforcement - Add .github/workflows/commitlint.yml for commit message enforcement - Add commitlint.config.cjs with config-conventional and custom scopes - Update package.json with @commitlint/cli and @commitlint/config-conventional devDeps - Update CONTRIBUTING.md to document commit and PR title conventions Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * docs: add base branch rule to copilot instructions Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix(ci): remove subject enforcement from commitlint and PR title check Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix(ci): make commit type non-mandatory, only basic message is required Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --- .github/copilot-instructions.md | 147 ++++ .github/workflows/commitlint.yml | 33 + .github/workflows/conventional-pr-title.yml | 35 + CONTRIBUTING.md | 69 +- commitlint.config.cjs | 15 + npm-shrinkwrap.json | 798 +++++++++++++++++++- package.json | 2 + yarn.lock | 544 ++++++++++--- 8 files changed, 1496 insertions(+), 147 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/workflows/commitlint.yml create mode 100644 .github/workflows/conventional-pr-title.yml create mode 100644 commitlint.config.cjs diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..7d67533 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,147 @@ +# Copilot Agent Instructions for firebase-tools-cli + +## Conventional Commits + +All commit messages should follow the [Conventional Commits](https://www.conventionalcommits.org/) specification where possible. Only basic format is checked by CI (non-empty message). + +### Format + +``` +type(scope)!: subject +``` + +- **type** (optional): `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` — or any descriptive word +- **scope** (optional): area of the codebase affected — see allowed scopes below +- **`!`** (optional): denotes a breaking change +- **subject** (optional): describe the change + +### Examples + +``` +feat(cli): add --dry-run flag to firestore export +fix(rtdb): handle missing databaseURL gracefully +docs: update README with new auth commands +chore: bump version to 0.6.0 +ci: add commitlint workflow +refactor(firestore)!: rename importData to importDocuments +``` + +### Allowed Scopes + +`cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps` + +--- + +## PR Title Convention + +PR titles must follow the same Conventional Commits format as commit messages: + +``` +type(scope)!: subject +``` + +This is enforced by the **Conventional PR Title** CI workflow. A PR with a non-conforming title will fail the check. + +### Valid PR title examples + +``` +feat(auth): add Google sign-in support +fix(rtdb): resolve connection timeout on large datasets +chore: bump version to 0.6.0 +docs: clarify setup instructions in README +``` + +--- + +## Branch Naming Conventions + +Follow the pattern `type/short-description` or `type/issue-short-description`: + +``` +feat/add-auth-login +fix/rtdb-timeout +docs/update-contributing +chore/bump-dependencies +ci/add-commitlint +``` + +--- + +## ⚠️ High-Risk Areas — Handle with Extra Care + +### `dist/` Build Artifacts +- **Never commit `dist/`** to the repository. It is listed in `.gitignore`. +- The `dist/` folder is generated by the build and bundled by esbuild. +- CI will build from source; any manual edits to `dist/` will be lost. + +### CLI Packaging +- The package is distributed as a CLI binary via npm (`firebase-tools-cli`). +- Entry point: `dist/index.js` (must be executable — `chmod +x` is applied in the build). +- The `bin` field in `package.json` must not be changed without updating the shebang and build config. +- Always verify `firebase-tools-cli --help` works after packaging. + +### Node 18 Compatibility +- Minimum Node.js version is `18.0.0` (see `engines` in `package.json`). +- Do **not** use Node.js APIs introduced after v18 without a compatibility guard. +- CI runs on Node 18; your code must work on Node 18. + +### Tests Not Configured +- `npm test` currently exits with an error ("no test specified"). +- Do **not** rely on tests to validate correctness. Use manual CLI verification instead. +- If you add tests, update `package.json` scripts accordingly. + +--- + +## Mandatory Verification Steps Before Opening a PR + +Run every step below and confirm it passes before opening or updating a PR: + +```bash +# 1. Install dependencies from lockfile +npm ci + +# 2. TypeScript type check (no emit) +npx tsc --noEmit + +# 3. Prettier formatting check +npx prettier --check "src/**/*.{ts,js,json}" + +# 4. Build the project +npm run build + +# 5. Package and test CLI installation end-to-end +npm pack +npm install -g firebase-tools-cli-*.tgz +firebase-tools-cli --help +``` + +All steps must exit with code 0 before the PR is opened. + +--- + +## Base Branch Rule (MANDATORY) + +When opening a pull request, always set the base branch as follows: + +- **Default:** `base = development` — use this for all regular work (features, fixes, docs, ci, chore, etc.) +- **Parent branch:** if the work is a follow-up to an existing branch/PR, use that branch as the base (not `development` and not `main`) +- **`main`:** only use `base = main` if explicitly instructed by a maintainer (e.g., hotfix release) + +> Never default to `main` unless a maintainer explicitly requests it. + +--- + +## PR Checklist + +Include this checklist in every PR description: + +- [ ] PR title is non-empty (conventional format recommended but not required) +- [ ] All commit messages follow Conventional Commits format +- [ ] `npm ci` passes without errors +- [ ] `npx tsc --noEmit` passes +- [ ] `npx prettier --check "src/**/*.{ts,js,json}"` passes +- [ ] `npm run build` produces a working `dist/` +- [ ] `npm pack && npm install -g firebase-tools-cli-*.tgz && firebase-tools-cli --help` succeeds +- [ ] No `dist/` files are committed +- [ ] Documentation updated if user-facing functionality changed +- [ ] Relevant issue linked (non-issue PRs are not merged) diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..011fcdf --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,33 @@ +name: Commitlint + +on: + pull_request: + branches: [main, development] + +permissions: + contents: read + +jobs: + commitlint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Lint commit messages + run: | + npx commitlint \ + --from ${{ github.event.pull_request.base.sha }} \ + --to ${{ github.event.pull_request.head.sha }} \ + --verbose diff --git a/.github/workflows/conventional-pr-title.yml b/.github/workflows/conventional-pr-title.yml new file mode 100644 index 0000000..750c8c7 --- /dev/null +++ b/.github/workflows/conventional-pr-title.yml @@ -0,0 +1,35 @@ +name: Conventional PR Title + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + branches: [main, development] + +permissions: + contents: read + +jobs: + validate-pr-title: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Validate PR title follows Conventional Commits + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + echo "PR title: $PR_TITLE" + + # Conventional Commits regex (lenient): + # type(scope)!: subject + # - type: any word (optional) + # - scope: optional, alphanumeric with hyphens + # - !: optional breaking change marker + # - subject: anything after the colon + PATTERN='^([a-zA-Z][a-zA-Z0-9_-]*(\([a-zA-Z0-9_-]+\))?!?: .*|.+)$' + + if echo "$PR_TITLE" | grep -qP "$PATTERN"; then + echo "✅ PR title is valid." + else + echo "❌ PR title must not be empty." + exit 1 + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ee9989..ebc0b09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,31 +53,75 @@ Thank you for your interest in contributing to Firebase Tools CLI! This guide wi ### Before You Submit -1. **Test your changes** thoroughly -2. **Run the linter** to check for code style issues -3. **Make sure all tests pass** -4. **Update documentation** if necessary +1. **Run all checks** to ensure everything passes: -### Creating a Pull Request + ```bash + npm ci + npx tsc --noEmit + npx prettier --check "src/**/*.{ts,js,json}" + npm run build + npm pack && npm install -g firebase-tools-cli-*.tgz && firebase-tools-cli --help + ``` + +2. **Update documentation** if your changes affect user-facing functionality. + +### Commit Message Convention + +Commit messages should follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) where possible. CI checks that commit messages are non-empty; type and subject format are not strictly enforced. + +**Recommended format:** `type(scope)!: subject` + +| Field | Details | +|-------|---------| +| `type` | Optional: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` | +| `scope` | Optional: `cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps` | +| `!` | Optional: marks a breaking change | +| `subject` | Describe the change (no format enforcement) | + +**Examples:** + +``` +feat(cli): add --dry-run flag to firestore export +fix(rtdb): handle missing databaseURL gracefully +docs: update README with new auth commands +chore: bump version to 0.6.0 +``` + +### PR Title Convention + +PR titles should follow the Conventional Commits format where possible. The CI workflow only checks that the title is non-empty. + +``` +type(scope)!: subject +``` -1. **Conventional commits** for your commit messages. For details, see [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). +### Branch Naming Conventions -2. **Conventional branch names** for your branch names. For details, see [Conventional Branches](https://conventional-branch.github.io/). For example, `feat/add-new-feature` or `bugfix/fix-123`. +Follow the pattern `type/short-description`: + +``` +feat/add-auth-login +fix/rtdb-timeout +docs/update-contributing +chore/bump-dependencies +``` + +### Creating a Pull Request -3. **Push your branch** to your fork: +1. **Push your branch** to your fork: ```bash - git push origin feature/your-feature-name + git push origin feat/your-feature-name ``` -4. **Create a pull request** on GitHub: +2. **Create a pull request** on GitHub: - Go to the main repository - Click "New Pull Request" - Select your branch - Fill out the pull request template -5. **Write a clear description** that includes: +3. **Write a clear description** that includes: - What changes you made - Why you made them - Any relevant issue numbers (e.g., "Fixes #123") @@ -85,7 +129,8 @@ Thank you for your interest in contributing to Firebase Tools CLI! This guide wi ### Pull Request Guidelines - **One feature per PR**: Keep pull requests focused on a single feature or fix -- **Clear title**: Use a descriptive title that explains what the PR does +- **Conventional title**: PR title must follow the Conventional Commits format — enforced by CI +- **Conventional commits**: All commits must follow Conventional Commits — enforced by CI - **Detailed description**: Explain the changes and why they're needed - **Link issues**: Reference any related issues in your description. Non-issue PRs will not be merged. diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 0000000..934945c --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1,15 @@ +/** @type {import('@commitlint/types').UserConfig} */ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'type-enum': [0], + 'type-case': [0], + 'type-empty': [0], + 'scope-enum': [0], + 'scope-empty': [0], + 'subject-case': [0], + 'subject-empty': [0], + 'subject-full-stop': [0], + 'subject-max-length': [0], + }, +}; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 0f7e659..e6dc000 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -23,6 +23,8 @@ "firebase-tools-cli": "dist/index.js" }, "devDependencies": { + "@commitlint/cli": "^20.4.3", + "@commitlint/config-conventional": "^20.4.3", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/express": "^4.17.21", "@types/inquirer": "^8.2.10", @@ -234,6 +236,262 @@ "node": ">=6.9.0" } }, + "node_modules/@commitlint/cli": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.3.tgz", + "integrity": "sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^20.4.3", + "@commitlint/lint": "^20.4.3", + "@commitlint/load": "^20.4.3", + "@commitlint/read": "^20.4.3", + "@commitlint/types": "^20.4.3", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.3.tgz", + "integrity": "sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "conventional-changelog-conventionalcommits": "^9.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.4.3.tgz", + "integrity": "sha512-jCZpZFkcSL3ZEdL5zgUzFRdytv3xPo8iukTe9VA+QGus/BGhpp1xXSVu2B006GLLb2gYUAEGEqv64kTlpZNgmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.4.3.tgz", + "integrity": "sha512-WcXGKBNn0wBKpX8VlXgxqedyrLxedIlLBCMvdamLnJFEbUGJ9JZmBVx4vhLV3ZyA8uONGOb+CzW0Y9HDbQ+ONQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", + "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.4.3.tgz", + "integrity": "sha512-UDJVErjLbNghop6j111rsHJYGw6MjCKAi95K0GT2yf4eeiDHy3JDRLWYWEjIaFgO+r+dQSkuqgJ1CdMTtrvHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.4.3.tgz", + "integrity": "sha512-W5VQKZ7fdJ1X3Tko+h87YZaqRMGN1KvQKXyCM8xFdxzMIf1KCZgN4uLz3osLB1zsFcVS4ZswHY64LI26/9ACag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.4.3.tgz", + "integrity": "sha512-CYOXL23e+nRKij81+d0+dymtIi7Owl9QzvblJYbEfInON/4MaETNSLFDI74LDu+YJ0ML5HZyw9Vhp9QpckwQ0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^20.4.3", + "@commitlint/parse": "^20.4.3", + "@commitlint/rules": "^20.4.3", + "@commitlint/types": "^20.4.3" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.4.3.tgz", + "integrity": "sha512-3cdJOUVP+VcgHa7bhJoWS+Z8mBNXB5aLWMBu7Q7uX8PSeWDzdbrBlR33J1MGGf7r1PZDp+mPPiFktk031PgdRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.4.3", + "@commitlint/execute-rule": "^20.0.0", + "@commitlint/resolve-extends": "^20.4.3", + "@commitlint/types": "^20.4.3", + "cosmiconfig": "^9.0.1", + "cosmiconfig-typescript-loader": "^6.1.0", + "is-plain-obj": "^4.1.0", + "lodash.mergewith": "^4.6.2", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", + "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.4.3.tgz", + "integrity": "sha512-hzC3JCo3zs3VkQ833KnGVuWjWIzR72BWZWjQM7tY/7dfKreKAm7fEsy71tIFCRtxf2RtMP2d3RLF1U9yhFSccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.4.3", + "conventional-changelog-angular": "^8.2.0", + "conventional-commits-parser": "^6.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.4.3.tgz", + "integrity": "sha512-j42OWv3L31WfnP8WquVjHZRt03w50Y/gEE8FAyih7GQTrIv2+pZ6VZ6pWLD/ml/3PO+RV2SPtRtTp/MvlTb8rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^20.4.3", + "@commitlint/types": "^20.4.3", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.4.3.tgz", + "integrity": "sha512-QucxcOy+00FhS9s4Uy0OyS5HeUV+hbC6OLqkTSIm6fwMdKva+OEavaCDuLtgd9akZZlsUo//XzSmPP3sLKBPog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.4.3", + "@commitlint/types": "^20.4.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.4.3.tgz", + "integrity": "sha512-Yuosd7Grn5qiT7FovngXLyRXTMUbj9PYiSkvUgWK1B5a7+ZvrbWDS7epeUapYNYatCy/KTpPFPbgLUdE+MUrBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^20.4.3", + "@commitlint/message": "^20.4.3", + "@commitlint/to-lines": "^20.0.0", + "@commitlint/types": "^20.4.3" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", + "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", + "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.4.3.tgz", + "integrity": "sha512-51OWa1Gi6ODOasPmfJPq6js4pZoomima4XLZZCrkldaH2V5Nb3bVhNXPeT6XV0gubbainSpTw4zi68NqAeCNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^6.3.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -1044,6 +1302,19 @@ "license": "BSD-3-Clause", "optional": true }, + "node_modules/@simple-libs/stream-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", + "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -1299,6 +1570,23 @@ "node": ">= 6.0.0" } }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1338,12 +1626,26 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, "node_modules/arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -1547,6 +1849,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1606,8 +1918,8 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "devOptional": true, "license": "ISC", - "optional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -1621,8 +1933,8 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "devOptional": true, "license": "MIT", - "optional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1684,6 +1996,17 @@ "node": ">=16" } }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1705,6 +2028,49 @@ "node": ">= 0.6" } }, + "node_modules/conventional-changelog-angular": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz", + "integrity": "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.0.tgz", + "integrity": "sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz", + "integrity": "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", @@ -1720,6 +2086,64 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -1787,6 +2211,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1854,6 +2291,26 @@ "once": "^1.4.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1944,8 +2401,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "devOptional": true, "license": "MIT", - "optional": true, "engines": { "node": ">=6" } @@ -2064,8 +2521,25 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT", - "optional": true + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { "version": "1.0.0", @@ -2292,8 +2766,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "devOptional": true, "license": "ISC", - "optional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -2335,6 +2809,54 @@ "node": ">= 0.4" } }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "deprecated": "This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2577,12 +3099,60 @@ ], "license": "BSD-3-Clause" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/inquirer": { "version": "8.2.7", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", @@ -2618,6 +3188,13 @@ "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -2651,6 +3228,29 @@ "node": ">=8" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2694,6 +3294,16 @@ "dev": true, "license": "MIT" }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jose": { "version": "4.15.9", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", @@ -2710,6 +3320,19 @@ "dev": true, "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -2732,6 +3355,20 @@ "bignumber.js": "^9.0.0" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -2818,6 +3455,13 @@ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, + "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/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", @@ -2828,8 +3472,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT", - "optional": true + "devOptional": true, + "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", @@ -2873,12 +3517,47 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -2942,6 +3621,19 @@ "node": ">= 0.6" } }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -3002,6 +3694,16 @@ "node": ">=6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3167,6 +3869,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3342,12 +4076,32 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "devOptional": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -3599,6 +4353,16 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3729,6 +4493,16 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -3924,8 +4698,8 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "devOptional": true, "license": "ISC", - "optional": true, "engines": { "node": ">=10" } @@ -3940,8 +4714,8 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, "license": "MIT", - "optional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -3959,8 +4733,8 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true, "license": "ISC", - "optional": true, "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 680c1af..bf71dc1 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,8 @@ }, "homepage": "https://github.com/omer-ayhan/firebase-tools-cli#readme", "devDependencies": { + "@commitlint/cli": "^20.4.3", + "@commitlint/config-conventional": "^20.4.3", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/express": "^4.17.21", "@types/inquirer": "^8.2.10", diff --git a/yarn.lock b/yarn.lock index f9f1994..af65872 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.27.1": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -118,131 +118,165 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@esbuild/aix-ppc64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz" - integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA== - -"@esbuild/android-arm@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz" - integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA== - -"@esbuild/android-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz" - integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg== - -"@esbuild/android-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz" - integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw== - -"@esbuild/darwin-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz" - integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== - -"@esbuild/darwin-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz" - integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ== - -"@esbuild/freebsd-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz" - integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw== - -"@esbuild/freebsd-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz" - integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw== - -"@esbuild/linux-arm@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz" - integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw== - -"@esbuild/linux-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz" - integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg== - -"@esbuild/linux-ia32@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz" - integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA== - -"@esbuild/linux-loong64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz" - integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg== - -"@esbuild/linux-mips64el@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz" - integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg== - -"@esbuild/linux-ppc64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz" - integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ== +"@commitlint/cli@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.3.tgz" + integrity sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA== + dependencies: + "@commitlint/format" "^20.4.3" + "@commitlint/lint" "^20.4.3" + "@commitlint/load" "^20.4.3" + "@commitlint/read" "^20.4.3" + "@commitlint/types" "^20.4.3" + tinyexec "^1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.3.tgz" + integrity sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg== + dependencies: + "@commitlint/types" "^20.4.3" + conventional-changelog-conventionalcommits "^9.2.0" + +"@commitlint/config-validator@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.4.3.tgz" + integrity sha512-jCZpZFkcSL3ZEdL5zgUzFRdytv3xPo8iukTe9VA+QGus/BGhpp1xXSVu2B006GLLb2gYUAEGEqv64kTlpZNgmA== + dependencies: + "@commitlint/types" "^20.4.3" + ajv "^8.11.0" + +"@commitlint/ensure@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.4.3.tgz" + integrity sha512-WcXGKBNn0wBKpX8VlXgxqedyrLxedIlLBCMvdamLnJFEbUGJ9JZmBVx4vhLV3ZyA8uONGOb+CzW0Y9HDbQ+ONQ== + dependencies: + "@commitlint/types" "^20.4.3" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^20.0.0": + version "20.0.0" + resolved "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz" + integrity sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw== + +"@commitlint/format@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/format/-/format-20.4.3.tgz" + integrity sha512-UDJVErjLbNghop6j111rsHJYGw6MjCKAi95K0GT2yf4eeiDHy3JDRLWYWEjIaFgO+r+dQSkuqgJ1CdMTtrvHsA== + dependencies: + "@commitlint/types" "^20.4.3" + picocolors "^1.1.1" -"@esbuild/linux-riscv64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz" - integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA== +"@commitlint/is-ignored@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.4.3.tgz" + integrity sha512-W5VQKZ7fdJ1X3Tko+h87YZaqRMGN1KvQKXyCM8xFdxzMIf1KCZgN4uLz3osLB1zsFcVS4ZswHY64LI26/9ACag== + dependencies: + "@commitlint/types" "^20.4.3" + semver "^7.6.0" + +"@commitlint/lint@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/lint/-/lint-20.4.3.tgz" + integrity sha512-CYOXL23e+nRKij81+d0+dymtIi7Owl9QzvblJYbEfInON/4MaETNSLFDI74LDu+YJ0ML5HZyw9Vhp9QpckwQ0A== + dependencies: + "@commitlint/is-ignored" "^20.4.3" + "@commitlint/parse" "^20.4.3" + "@commitlint/rules" "^20.4.3" + "@commitlint/types" "^20.4.3" + +"@commitlint/load@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/load/-/load-20.4.3.tgz" + integrity sha512-3cdJOUVP+VcgHa7bhJoWS+Z8mBNXB5aLWMBu7Q7uX8PSeWDzdbrBlR33J1MGGf7r1PZDp+mPPiFktk031PgdRw== + dependencies: + "@commitlint/config-validator" "^20.4.3" + "@commitlint/execute-rule" "^20.0.0" + "@commitlint/resolve-extends" "^20.4.3" + "@commitlint/types" "^20.4.3" + cosmiconfig "^9.0.1" + cosmiconfig-typescript-loader "^6.1.0" + is-plain-obj "^4.1.0" + lodash.mergewith "^4.6.2" + picocolors "^1.1.1" -"@esbuild/linux-s390x@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz" - integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ== +"@commitlint/message@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz" + integrity sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ== + +"@commitlint/parse@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/parse/-/parse-20.4.3.tgz" + integrity sha512-hzC3JCo3zs3VkQ833KnGVuWjWIzR72BWZWjQM7tY/7dfKreKAm7fEsy71tIFCRtxf2RtMP2d3RLF1U9yhFSccA== + dependencies: + "@commitlint/types" "^20.4.3" + conventional-changelog-angular "^8.2.0" + conventional-commits-parser "^6.3.0" + +"@commitlint/read@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/read/-/read-20.4.3.tgz" + integrity sha512-j42OWv3L31WfnP8WquVjHZRt03w50Y/gEE8FAyih7GQTrIv2+pZ6VZ6pWLD/ml/3PO+RV2SPtRtTp/MvlTb8rQ== + dependencies: + "@commitlint/top-level" "^20.4.3" + "@commitlint/types" "^20.4.3" + git-raw-commits "^4.0.0" + minimist "^1.2.8" + tinyexec "^1.0.0" + +"@commitlint/resolve-extends@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.4.3.tgz" + integrity sha512-QucxcOy+00FhS9s4Uy0OyS5HeUV+hbC6OLqkTSIm6fwMdKva+OEavaCDuLtgd9akZZlsUo//XzSmPP3sLKBPog== + dependencies: + "@commitlint/config-validator" "^20.4.3" + "@commitlint/types" "^20.4.3" + global-directory "^4.0.1" + import-meta-resolve "^4.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + +"@commitlint/rules@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/rules/-/rules-20.4.3.tgz" + integrity sha512-Yuosd7Grn5qiT7FovngXLyRXTMUbj9PYiSkvUgWK1B5a7+ZvrbWDS7epeUapYNYatCy/KTpPFPbgLUdE+MUrBg== + dependencies: + "@commitlint/ensure" "^20.4.3" + "@commitlint/message" "^20.4.3" + "@commitlint/to-lines" "^20.0.0" + "@commitlint/types" "^20.4.3" + +"@commitlint/to-lines@^20.0.0": + version "20.0.0" + resolved "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz" + integrity sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw== + +"@commitlint/top-level@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz" + integrity sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ== + dependencies: + escalade "^3.2.0" + +"@commitlint/types@^20.4.3": + version "20.4.3" + resolved "https://registry.npmjs.org/@commitlint/types/-/types-20.4.3.tgz" + integrity sha512-51OWa1Gi6ODOasPmfJPq6js4pZoomima4XLZZCrkldaH2V5Nb3bVhNXPeT6XV0gubbainSpTw4zi68NqAeCNCg== + dependencies: + conventional-commits-parser "^6.3.0" + picocolors "^1.1.1" "@esbuild/linux-x64@0.25.5": version "0.25.5" resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz" integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw== -"@esbuild/netbsd-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz" - integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw== - -"@esbuild/netbsd-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz" - integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ== - -"@esbuild/openbsd-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz" - integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw== - -"@esbuild/openbsd-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz" - integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg== - -"@esbuild/sunos-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz" - integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA== - -"@esbuild/win32-arm64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz" - integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw== - -"@esbuild/win32-ia32@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz" - integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ== - -"@esbuild/win32-x64@0.25.5": - version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz" - integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== - "@fastify/busboy@^3.0.0": version "3.1.1" resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz" @@ -489,6 +523,11 @@ resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@simple-libs/stream-utils@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz" + integrity sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA== + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" @@ -672,6 +711,16 @@ agent-base@6: dependencies: debug "4" +ajv@^8.11.0: + version "8.18.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" @@ -691,11 +740,21 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + arrify@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" @@ -784,6 +843,11 @@ call-bound@^1.0.2: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" @@ -852,6 +916,14 @@ commander@^11.0.0: resolved "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + content-disposition@~0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" @@ -864,6 +936,28 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== +conventional-changelog-angular@^8.2.0: + version "8.3.0" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz" + integrity sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA== + dependencies: + compare-func "^2.0.0" + +conventional-changelog-conventionalcommits@^9.2.0: + version "9.3.0" + resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.0.tgz" + integrity sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA== + dependencies: + compare-func "^2.0.0" + +conventional-commits-parser@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz" + integrity sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg== + dependencies: + "@simple-libs/stream-utils" "^1.2.0" + meow "^13.0.0" + cookie-signature@~1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -874,6 +968,28 @@ cookie@~0.7.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cosmiconfig-typescript-loader@^6.1.0: + version "6.2.0" + resolved "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz" + integrity sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ== + dependencies: + jiti "^2.6.1" + +cosmiconfig@^9.0.1, cosmiconfig@>=9: + version "9.0.1" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz" + integrity sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + +dargs@^8.0.0: + version "8.1.0" + resolved "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz" + integrity sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw== + debug@^4.1.0, debug@^4.3.4, debug@4: version "4.4.1" resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" @@ -915,6 +1031,13 @@ destroy@~1.2.0, destroy@1.2.0: resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +dot-prop@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" @@ -968,6 +1091,18 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +error-ex@^1.3.1: + version "1.3.4" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== + dependencies: + is-arrayish "^0.2.1" + es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" @@ -1026,7 +1161,7 @@ esbuild@^0.25.5: "@esbuild/win32-ia32" "0.25.5" "@esbuild/win32-x64" "0.25.5" -escalade@^3.1.1: +escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== @@ -1098,11 +1233,16 @@ farmhash-modern@^1.1.0: resolved "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz" integrity sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + fast-xml-builder@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz" @@ -1242,6 +1382,22 @@ get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +git-raw-commits@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz" + integrity sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ== + dependencies: + dargs "^8.0.0" + meow "^12.0.1" + split2 "^4.0.0" + +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== + dependencies: + ini "4.1.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" @@ -1395,11 +1551,29 @@ ieee754@^1.1.13: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +import-fresh@^3.3.0: + version "3.3.1" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-meta-resolve@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz" + integrity sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg== + inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + inquirer@^8.2.6: version "8.2.7" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz" @@ -1426,6 +1600,11 @@ ipaddr.js@1.9.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -1441,6 +1620,16 @@ is-interactive@^1.0.0: resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" @@ -1463,6 +1652,11 @@ javascript-natural-sort@0.7.1: resolved "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz" integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== +jiti@^2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz" + integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== + jose@^4.15.4: version "4.15.9" resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz" @@ -1473,6 +1667,13 @@ js-tokens@^4.0.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -1490,6 +1691,16 @@ json-bigint@^1.0.0: dependencies: bignumber.js "^9.0.0" +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + jsonwebtoken@^9.0.0: version "9.0.2" resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" @@ -1557,6 +1768,11 @@ limiter@^1.1.5: resolved "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz" integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" @@ -1597,11 +1813,36 @@ lodash.isstring@^4.0.1: resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + lodash@^4.17.21: version "4.17.23" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz" @@ -1645,6 +1886,16 @@ media-typer@0.3.0: resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +meow@^12.0.1: + version "12.1.1" + resolved "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz" + integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== + +meow@^13.0.0: + version "13.2.0" + resolved "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz" + integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA== + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" @@ -1682,6 +1933,11 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + ms@^2.1.1, ms@^2.1.3, ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -1776,6 +2032,23 @@ p-limit@^3.0.1: dependencies: yocto-queue "^0.1.0" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -1865,6 +2138,21 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -1909,7 +2197,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^7.5.4: +semver@^7.5.4, semver@^7.6.0: version "7.7.2" resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== @@ -1998,6 +2286,11 @@ source-map@^0.5.0: resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + statuses@~2.0.1, statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" @@ -2076,6 +2369,11 @@ through@^2.3.6: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tinyexec@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz" + integrity sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" @@ -2109,7 +2407,7 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@^5.8.3: +typescript@^5.8.3, typescript@>=4.9.5, typescript@>=5: version "5.8.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== @@ -2226,7 +2524,7 @@ yargs-parser@^21.1.1: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.7.2: +yargs@^17.0.0, yargs@^17.7.2: version "17.7.2" resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== From b1d3375aac5a4720cff42349c04d9fdea2952865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Ayhan?= Date: Sat, 7 Mar 2026 15:48:48 +0300 Subject: [PATCH 4/8] Remove detailed export format and pretty-printing; output single compact importable JSON (#56) * Initial plan * Remove detailed export and pretty formatting; default to single compact importable export Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * build: update package version in package json * chore: add shrinkwrap to postinstall * Fix subcollection check and collections processed count in firestore-export Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 8 +- npm-shrinkwrap.json | 4 +- package.json | 4 +- src/actions/firestore/firestore-export.ts | 136 +++------------------ src/actions/rtdb/rtdb-export.ts | 105 ++-------------- src/commands/firestore/firestore-export.ts | 2 - src/commands/rtdb/rtdb-export.ts | 4 +- 7 files changed, 38 insertions(+), 225 deletions(-) diff --git a/README.md b/README.md index 059a216..dc0f9f9 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Below is a brief list of the available commands and their function: | Command | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------- | -| **firestore:export** | Export all collections from Firestore. Supports detailed and importable formats with subcollection handling. | +| **firestore:export** | Export all collections from Firestore to a single compact importable JSON file (`firestore_export.json`). Supports subcollection handling and collection exclusions. | | **firestore:import** | Import data to Firestore from JSON file. Supports batch operations and merge functionality. | | **firestore:list** | List all collections and their basic information from the current project's Firestore database. | | **firestore:query** | Query a collection or fetch a specific document. Supports advanced filtering, ordering, and field-specific queries. | @@ -58,7 +58,7 @@ Below is a brief list of the available commands and their function: | Command | Description | | --------------- | ----------------------------------------------------------------------------------------------------------- | -| **rtdb:export** | Export all data from Realtime Database. Supports detailed and importable formats with exclusion options. | +| **rtdb:export** | Export all data from Realtime Database to a single compact importable JSON file (`rtdb_export.json`). Supports exclusion options and top-level-only export. | | **rtdb:import** | Import data to Realtime Database from JSON file. Supports batch operations and merge functionality. | | **rtdb:list** | List all top-level nodes and their basic information from the current project's Realtime Database. | | **rtdb:query** | Query a specific path in Realtime Database. Supports filtering, ordering, and JSON output with file saving. | @@ -94,13 +94,13 @@ To clear the default project setting, run `firebase-tools-cli projects --clear-d firebase-tools-cli firestore:export --output ./backup-$(date +%Y%m%d)/ # Import data to another project -firebase-tools-cli firestore:import ./backup-20231201/firestore-export.json +firebase-tools-cli firestore:import ./backup-20231201/firestore_export.json # Export Realtime Database firebase-tools-cli rtdb:export --database-url https://source-project-rtdb.firebaseio.com/ --output ./rtdb-backup/ # Import to target database -firebase-tools-cli rtdb:import ./rtdb-backup/rtdb-export.json --database-url https://target-project-rtdb.firebaseio.com/ +firebase-tools-cli rtdb:import ./rtdb-backup/rtdb_export.json --database-url https://target-project-rtdb.firebaseio.com/ ``` ### Advanced Querying diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e6dc000..05bbaea 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "firebase-tools-cli", - "version": "0.5.3", + "version": "0.5.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebase-tools-cli", - "version": "0.5.3", + "version": "0.5.9", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index bf71dc1..ea263e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-tools-cli", - "version": "0.5.3", + "version": "0.5.9", "files": [ "dist/**/*", "README.md", @@ -24,7 +24,7 @@ "release": "chmod +x ./scripts/release.sh && ./scripts/release.sh", "prepublishOnly": "npm run build", "prepare": "npm run clean && npm run build", - "postinstall": "test -f dist/index.js && chmod +x dist/index.js || true" + "postinstall": "npm shrinkwrap && test -f dist/index.js && chmod +x dist/index.js || true" }, "keywords": [ "firestore", diff --git a/src/actions/firestore/firestore-export.ts b/src/actions/firestore/firestore-export.ts index 56d55aa..ca13262 100644 --- a/src/actions/firestore/firestore-export.ts +++ b/src/actions/firestore/firestore-export.ts @@ -7,9 +7,7 @@ import { QueryDocumentSnapshotType } from '@/types'; type ExportCommandOptionsType = { exclude?: string[]; - noSubcollections?: boolean; - detailed?: boolean; - importable?: boolean; + subcollections?: boolean; output?: string; }; @@ -29,21 +27,10 @@ export async function exportCollections(options: ExportCommandOptionsType) { chalk.cyan(`📁 Found ${collections.length} top-level collections\n`) ); - const allData: { - [key: string]: { - id: string; - data: any; - createTime: admin.firestore.Timestamp; - updateTime: admin.firestore.Timestamp; - error?: string; - subcollections?: { - [key: string]: any[]; - }; - }[]; - } = {}; const importData: ImportData = {}; let totalDocsRead = 0; let totalSubDocsRead = 0; + let collectionsProcessed = 0; for (const collection of collections) { const collectionName = collection.id; @@ -60,7 +47,6 @@ export async function exportCollections(options: ExportCommandOptionsType) { try { const snapshot = await collection.get(); - const documents = []; let collectionDocsRead = 0; let collectionSubDocsRead = 0; @@ -80,31 +66,14 @@ export async function exportCollections(options: ExportCommandOptionsType) { }, 300); for (const doc of snapshot.docs) { - const docData: { - id: string; - data: any; - createTime: admin.firestore.Timestamp; - updateTime: admin.firestore.Timestamp; - subcollections?: { - [key: string]: any[]; - }; - } = { - id: doc.id, - data: doc.data(), - createTime: doc.createTime, - updateTime: doc.updateTime, - }; - // Add to importable format importData[collectionName][doc.id] = doc.data(); collectionDocsRead++; // Handle subcollections if enabled - if (!options.noSubcollections) { + if (options.subcollections !== false) { const subcollections = await doc.ref.listCollections(); if (subcollections.length > 0) { - docData.subcollections = {}; - // Clear loading line and show subcollection info clearInterval(loadingInterval); process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line @@ -116,30 +85,23 @@ export async function exportCollections(options: ExportCommandOptionsType) { for (const subcol of subcollections) { const subSnapshot = await subcol.get(); - const subDocs: any[] = []; // For importable format const subCollectionPath = `${collectionName}__${doc.id}__${subcol.id}`; importData[subCollectionPath] = {}; + let subDocsRead = 0; subSnapshot.forEach((subDoc: QueryDocumentSnapshotType) => { - const subDocData = { - id: subDoc.id, - data: subDoc.data(), - createTime: subDoc.createTime, - updateTime: subDoc.updateTime, - }; - subDocs.push(subDocData); collectionSubDocsRead++; + subDocsRead++; // Add to importable format importData[subCollectionPath][subDoc.id] = subDoc.data(); }); - docData.subcollections[subcol.id] = subDocs; console.log( chalk.gray( - ` └── Subcollection ${subcol.id}: ${subDocs.length} documents read` + ` └── Subcollection ${subcol.id}: ${subDocsRead} documents read` ) ); } @@ -156,17 +118,15 @@ export async function exportCollections(options: ExportCommandOptionsType) { } } } - - documents.push(docData); } // Clear loading indicator clearInterval(loadingInterval); process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - allData[collectionName] = documents; totalDocsRead += collectionDocsRead; totalSubDocsRead += collectionSubDocsRead; + collectionsProcessed++; // Show final count for this collection const subCollectionText = @@ -189,61 +149,30 @@ export async function exportCollections(options: ExportCommandOptionsType) { } } - // Generate file names - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const outputDir = options.output || './'; - console.log(chalk.blue('💾 Saving export files...')); + console.log(chalk.blue('💾 Saving export file...')); // Create saving loading indicator let savingDots = 0; - let savingInterval = setInterval(() => { + const savingInterval = setInterval(() => { const dots = '.'.repeat((savingDots % 3) + 1); - process.stdout.write(`\r${chalk.gray(` └── Writing files${dots} `)}`); + process.stdout.write(`\r${chalk.gray(` └── Writing file${dots} `)}`); savingDots++; }, 200); - // Save detailed format - if (options.detailed !== false) { - const detailedFile = path.join( - outputDir, - `firestore_detailed_${timestamp}.json` - ); - fs.writeFileSync(detailedFile, JSON.stringify(allData, null, 2)); - - clearInterval(savingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log(chalk.green(`📄 Detailed backup saved: ${detailedFile}`)); - - // Restart saving indicator if we have more files to save - if (options.importable !== false) { - savingInterval = setInterval(() => { - const dots = '.'.repeat((savingDots % 3) + 1); - process.stdout.write( - `\r${chalk.gray(` └── Writing files${dots} `)}` - ); - savingDots++; - }, 200); - } - } - - // Save importable format - if (options.importable !== false) { - const importableFile = path.join( - outputDir, - `firestore_importable_${timestamp}.json` - ); - fs.writeFileSync(importableFile, JSON.stringify(importData, null, 2)); + const exportFile = path.join(outputDir, 'firestore_export.json'); + fs.writeFileSync(exportFile, JSON.stringify(importData)); - clearInterval(savingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log(chalk.green(`📤 Importable backup saved: ${importableFile}`)); - } + clearInterval(savingInterval); + process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line + console.log(chalk.green(`📤 Export saved: ${exportFile}`)); - // Summary with detailed read counts + // Summary + const exportSize = (fs.statSync(exportFile).size / 1024 / 1024).toFixed(2); console.log(chalk.blue('\n📊 Export Summary:')); console.log( - chalk.gray(` └── Collections processed: ${Object.keys(allData).length}`) + chalk.gray(` └── Collections processed: ${collectionsProcessed}`) ); console.log(chalk.gray(` └── Documents read: ${totalDocsRead}`)); @@ -256,34 +185,7 @@ export async function exportCollections(options: ExportCommandOptionsType) { ); } - // Calculate file sizes - if (options.detailed !== false) { - const detailedFile = path.join( - outputDir, - `firestore_detailed_${timestamp}.json` - ); - const detailedSize = ( - fs.statSync(detailedFile).size / - 1024 / - 1024 - ).toFixed(2); - console.log(chalk.gray(` └── Detailed file size: ${detailedSize} MB`)); - } - - if (options.importable !== false) { - const importableFile = path.join( - outputDir, - `firestore_importable_${timestamp}.json` - ); - const importableSize = ( - fs.statSync(importableFile).size / - 1024 / - 1024 - ).toFixed(2); - console.log( - chalk.gray(` └── Importable file size: ${importableSize} MB`) - ); - } + console.log(chalk.gray(` └── Export file size: ${exportSize} MB`)); console.log(chalk.green('\n🎉 Export completed successfully!')); } catch (error) { diff --git a/src/actions/rtdb/rtdb-export.ts b/src/actions/rtdb/rtdb-export.ts index 509f0f7..8c845c3 100644 --- a/src/actions/rtdb/rtdb-export.ts +++ b/src/actions/rtdb/rtdb-export.ts @@ -8,8 +8,6 @@ import { countNodes } from '@/utils'; type ExportRTDBOptionsType = { exclude?: string[]; subcollections?: boolean; - detailed?: boolean; - importable?: boolean; output?: string; }; @@ -17,14 +15,6 @@ export async function exportRealtimeDatabase(options: ExportRTDBOptionsType) { try { console.log(chalk.blue('🔍 Starting Realtime Database export...\n')); - if (options.importable === false && options.detailed === false) { - console.log(chalk.yellow('💡 No export format selected')); - console.log( - chalk.gray(' • Use --detailed or --importable to export data') - ); - return; - } - // Get the database reference (should be configured during initialization) const rtdbApp = admin.app('rtdb-app'); const rtdb = rtdbApp.database(); @@ -71,108 +61,33 @@ export async function exportRealtimeDatabase(options: ExportRTDBOptionsType) { allData = topLevelData; } - // Generate file names - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const outputDir = options.output || './'; - console.log(chalk.blue('💾 Saving export files...')); + console.log(chalk.blue('💾 Saving export file...')); // Create saving loading indicator let savingDots = 0; - let savingInterval = setInterval(() => { + const savingInterval = setInterval(() => { const dots = '.'.repeat((savingDots % 3) + 1); - process.stdout.write(`\r${chalk.gray(` └── Writing files${dots} `)}`); + process.stdout.write(`\r${chalk.gray(` └── Writing file${dots} `)}`); savingDots++; }, 200); - let filesCreated = 0; - - // Save detailed format (includes metadata) - if (options.detailed !== false) { - const detailedData = { - exportInfo: { - timestamp: new Date().toISOString(), - source: 'Firebase Realtime Database', - databaseUrl: rtdbApp.options.databaseURL, - exportedBy: 'firebase-tools-cli', - totalNodes: countNodes(allData), - }, - data: allData, - }; - - const detailedFile = path.join( - outputDir, - `rtdb_detailed_${timestamp}.json` - ); - fs.writeFileSync(detailedFile, JSON.stringify(detailedData, null, 2)); - filesCreated++; - - clearInterval(savingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log(chalk.green(`📄 Detailed backup saved: ${detailedFile}`)); - - // Restart saving indicator if we have more files to save - if (options.importable !== false) { - savingInterval = setInterval(() => { - const dots = '.'.repeat((savingDots % 3) + 1); - process.stdout.write( - `\r${chalk.gray(` └── Writing files${dots} `)}` - ); - savingDots++; - }, 200); - } - } + const exportFile = path.join(outputDir, 'rtdb_export.json'); + fs.writeFileSync(exportFile, JSON.stringify(allData)); - // Save importable format (clean data only) - if (options.importable !== false) { - const importableFile = path.join( - outputDir, - `rtdb_importable_${timestamp}.json` - ); - fs.writeFileSync(importableFile, JSON.stringify(allData, null, 2)); - filesCreated++; - - clearInterval(savingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log(chalk.green(`📤 Importable backup saved: ${importableFile}`)); - } + clearInterval(savingInterval); + process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line + console.log(chalk.green(`📤 Export saved: ${exportFile}`)); // Summary + const exportSize = (fs.statSync(exportFile).size / 1024 / 1024).toFixed(2); console.log(chalk.blue('\n📊 Export Summary:')); console.log(chalk.gray(` └── Database: ${rtdbApp.options.databaseURL}`)); console.log( chalk.gray(` └── Total nodes exported: ${countNodes(allData)}`) ); - console.log(chalk.gray(` └── Files created: ${filesCreated}`)); - - // Calculate file sizes - if (options.detailed !== false) { - const detailedFile = path.join( - outputDir, - `rtdb_detailed_${timestamp}.json` - ); - const detailedSize = ( - fs.statSync(detailedFile).size / - 1024 / - 1024 - ).toFixed(2); - console.log(chalk.gray(` └── Detailed file size: ${detailedSize} MB`)); - } - - if (options.importable !== false) { - const importableFile = path.join( - outputDir, - `rtdb_importable_${timestamp}.json` - ); - const importableSize = ( - fs.statSync(importableFile).size / - 1024 / - 1024 - ).toFixed(2); - console.log( - chalk.gray(` └── Importable file size: ${importableSize} MB`) - ); - } + console.log(chalk.gray(` └── Export file size: ${exportSize} MB`)); console.log( chalk.green('\n🎉 Realtime Database export completed successfully!') diff --git a/src/commands/firestore/firestore-export.ts b/src/commands/firestore/firestore-export.ts index eab091b..68e6be6 100644 --- a/src/commands/firestore/firestore-export.ts +++ b/src/commands/firestore/firestore-export.ts @@ -7,8 +7,6 @@ const firestoreExport = program .createCommand('firestore:export') .description('Export all collections from Firestore') .option('-o, --output ', 'Output directory', './') - .option('--no-detailed', 'Skip detailed format export') - .option('--no-importable', 'Skip importable format export') .option('--no-subcollections', 'Skip subcollections') .option('-e, --exclude ', 'Exclude specific collections') .action(async (options) => { diff --git a/src/commands/rtdb/rtdb-export.ts b/src/commands/rtdb/rtdb-export.ts index 461a79e..1c8e15d 100644 --- a/src/commands/rtdb/rtdb-export.ts +++ b/src/commands/rtdb/rtdb-export.ts @@ -8,8 +8,6 @@ const rtdbExport = program .createCommand('rtdb:export') .description('Export all data from Realtime Database') .option('-o, --output ', 'Output directory', './') - .option('--no-detailed', 'Skip detailed format export') - .option('--no-importable', 'Skip importable format export') .option('--no-subcollections', 'Skip nested data (limit to top level only)') .option('-e, --exclude ', 'Exclude specific top-level paths') .option('-d, --database-url ', 'Firebase Realtime Database URL') @@ -20,7 +18,7 @@ const rtdbExport = program Examples: $ firebase-tools-cli rtdb:export --database-url https://my-project-default-rtdb.firebaseio.com/ --output ./backups/ $ firebase-tools-cli rtdb:export --database-url https://my-project-default-rtdb.firebaseio.com/ --exclude users logs --output ./backups/ - $ firebase-tools-cli rtdb:export --database-url https://my-project-default-rtdb.firebaseio.com/ --no-subcollections --no-detailed` + $ firebase-tools-cli rtdb:export --database-url https://my-project-default-rtdb.firebaseio.com/ --no-subcollections` ) .action(async (options) => { try { From 6da522decd39cb873794f8f1234f15bf9f635ef8 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:32:21 +0300 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20Firestore=20export=20=E2=80=94=20pa?= =?UTF-8?q?rallel=20collection=20processing=20&=20streaming=20output=20(#5?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * feat: improve Firestore export with parallelization and streaming Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * build: update cli version in package json * ci: update shrinkwrap * build: add a type check command --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> Co-authored-by: Ömer Ayhan --- package.json | 1 + src/actions/firestore/firestore-export.ts | 307 ++++++++++++--------- src/commands/firestore/firestore-export.ts | 22 +- yarn.lock | 120 ++++++++ 4 files changed, 324 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index ea263e6..ad291a4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "format": "prettier --write \"src/**/*.{ts,js,json}\"", "format:check": "prettier --check \"src/**/*.{ts,js,json}\"", "release": "chmod +x ./scripts/release.sh && ./scripts/release.sh", + "check:types": "tsc --noEmit", "prepublishOnly": "npm run build", "prepare": "npm run clean && npm run build", "postinstall": "npm shrinkwrap && test -f dist/index.js && chmod +x dist/index.js || true" diff --git a/src/actions/firestore/firestore-export.ts b/src/actions/firestore/firestore-export.ts index ca13262..1471b54 100644 --- a/src/actions/firestore/firestore-export.ts +++ b/src/actions/firestore/firestore-export.ts @@ -2,24 +2,58 @@ import chalk from 'chalk'; import * as admin from 'firebase-admin'; import fs from 'fs'; import path from 'path'; - -import { QueryDocumentSnapshotType } from '@/types'; +import { Writable } from 'stream'; +import zlib from 'zlib'; type ExportCommandOptionsType = { exclude?: string[]; subcollections?: boolean; output?: string; + concurrency?: number; + gzip?: boolean; }; -type ImportData = { - [key: string]: { - [key: string]: any; - }; -}; +/** Write a string to a Writable stream, respecting backpressure. */ +function writeToStream(stream: Writable, data: string): Promise { + return new Promise((resolve, reject) => { + const onError = (err: Error) => reject(err); + stream.once('error', onError); + + // The write callback fires once the data has been accepted (or on error), + // so we don't need a separate 'drain' listener. + stream.write(data, (writeErr) => { + stream.removeListener('error', onError); + if (writeErr) reject(writeErr); + else resolve(); + }); + }); +} + +/** Process items with at most `concurrency` tasks running simultaneously. */ +async function runWithConcurrency( + items: T[], + concurrency: number, + fn: (item: T) => Promise +): Promise { + const queue = [...items]; + const workers = Array.from( + { length: Math.min(concurrency, items.length) }, + async () => { + while (queue.length > 0) { + const item = queue.shift()!; + await fn(item); + } + } + ); + await Promise.all(workers); +} export async function exportCollections(options: ExportCommandOptionsType) { try { const db = admin.firestore(); + const concurrency = options.concurrency ?? 5; + const useGzip = options.gzip ?? false; + console.log(chalk.blue('🔍 Starting Firestore export...\n')); const collections = await db.listCollections(); @@ -27,149 +61,173 @@ export async function exportCollections(options: ExportCommandOptionsType) { chalk.cyan(`📁 Found ${collections.length} top-level collections\n`) ); - const importData: ImportData = {}; - let totalDocsRead = 0; - let totalSubDocsRead = 0; - let collectionsProcessed = 0; - - for (const collection of collections) { - const collectionName = collection.id; - - // Skip collections if specified - if (options.exclude && options.exclude.includes(collectionName)) { + // Filter excluded collections up-front + const filteredCollections = collections.filter((collection) => { + if (options.exclude && options.exclude.includes(collection.id)) { console.log( - chalk.yellow(`⏭️ Skipping excluded collection: ${collectionName}`) + chalk.yellow(`⏭️ Skipping excluded collection: ${collection.id}`) ); - continue; + return false; } + return true; + }); - console.log(chalk.blue(`📖 Reading collection: ${collectionName}`)); - - try { - const snapshot = await collection.get(); - let collectionDocsRead = 0; - let collectionSubDocsRead = 0; + // Set up output file with optional gzip compression + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const outputDir = options.output || './'; + const ext = useGzip ? 'json.gz' : 'json'; + const outputFile = path.join( + outputDir, + `firestore_export_${timestamp}.${ext}` + ); - console.log(chalk.gray(` └── Documents found: ${snapshot.size}`)); + const fileStream = fs.createWriteStream(outputFile); + let outputStream: Writable; + if (useGzip) { + const gzipStream = zlib.createGzip(); + gzipStream.pipe(fileStream); + outputStream = gzipStream; + } else { + outputStream = fileStream; + } - // For importable format - importData[collectionName] = {}; + // Counters (updated inside the serialised write lock, so no data races) + let firstEntry = true; + let totalDocsRead = 0; + let totalSubDocsRead = 0; + let collectionsProcessed = 0; - // Create loading indicator - let loadingDots = 0; - let loadingInterval = setInterval(() => { - const dots = '.'.repeat((loadingDots % 3) + 1); - process.stdout.write( - `\r${chalk.gray(` └── Processing${dots} `)}` - ); - loadingDots++; - }, 300); - - for (const doc of snapshot.docs) { - // Add to importable format - importData[collectionName][doc.id] = doc.data(); - collectionDocsRead++; - - // Handle subcollections if enabled - if (options.subcollections !== false) { - const subcollections = await doc.ref.listCollections(); - if (subcollections.length > 0) { - // Clear loading line and show subcollection info - clearInterval(loadingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log( - chalk.gray( - ` └── Document ${doc.id} has ${subcollections.length} subcollections` - ) + // All writes to the output stream are serialized through this promise + // chain so concurrent collection tasks never interleave their JSON chunks. + let writeLock: Promise = writeToStream(outputStream, '{'); + + /** + * Schedule a serialized write of one collection's data. + * Resolves once the chunk has been flushed to the stream. + * + * Each collection is written as a raw JSON key/value pair appended to the + * top-level object. Manual construction (rather than a single + * JSON.stringify of the whole dataset) is intentional: it lets us stream + * each collection to disk as soon as it finishes loading, keeping memory + * usage proportional to the largest single collection rather than the + * entire database. + */ + const appendToStream = ( + collectionName: string, + collectionData: { [docId: string]: any }, + subData: { [subPath: string]: { [docId: string]: any } } + ): Promise => { + return new Promise((resolve, reject) => { + writeLock = writeLock.then(async () => { + try { + const prefix = firstEntry ? '' : ','; + firstEntry = false; + + await writeToStream( + outputStream, + `${prefix}${JSON.stringify(collectionName)}:${JSON.stringify( + collectionData + )}` + ); + + for (const [subPath, subDocs] of Object.entries(subData)) { + await writeToStream( + outputStream, + `,${JSON.stringify(subPath)}:${JSON.stringify(subDocs)}` ); + } + resolve(); + } catch (err) { + reject(err); + } + }); + }); + }; + + // Process collections in parallel up to the configured concurrency limit + await runWithConcurrency( + filteredCollections, + concurrency, + async (collection) => { + const collectionName = collection.id; + console.log(chalk.blue(`📖 Reading collection: ${collectionName}`)); + + try { + const snapshot = await collection.get(); + console.log(chalk.gray(` └── Documents found: ${snapshot.size}`)); + + const collectionData: { [docId: string]: any } = {}; + const subData: { [subPath: string]: { [docId: string]: any } } = {}; + let collectionDocsRead = 0; + let collectionSubDocsRead = 0; + + for (const doc of snapshot.docs) { + collectionData[doc.id] = doc.data(); + collectionDocsRead++; + + // Subcollections are opt-in for performance + if (options.subcollections) { + const subcollections = await doc.ref.listCollections(); for (const subcol of subcollections) { const subSnapshot = await subcol.get(); + const subPath = `${collectionName}__${doc.id}__${subcol.id}`; + subData[subPath] = {}; - // For importable format - const subCollectionPath = `${collectionName}__${doc.id}__${subcol.id}`; - importData[subCollectionPath] = {}; - - let subDocsRead = 0; - subSnapshot.forEach((subDoc: QueryDocumentSnapshotType) => { + subSnapshot.forEach((subDoc) => { + subData[subPath][subDoc.id] = subDoc.data(); collectionSubDocsRead++; - subDocsRead++; - - // Add to importable format - importData[subCollectionPath][subDoc.id] = subDoc.data(); }); console.log( chalk.gray( - ` └── Subcollection ${subcol.id}: ${subDocsRead} documents read` + ` └── Subcollection ${collectionName}/${doc.id}/${subcol.id}: ${subSnapshot.size} documents` ) ); } - - // Restart loading indicator if there are more documents - if (collectionDocsRead < snapshot.size) { - loadingInterval = setInterval(() => { - const dots = '.'.repeat((loadingDots % 3) + 1); - process.stdout.write( - `\r${chalk.gray(` └── Processing${dots} `)}` - ); - loadingDots++; - }, 300); - } } } - } - - // Clear loading indicator - clearInterval(loadingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - totalDocsRead += collectionDocsRead; - totalSubDocsRead += collectionSubDocsRead; - collectionsProcessed++; - - // Show final count for this collection - const subCollectionText = - collectionSubDocsRead > 0 - ? chalk.gray(` + ${collectionSubDocsRead} subdocuments`) - : ''; - - console.log( - chalk.green( - ` ✅ Collection ${collectionName} exported: ${collectionDocsRead} documents${subCollectionText}\n` - ) - ); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : String(error); - console.error( - chalk.red(` ❌ Error reading collection ${collectionName}:`), - errorMessage - ); + // Stream this collection's data to the output file + await appendToStream(collectionName, collectionData, subData); + + totalDocsRead += collectionDocsRead; + totalSubDocsRead += collectionSubDocsRead; + collectionsProcessed++; + + const subText = + collectionSubDocsRead > 0 + ? chalk.gray(` + ${collectionSubDocsRead} subdocuments`) + : ''; + console.log( + chalk.green( + ` ✅ Collection ${collectionName} exported: ${collectionDocsRead} documents${subText}\n` + ) + ); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + chalk.red(` ❌ Error reading collection ${collectionName}:`), + errorMessage + ); + } } - } - - const outputDir = options.output || './'; - - console.log(chalk.blue('💾 Saving export file...')); - - // Create saving loading indicator - let savingDots = 0; - const savingInterval = setInterval(() => { - const dots = '.'.repeat((savingDots % 3) + 1); - process.stdout.write(`\r${chalk.gray(` └── Writing file${dots} `)}`); - savingDots++; - }, 200); + ); - const exportFile = path.join(outputDir, 'firestore_export.json'); - fs.writeFileSync(exportFile, JSON.stringify(importData)); + // Wait for all pending writes to finish, then close the JSON object + await writeLock; + await writeToStream(outputStream, '}'); - clearInterval(savingInterval); - process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line - console.log(chalk.green(`📤 Export saved: ${exportFile}`)); + // Close the stream(s) + await new Promise((resolve, reject) => { + outputStream.end((err?: Error | null) => { + if (err) reject(err); + else resolve(); + }); + }); // Summary - const exportSize = (fs.statSync(exportFile).size / 1024 / 1024).toFixed(2); console.log(chalk.blue('\n📊 Export Summary:')); console.log( chalk.gray(` └── Collections processed: ${collectionsProcessed}`) @@ -185,7 +243,10 @@ export async function exportCollections(options: ExportCommandOptionsType) { ); } - console.log(chalk.gray(` └── Export file size: ${exportSize} MB`)); + const fileSize = (fs.statSync(outputFile).size / 1024 / 1024).toFixed(2); + console.log( + chalk.gray(` └── Export file: ${outputFile} (${fileSize} MB)`) + ); console.log(chalk.green('\n🎉 Export completed successfully!')); } catch (error) { diff --git a/src/commands/firestore/firestore-export.ts b/src/commands/firestore/firestore-export.ts index 68e6be6..c47fca3 100644 --- a/src/commands/firestore/firestore-export.ts +++ b/src/commands/firestore/firestore-export.ts @@ -5,13 +5,29 @@ import { exportCollections } from '@/actions/firestore/firestore-export'; const firestoreExport = program .createCommand('firestore:export') - .description('Export all collections from Firestore') + .description( + 'Export all Firestore collections to a compact importable JSON file.\n' + + 'Top-level collections are exported in parallel (see --concurrency) to\n' + + 'maximise throughput. Output is streamed directly to disk so memory usage\n' + + 'stays low even for very large databases. Subcollection export is opt-in\n' + + '(--subcollections) so the common case is as fast as possible.' + ) .option('-o, --output ', 'Output directory', './') - .option('--no-subcollections', 'Skip subcollections') + .option( + '-c, --concurrency ', + 'Number of collections exported in parallel', + '5' + ) + .option('--subcollections', 'Also export subcollections (opt-in)') + .option('--gzip', 'Compress output with gzip (.json.gz)') .option('-e, --exclude ', 'Exclude specific collections') .action(async (options) => { try { - await exportCollections(options); + const concurrency = parseInt(options.concurrency, 10); + await exportCollections({ + ...options, + concurrency: Number.isFinite(concurrency) ? concurrency : 5, + }); process.exit(0); } catch (error) { process.exit(1); diff --git a/yarn.lock b/yarn.lock index af65872..2452e8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -272,11 +272,131 @@ conventional-commits-parser "^6.3.0" picocolors "^1.1.1" +"@esbuild/aix-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz" + integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA== + +"@esbuild/android-arm@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz" + integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA== + +"@esbuild/android-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz" + integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg== + +"@esbuild/android-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz" + integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw== + +"@esbuild/darwin-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz" + integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== + +"@esbuild/darwin-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz" + integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ== + +"@esbuild/freebsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz" + integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw== + +"@esbuild/freebsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz" + integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw== + +"@esbuild/linux-arm@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz" + integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw== + +"@esbuild/linux-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz" + integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg== + +"@esbuild/linux-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz" + integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA== + +"@esbuild/linux-loong64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz" + integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg== + +"@esbuild/linux-mips64el@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz" + integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg== + +"@esbuild/linux-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz" + integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ== + +"@esbuild/linux-riscv64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz" + integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA== + +"@esbuild/linux-s390x@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz" + integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ== + "@esbuild/linux-x64@0.25.5": version "0.25.5" resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz" integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw== +"@esbuild/netbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz" + integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw== + +"@esbuild/netbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz" + integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ== + +"@esbuild/openbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz" + integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw== + +"@esbuild/openbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz" + integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg== + +"@esbuild/sunos-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz" + integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA== + +"@esbuild/win32-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz" + integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw== + +"@esbuild/win32-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz" + integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ== + +"@esbuild/win32-x64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz" + integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== + "@fastify/busboy@^3.0.0": version "3.1.1" resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz" From 3c69d0e58a078b5280fbe45cabb05487df6af81e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:57:12 +0300 Subject: [PATCH 6/8] feat(ci): add Bun runtime support (#60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc: contributing file * ci: add lenient conventional commits and PR title checks (#55) * Initial plan * ci: enforce conventional commits and PR titles - Add .github/copilot-instructions.md with conventions and checklists - Add .github/workflows/conventional-pr-title.yml for PR title enforcement - Add .github/workflows/commitlint.yml for commit message enforcement - Add commitlint.config.cjs with config-conventional and custom scopes - Update package.json with @commitlint/cli and @commitlint/config-conventional devDeps - Update CONTRIBUTING.md to document commit and PR title conventions Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * docs: add base branch rule to copilot instructions Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix(ci): remove subject enforcement from commitlint and PR title check Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix(ci): make commit type non-mandatory, only basic message is required Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * Initial plan * feat(ci): add Bun support for improved performance Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * build: update yarn lock file --------- Co-authored-by: Ömer Ayhan Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --- .github/workflows/bun-compat.yml | 39 ++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 12 +++++----- README.md | 21 ++++++++++++++++- llm.txt | 3 ++- npm-shrinkwrap.json | 1 + package.json | 3 ++- 6 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/bun-compat.yml diff --git a/.github/workflows/bun-compat.yml b/.github/workflows/bun-compat.yml new file mode 100644 index 0000000..3fac4bf --- /dev/null +++ b/.github/workflows/bun-compat.yml @@ -0,0 +1,39 @@ +name: Bun Compatibility + +on: + push: + branches: [development] + pull_request: + branches: [main, development] + +permissions: + contents: read + +jobs: + bun-compat: + name: Verify Bun compatibility + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies with Bun + run: bun install + + - name: TypeScript compilation check + run: bunx tsc --noEmit + + - name: Build project with Bun + run: bun esbuild.config.js && chmod +x dist/index.js + + - name: Test CLI help (Bun) + run: bun run dist/index.js --help + + - name: All Bun checks passed + run: echo "✅ Bun compatibility checks passed successfully!" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ebc0b09..3fb5561 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,12 +71,12 @@ Commit messages should follow [Conventional Commits](https://www.conventionalcom **Recommended format:** `type(scope)!: subject` -| Field | Details | -|-------|---------| -| `type` | Optional: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` | -| `scope` | Optional: `cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps` | -| `!` | Optional: marks a breaking change | -| `subject` | Describe the change (no format enforcement) | +| Field | Details | +| --------- | ------------------------------------------------------------------------------------------------------ | +| `type` | Optional: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` | +| `scope` | Optional: `cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps` | +| `!` | Optional: marks a breaking change | +| `subject` | Describe the change (no format enforcement) | **Examples:** diff --git a/README.md b/README.md index dc0f9f9..fb623b1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![License][license-image]][license-url] [![Node Version][node-badge]][npm] [![NPM version][npm-badge]][npm] +[![Bun compatible][bun-badge]][bun-url] The Firebase Tools CLI is a command-line interface for managing Firebase services including Firestore, Realtime Database, and Remote Config. It provides powerful tools to export, import, query, and manage your Firebase data from the command line. @@ -29,6 +30,22 @@ npm install -g firebase-tools-cli This will provide you with the globally accessible `firebase-tools-cli` command. +### Bun + +Firebase Tools CLI also supports [Bun](https://bun.sh/) (>=1.0.0) as a runtime. You can install it globally using Bun's package manager: + +```bash +bun install -g firebase-tools-cli +``` + +Or run commands directly with Bun after a local install: + +```bash +bun run firebase-tools-cli --help +``` + +> **Note:** Core features are expected to be compatible with Bun. Some interactive prompts that rely on Node.js-specific stdin handling may behave slightly differently under Bun. If you encounter issues, please [open an issue](https://github.com/omer-ayhan/firebase-tools-cli/issues). + ## Commands **The command `firebase-tools-cli --help` lists the available commands and `firebase-tools-cli --help` shows more details for an individual command.** @@ -130,7 +147,7 @@ firebase-tools-cli remote-config:convert config.json --version-number 2 --user-e ## Requirements -- Node.js >= 18.0.0 +- Node.js >= 18.0.0 **or** Bun >= 1.0.0 - Valid Firebase project with appropriate permissions - Service account key @@ -161,3 +178,5 @@ Firebase Tools CLI is licensed under the [MIT License](LICENSE.txt). [node-badge]: https://img.shields.io/node/v/firebase-tools-cli.svg [npm]: https://www.npmjs.com/package/firebase-tools-cli [npm-badge]: https://img.shields.io/npm/v/firebase-tools-cli.svg +[bun-badge]: https://img.shields.io/badge/bun-%3E%3D1.0.0-black?logo=bun +[bun-url]: https://bun.sh diff --git a/llm.txt b/llm.txt index 90a89cd..9d3f688 100644 --- a/llm.txt +++ b/llm.txt @@ -271,6 +271,7 @@ firebase-tools-cli - Use service account for production workflows ## Version Information -- Node.js: >=14.0.0 required +- Node.js: >=18.0.0 required (or Bun >=1.0.0) +- Bun: >=1.0.0 supported as an alternative runtime - Firebase Admin SDK: v12.0.0+ - Supports all current Firestore features and regions \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 05bbaea..34c69fc 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -33,6 +33,7 @@ "typescript": "^5.8.3" }, "engines": { + "bun": ">=1.0.0", "node": ">=18.0.0" } }, diff --git a/package.json b/package.json index ad291a4..0dd4922 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "open": "^8.4.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0", + "bun": ">=1.0.0" }, "repository": { "type": "git", From 4c91602941bf7be8186d270a90209742b8231ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Ayhan?= Date: Sat, 7 Mar 2026 17:26:53 +0300 Subject: [PATCH 7/8] Add GitHub feature request issue template (#64) * Initial plan * Add GitHub feature request issue template Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/feature_request.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..24ddede --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea or improvement for this project +title: '[Feature] ' +labels: enhancement +assignees: '' +--- + +## Problem Statement + +A clear description of the problem or limitation you're experiencing. + +## Proposed Solution + +Describe the feature or improvement you'd like to see. + +## Alternatives Considered + +Any alternative solutions or features you've considered. + +## Additional Context + +Add any other context, screenshots, or examples about the feature request here. From c3998d036ed41d64757f5e5e4009c06096e7e9ed Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 17:48:45 +0300 Subject: [PATCH 8/8] feat(firestore): add collection group queries and document nested path support (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * feat(firestore): add collection group and nested path query support - Add --collection-group / -g flag to firestore:query command to query all subcollections with a given name across the entire database using db.collectionGroup() - Extract parseWhereValue() and applyCollectionQueryConstraints() helpers to eliminate duplication between queryCollectionData and the new queryCollectionGroupData function - Add queryCollectionGroupData() action with full output support (console, --json, --output), including index-requirement notice - Update firestore:query help text with subcollection and collection group examples - Update README.md command descriptions and examples for both Firestore nested/group queries and RTDB nested path/field queries - Update llm.txt with Nested Path Queries section covering Firestore subcollections, collection groups, and RTDB deep paths and field filters Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * fix(firestore): remove unused param and fix collection group index warning accuracy - Remove unused collectionPathStr parameter from queryCollectionGroupData - Show the Firestore index warning only when --where or --order-by is specified (simple collection group queries do not require an index) - Update --collection-group option description and help text to reflect that only filtered/ordered queries may require an index Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> * docs: update firestıre query help examples --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: omer-ayhan <32736625+omer-ayhan@users.noreply.github.com> Co-authored-by: Ömer Ayhan --- README.md | 21 +- llm.txt | 31 ++- src/actions/firestore/firestore-query.ts | 251 +++++++++++++++++++--- src/commands/firestore/firestore-query.ts | 24 ++- 4 files changed, 287 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index fb623b1..34a5c69 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Below is a brief list of the available commands and their function: | **firestore:export** | Export all collections from Firestore to a single compact importable JSON file (`firestore_export.json`). Supports subcollection handling and collection exclusions. | | **firestore:import** | Import data to Firestore from JSON file. Supports batch operations and merge functionality. | | **firestore:list** | List all collections and their basic information from the current project's Firestore database. | -| **firestore:query** | Query a collection or fetch a specific document. Supports advanced filtering, ordering, and field-specific queries. | +| **firestore:query** | Query a collection or fetch a specific document. Supports subcollection paths (e.g., `users user1 orders`), collection group queries (`--collection-group`), advanced filtering, ordering, and field-specific queries. | ### Realtime Database Commands @@ -78,7 +78,7 @@ Below is a brief list of the available commands and their function: | **rtdb:export** | Export all data from Realtime Database to a single compact importable JSON file (`rtdb_export.json`). Supports exclusion options and top-level-only export. | | **rtdb:import** | Import data to Realtime Database from JSON file. Supports batch operations and merge functionality. | | **rtdb:list** | List all top-level nodes and their basic information from the current project's Realtime Database. | -| **rtdb:query** | Query a specific path in Realtime Database. Supports filtering, ordering, and JSON output with file saving. | +| **rtdb:query** | Query a specific path in Realtime Database. Supports deep nested paths (e.g., `/root/a/b/c`), nested field filters (`field/subfield,==,value`), ordering, and JSON output with file saving. | ### Remote Config Commands @@ -127,12 +127,29 @@ firebase-tools-cli rtdb:import ./rtdb-backup/rtdb_export.json --database-url htt firebase-tools-cli firestore:query users --where "age,>=,18" --limit 10 firebase-tools-cli firestore:query users --order-by "name,asc" +# Query Firestore subcollections (nested paths) +firebase-tools-cli firestore:query users user1 orders +firebase-tools-cli firestore:query users user1 orders --where "status,==,shipped" --limit 5 +firebase-tools-cli firestore:query users user1 orders order1 + +# Query Firestore collection groups (all subcollections with the same name) +firebase-tools-cli firestore:query orders --collection-group +firebase-tools-cli firestore:query orders --collection-group --where "status,==,shipped" --limit 20 + # Query specific document fields firebase-tools-cli firestore:query users user1 --field profile.settings # Query Realtime Database with filtering firebase-tools-cli rtdb:query users --where "age,>=,18" --limit 10 --database-url https://my-project-rtdb.firebaseio.com/ firebase-tools-cli rtdb:query posts --order-by "timestamp,desc" --json --output results.json + +# Query Realtime Database at a nested path +firebase-tools-cli rtdb:query users/user4/active --database-url https://my-project-rtdb.firebaseio.com/ +firebase-tools-cli rtdb:query users user4 active --database-url https://my-project-rtdb.firebaseio.com/ + +# Query Realtime Database with nested field filters +firebase-tools-cli rtdb:query users --where "workouts/appVersion,==,2.3.1" --database-url https://my-project-rtdb.firebaseio.com/ +firebase-tools-cli rtdb:query workouts --where "settings/difficulty,>=,3" --order-by "settings/duration,desc" --database-url https://my-project-rtdb.firebaseio.com/ ``` ### Remote Config Management diff --git a/llm.txt b/llm.txt index 9d3f688..818c17a 100644 --- a/llm.txt +++ b/llm.txt @@ -31,10 +31,12 @@ A comprehensive command-line interface for Firebase Firestore database operation - `-m, --merge` - Merge documents instead of overwriting - `-e, --exclude ` - Exclude specific collections -- `firebase-tools-cli query ` - Query a specific collection +- `firebase-tools-cli query ` - Query a specific collection or document - `-w, --where ` - Where clause (e.g., "age,>=,18") - `-l, --limit ` - Limit number of results - `-o, --order-by ` - Order by field (e.g., "name,asc") + - `-f, --field ` - Show specific field from document (document queries only) + - `-g, --collection-group` - Query as a collection group across all matching subcollections - `--json` - Output results as JSON - `--output ` - Save JSON output to file @@ -146,6 +148,33 @@ Supported Firestore query operators: - `in` - Value is in array - `not-in` - Value is not in array +## Nested Path Queries + +### Firestore Subcollections +Use space-separated path segments to target subcollections. Odd segments = collection query, even segments = document query: +- `firestore:query users` - Query top-level `users` collection +- `firestore:query users user1 orders` - Query `orders` subcollection under `users/user1` +- `firestore:query users user1 orders order42` - Fetch specific document `users/user1/orders/order42` +- `firestore:query users user1 orders --where "status,==,shipped"` - Filter subcollection + +### Firestore Collection Group Queries +Use `--collection-group` (`-g`) to query all subcollections with a given name across the entire database: +- `firestore:query orders --collection-group` - Query all `orders` subcollections +- `firestore:query orders --collection-group --where "status,==,shipped" --limit 20` +- **Filtered/ordered queries may require a Firestore composite index** - follow the error link to create it in Firebase Console if needed. + +### RTDB Nested Paths +Specify deep paths using either slash-separated or space-separated segments: +- `rtdb:query users/user4/active` - Query at path `/users/user4/active` +- `rtdb:query users user4 active` - Same as above (space-separated) +- `rtdb:query root/a/b/c` - Query at arbitrary depth + +### RTDB Nested Field Filters +Use `/`-separated field paths in `--where` and `--order-by` to filter on nested fields: +- `rtdb:query users --where "workouts/appVersion,==,2.3.1"` - Filter on nested field +- `rtdb:query workouts --order-by "settings/duration,desc"` - Sort by nested field +- `rtdb:query workouts --where "settings/difficulty,>=,3" --order-by "settings/duration,asc"` - Combine both + ## Configuration Files - `~/.firebase-tools-cli/config.json` - Default project and settings - `~/.firebase-tools-cli/credentials.json` - OAuth credentials (auto-managed) diff --git a/src/actions/firestore/firestore-query.ts b/src/actions/firestore/firestore-query.ts index e447a36..8acac48 100644 --- a/src/actions/firestore/firestore-query.ts +++ b/src/actions/firestore/firestore-query.ts @@ -9,6 +9,7 @@ type QueryCommandOptionsType = { field?: string; json?: boolean; output?: string; + collectionGroup?: boolean; }; type QueryDocumentSnapshotType = admin.firestore.QueryDocumentSnapshot< @@ -125,6 +126,24 @@ export async function queryCollection( const db = admin.firestore(); + // Collection group query: queries all subcollections with this name across the entire database + if (options.collectionGroup) { + if (collectionPath.length !== 1) { + throw new Error( + '--collection-group requires exactly one collection ID (e.g., firestore:query orders --collection-group)' + ); + } + if (options.field) { + console.log( + chalk.yellow( + '⚠️ --field option is only available for document queries, not collection group queries' + ) + ); + } + await queryCollectionGroupData(db, collectionPath[0], options); + return; + } + // Check if we're querying a document or a collection const isDocumentQuery = collectionPath.length % 2 === 0; @@ -211,7 +230,7 @@ async function queryDocument( const [filterField, operator, value] = options.where.split(','); if (!filterField || !operator || value === undefined) { throw new Error( - 'Where clause must be in format "field,operator,value" (e.g., "page_type,==,question-page_v1")' + 'Where clause must be in format "field,operator,value" (e.g., "page_type,==,question_1")' ); } @@ -561,6 +580,51 @@ async function queryDocument( } } +// Helper function to parse a where-clause value string to the appropriate JS type +function parseWhereValue(raw: string): string | number | boolean | null { + if (raw === 'true') return true; + if (raw === 'false') return false; + if (raw === 'null') return null; + if (!isNaN(Number(raw))) return Number(raw); + return raw; +} + +// Helper function to apply where/limit/orderBy constraints to a Firestore query +function applyCollectionQueryConstraints( + query: admin.firestore.Query< + admin.firestore.DocumentData, + admin.firestore.DocumentData + >, + options: QueryCommandOptionsType +): admin.firestore.Query< + admin.firestore.DocumentData, + admin.firestore.DocumentData +> { + if (options.where) { + const [field, operator, value] = options.where.split(','); + const operatorType = operator.trim() as admin.firestore.WhereFilterOp; + const parsedValue = parseWhereValue(value.trim()); + query = query.where(field.trim(), operatorType, parsedValue); + console.log( + chalk.gray(` └── Filter: ${field} ${operator} ${parsedValue}`) + ); + } + + if (options.limit) { + query = query.limit(parseInt(options.limit)); + console.log(chalk.gray(` └── Limit: ${options.limit}`)); + } + + if (options.orderBy) { + const [field, direction] = options.orderBy.split(','); + const directionType = direction?.trim() as admin.firestore.OrderByDirection; + query = query.orderBy(field.trim(), directionType || 'asc'); + console.log(chalk.gray(` └── Order: ${field} ${direction || 'asc'}`)); + } + + return query; +} + async function queryCollectionData( db: admin.firestore.Firestore, collectionPath: string[], @@ -594,38 +658,7 @@ async function queryCollectionData( >; } - // Apply where clause - if (options.where) { - const [field, operator, value] = options.where.split(','); - const operatorType = operator.trim() as admin.firestore.WhereFilterOp; - - // Parse value to appropriate type - let parsedValue: any = value.trim(); - if (parsedValue === 'true') parsedValue = true; - else if (parsedValue === 'false') parsedValue = false; - else if (parsedValue === 'null') parsedValue = null; - else if (!isNaN(Number(parsedValue))) parsedValue = Number(parsedValue); - - query = query.where(field.trim(), operatorType, parsedValue); - console.log( - chalk.gray(` └── Filter: ${field} ${operator} ${parsedValue}`) - ); - } - - // Apply limit - if (options.limit) { - query = query.limit(parseInt(options.limit)); - console.log(chalk.gray(` └── Limit: ${options.limit}`)); - } - - // Apply ordering - if (options.orderBy) { - const [field, direction] = options.orderBy.split(','); - const directionType = direction?.trim() as admin.firestore.OrderByDirection; - - query = query.orderBy(field.trim(), directionType || 'asc'); - console.log(chalk.gray(` └── Order: ${field} ${direction || 'asc'}`)); - } + query = applyCollectionQueryConstraints(query, options); const snapshot = await query.get(); @@ -744,3 +777,155 @@ async function queryCollectionData( console.log(chalk.gray(` └── Project: ${admin.app().options.projectId}`)); } } + +async function queryCollectionGroupData( + db: admin.firestore.Firestore, + collectionId: string, + options: QueryCommandOptionsType +) { + console.log( + chalk.cyan( + `🌐 Collection group query: searching all "${collectionId}" subcollections across the database\n` + ) + ); + + // Only filtered/ordered collection group queries require a Firestore index + if (options.where || options.orderBy) { + console.log( + chalk.gray( + ' ⚠️ Note: Filtered/ordered collection group queries may require a Firestore composite index.' + ) + ); + console.log( + chalk.gray( + ' If this query fails with an index error, follow the link in the error' + ) + ); + console.log( + chalk.gray( + ' message to create the required index in Firebase Console.\n' + ) + ); + } + let query: admin.firestore.Query< + admin.firestore.DocumentData, + admin.firestore.DocumentData + > = db.collectionGroup(collectionId); + + query = applyCollectionQueryConstraints(query, options); + + const snapshot = await query.get(); + + if (snapshot.size === 0) { + console.log(chalk.yellow('⚠️ No documents found matching the query')); + return; + } + + console.log(chalk.green(`✅ Found ${snapshot.size} document(s)\n`)); + + // Collect results for JSON output + const results: any[] = []; + + snapshot.forEach((doc: QueryDocumentSnapshotType) => { + const docData = { + id: doc.id, + path: doc.ref.path, + data: doc.data(), + createTime: doc.createTime, + updateTime: doc.updateTime, + }; + + results.push(docData); + + // Only show console output if not JSON mode + if (!options.json) { + console.log(chalk.white(`📁 ${doc.id}`)); + console.log(chalk.gray(` └── Path: ${doc.ref.path}`)); + + // Show field values in detail + const data = doc.data(); + const entries = Object.entries(data); + + // Show up to 10 fields to prevent overwhelming output + const maxDisplay = 10; + const displayEntries = entries.slice(0, maxDisplay); + + for (const [key, value] of displayEntries) { + let displayValue: string; + + if (typeof value === 'object' && value !== null) { + if (Array.isArray(value)) { + displayValue = `[Array with ${value.length} items]`; + } else { + displayValue = `[Object with ${Object.keys(value).length} keys]`; + } + } else if (typeof value === 'string' && value.length > 50) { + displayValue = `${value.substring(0, 50)}...`; + } else { + displayValue = String(value); + } + + console.log(chalk.gray(` └── ${key}: ${displayValue}`)); + } + + if (entries.length > maxDisplay) { + console.log( + chalk.gray( + ` └── ... and ${entries.length - maxDisplay} more fields` + ) + ); + } + + console.log(); + } + }); + + // Prepare output data + const outputData = { + type: 'collection_group', + collectionId: collectionId, + query: { + ...(options.where && { where: options.where }), + ...(options.limit && { limit: parseInt(options.limit) }), + ...(options.orderBy && { orderBy: options.orderBy }), + }, + summary: { + totalDocuments: snapshot.size, + }, + results: results, + timestamp: new Date().toISOString(), + }; + + // Handle file output + if (options.output) { + const outputFile = options.output.endsWith('.json') + ? options.output + : `${options.output}.json`; + + fs.writeFileSync(outputFile, JSON.stringify(outputData, null, 2)); + console.log(chalk.green(`📄 Query results saved to: ${outputFile}`)); + + const fileSize = (fs.statSync(outputFile).size / 1024).toFixed(2); + console.log(chalk.gray(` └── File size: ${fileSize} KB`)); + } + + // Handle JSON console output + if (options.json) { + console.log(JSON.stringify(outputData, null, 2)); + } else if (!options.output) { + // Show detailed summary only if not in JSON mode and no file output + console.log(chalk.blue('📊 Collection Group Query Summary:')); + console.log(chalk.gray(` └── Collection ID: ${collectionId}`)); + console.log(chalk.gray(` └── Total documents: ${snapshot.size}`)); + if (options.where) { + console.log(chalk.gray(` └── Where: ${options.where}`)); + } + if (options.orderBy) { + console.log(chalk.gray(` └── Order by: ${options.orderBy}`)); + } + if (options.limit) { + console.log(chalk.gray(` └── Limit: ${options.limit}`)); + } + console.log(chalk.gray(` └── Project: ${admin.app().options.projectId}`)); + } +} diff --git a/src/commands/firestore/firestore-query.ts b/src/commands/firestore/firestore-query.ts index 64741e8..0b7a0f1 100644 --- a/src/commands/firestore/firestore-query.ts +++ b/src/commands/firestore/firestore-query.ts @@ -24,6 +24,10 @@ const firestoreQuery = program '-f, --field ', 'Show only specific field(s) from document (e.g., "pages" or "pages.0.title") - only for document queries' ) + .option( + '-g, --collection-group', + 'Query as a collection group - searches all subcollections with this name across the entire database (filtered/ordered queries may require a Firestore index)' + ) .option('--json', 'Output results as JSON') .option('--output ', 'Save JSON output to file') .addHelpText( @@ -41,19 +45,31 @@ Examples: $ firebase-tools-cli firestore:query users user1 posts post1 $ firebase-tools-cli firestore:query users user1 posts post1 comments comment1 + Subcollection queries (nested paths): + $ firebase-tools-cli firestore:query users user1 orders + $ firebase-tools-cli firestore:query users user1 orders --where "status,==,shipped" --limit 5 + $ firebase-tools-cli firestore:query users user1 orders order1 + + Collection group queries (across all subcollections with this name): + $ firebase-tools-cli firestore:query orders --collection-group + $ firebase-tools-cli firestore:query orders --collection-group --where "status,==,shipped" + $ firebase-tools-cli firestore:query orders --collection-group --order-by "createdAt,desc" --limit 20 + Field-specific queries (document queries only): - $ firebase-tools-cli firestore:query mobile_onboardings 1 --field pages + $ firebase-tools-cli firestore:query onboardings v1 --field pages $ firebase-tools-cli firestore:query users user1 --field profile.settings $ firebase-tools-cli firestore:query posts post1 --field metadata.tags.0 Field queries with filtering (array fields only): - $ firebase-tools-cli firestore:query mobile_onboardings 1 --field pages --where "page_type,==,question-page_v1" - $ firebase-tools-cli firestore:query mobile_onboardings 1 --field pages --where "page_type,==,question-page_v1" --order-by "page_order,asc" + $ firebase-tools-cli firestore:query onboardings v1 --field pages --where "page_type,==,question_1" + $ firebase-tools-cli firestore:query onboardings v1 --field pages --where "page_type,==,question_1" --order-by "page_order,asc" $ firebase-tools-cli firestore:query users user1 --field posts --where "published,==,true" --limit 5 $ firebase-tools-cli firestore:query posts post1 --field comments --where "rating,>=,4" --order-by "timestamp,desc" Note: Query options (--where, --limit, --order-by) apply to collection queries and document array fields. -Field queries (--field) only apply to document queries.` +Field queries (--field) only apply to document queries. +Collection group queries (--collection-group) with filters or ordering may require a Firestore composite index - follow +the link in the error message to create it in Firebase Console if needed.` ) .action(async (collection, subcollections, options) => { try {