diff --git a/.github/scripts/update-release.js b/.github/scripts/update-release.js index a57956ba7..1ff7d8f12 100644 --- a/.github/scripts/update-release.js +++ b/.github/scripts/update-release.js @@ -1,12 +1,11 @@ const core = require('@actions/core'); -const { Octokit } = require("@octokit/core"); - /** * Functionality from tubone24/update_release. * Link: https://github.com/tubone24/update_release */ const updateRelease = async () => { try { + const { Octokit } = await import('@octokit/core'); const octokit = new Octokit({ auth: process.env.REPO_ACCESS_TOKEN }) diff --git a/README.md b/README.md index 0f7a2a3bd..107fed573 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This module contains core functionality for the twilio-cli. ## Requirements -Currently, Node 20+ is supported. We support the [LTS versions](https://nodejs.org/en/about/releases) of Node. +Currently, Node 22+ is supported. We support the [LTS versions](https://nodejs.org/en/about/releases) of Node. ## Base commands diff --git a/package-lock.json b/package-lock.json index e793f5bf0..10f88fdbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@twilio/cli-core", - "version": "8.3.3", + "version": "8.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@twilio/cli-core", - "version": "8.3.3", + "version": "8.3.4", "license": "MIT", "dependencies": { "@actions/core": "^2.0.0", @@ -15,7 +15,7 @@ "@oclif/plugin-help": "^5.1.3", "@oclif/plugin-plugins": "2.1.0", "@octokit/rest": "^21.1.1", - "axios": "^1.12.0", + "axios": "^1.19.0", "chalk": "^4.1.2", "columnify": "^1.5.4", "fs-extra": "^9.0.1", @@ -25,7 +25,7 @@ "qs": "^6.9.4", "semver": "^7.5.2", "tsv": "^0.2.0", - "twilio": "^5.3.0" + "twilio": "^6.0.0" }, "devDependencies": { "@oclif/test": "^1.2.6", @@ -46,7 +46,7 @@ "tmp": "^0.2.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } }, "node_modules/@actions/core": { @@ -100,13 +100,13 @@ "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -115,9 +115,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -125,21 +125,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -205,14 +205,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.8", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -222,14 +222,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -240,7 +240,7 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", @@ -249,9 +249,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -259,29 +259,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -291,9 +291,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -301,9 +301,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -311,9 +311,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -321,27 +321,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "version": "7.29.8", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -351,33 +351,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.8", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -385,14 +385,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.8", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -493,10 +493,20 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/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==", + "version": "4.3.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -1118,16 +1128,16 @@ } }, "node_modules/@octokit/core": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", - "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "version": "7.0.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/core/-/core-7.0.7.tgz", + "integrity": "sha512-DcB0M3KFgr9ECI328lhBMVsyFT2DnmNucSBTqEN3exyNKUzkkpUSCHmTRcunF41Eou2TIQKW4seewri8ON9bSA==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^6.0.0", - "@octokit/graphql": "^9.0.3", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", + "@octokit/graphql": "^9.0.4", + "@octokit/request": "^10.0.13", + "@octokit/request-error": "^7.1.1", + "@octokit/types": "^17.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, @@ -1135,6 +1145,21 @@ "node": ">= 20" } }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/endpoint": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", @@ -1149,19 +1174,34 @@ } }, "node_modules/@octokit/graphql": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", - "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "version": "9.0.4", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/graphql/-/graphql-9.0.4.tgz", + "integrity": "sha512-5s15CCiY8XXQ+FG+b1YQcl6Z2FA++nwAz/tg2VUrTmnMncP+2nnGUEYANImdnxsA2Fnq+Mbl7hDjUTw7cFAwcg==", "license": "MIT", "dependencies": { - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", + "@octokit/request": "^10.0.13", + "@octokit/types": "^17.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, "node_modules/@octokit/openapi-types": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", @@ -1211,15 +1251,15 @@ } }, "node_modules/@octokit/request": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz", - "integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==", + "version": "10.0.13", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/request/-/request-10.0.13.tgz", + "integrity": "sha512-v2269YxL9Yf+x3d+gRI63FP0vFQEiWgLyBzxe/Y+0yFDg2B/Tzf5dhh9VNfccVAQnfcfwQWyk/y6Bn7rUXXs7A==", "license": "MIT", "dependencies": { "@octokit/endpoint": "^11.0.3", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "fast-content-type-parse": "^3.0.0", + "@octokit/request-error": "^7.1.1", + "@octokit/types": "^17.0.0", + "content-type": "^2.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" }, @@ -1228,17 +1268,60 @@ } }, "node_modules/@octokit/request-error": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", - "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "version": "7.1.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/request-error/-/request-error-7.1.1.tgz", + "integrity": "sha512-+eaY7G2VVpSf2pc5Gn1+mph837V/d/TYTJAgWL9Tb0ogGYcpN3IlAVFgjL+Vv93F/sevrxkvsYCedtpLdcFLzA==", "license": "MIT", "dependencies": { - "@octokit/types": "^16.0.0" + "@octokit/types": "^17.0.0" }, "engines": { "node": ">= 20" } }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "28.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", + "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "17.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/@octokit/types/-/types-17.0.0.tgz", + "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^28.0.0" + } + }, + "node_modules/@octokit/request/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@octokit/rest": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz", @@ -2550,7 +2633,7 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, @@ -2580,13 +2663,14 @@ } }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.19.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -2617,9 +2701,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.27", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.27.tgz", - "integrity": "sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==", + "version": "2.11.10", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/baseline-browser-mapping/-/baseline-browser-mapping-2.11.10.tgz", + "integrity": "sha512-35JEvJ5/KKlbCHjMCsONI2w6HE88STjVdHk+C7d8LtcFxUjZR1KeLP9izofn2qs0KUxX5r4z73bwH/rd+JHacw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2668,9 +2752,9 @@ "peer": true }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -2698,9 +2782,9 @@ "license": "ISC" }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -2718,10 +2802,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -2865,9 +2949,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001792", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", - "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "version": "1.0.30001806", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -3146,7 +3230,7 @@ }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { @@ -3633,7 +3717,7 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { @@ -3790,9 +3874,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.351", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.351.tgz", - "integrity": "sha512-9D7Iqx8RImSvCnOsj86rCH6eQjZFQoM04Jn6HnZVM0Nu/G58/gmKYQ1d12MZTbjQbQSTGI8nwEy07ErsA2slLA==", + "version": "1.5.399", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", + "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", "dev": true, "license": "ISC" }, @@ -4395,10 +4479,20 @@ "license": "Python-2.0" }, "node_modules/eslint/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==", + "version": "4.3.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4529,22 +4623,6 @@ "node": ">=8.0.0" } }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4657,9 +4735,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -4837,16 +4915,16 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -5244,9 +5322,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -5497,9 +5575,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6640,9 +6718,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -6654,7 +6732,7 @@ }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", @@ -7042,7 +7120,7 @@ }, "node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", @@ -7233,7 +7311,7 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { @@ -7242,7 +7320,7 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { @@ -7355,9 +7433,9 @@ "license": "Python-2.0" }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -7365,10 +7443,20 @@ } }, "node_modules/mocha/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==", + "version": "4.3.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7573,11 +7661,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "version": "2.0.51", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-package-data": { "version": "3.0.3", @@ -11863,12 +11954,13 @@ } }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.15.3", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -12739,14 +12831,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -13502,9 +13594,9 @@ } }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { @@ -13668,9 +13760,9 @@ } }, "node_modules/twilio": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.13.1.tgz", - "integrity": "sha512-sT+PkhptF4Mf7t8eXFFvPQx4w5VHnBIPXbltGPMFRe+R2GxfRdMuFbuNA/cEm0aQR6LFQOn33+fhClg+TjRVqQ==", + "version": "6.0.2", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/twilio/-/twilio-6.0.2.tgz", + "integrity": "sha512-RN3TZxUtxLz2HBZVt62+LdZxQbrMVgYKtuzLgwmO7nqKvR+gQS5mCackD9hf4Y7MmoK/bX7tCm7kaJC8kC8zFA==", "license": "MIT", "dependencies": { "axios": "^1.13.5", @@ -13682,7 +13774,7 @@ "xmlbuilder": "^13.0.2" }, "engines": { - "node": ">=14.0" + "node": ">=20.0.0" } }, "node_modules/type-check": { @@ -13857,9 +13949,9 @@ } }, "node_modules/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", + "version": "6.28.0", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -13902,7 +13994,7 @@ }, "node_modules/update-browserslist-db": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ @@ -14228,7 +14320,7 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "resolved": "https://npmjs.artifacts.twilio.com/artifactory/api/npm/virtual-npm-twilio/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" diff --git a/package.json b/package.json index 4827a3413..6dd5b20bd 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@oclif/plugin-help": "^5.1.3", "@oclif/plugin-plugins": "2.1.0", "@octokit/rest": "^21.1.1", - "axios": "^1.12.0", + "axios": "^1.19.0", "chalk": "^4.1.2", "columnify": "^1.5.4", "fs-extra": "^9.0.1", @@ -42,7 +42,7 @@ "qs": "^6.9.4", "semver": "^7.5.2", "tsv": "^0.2.0", - "twilio": "^5.3.0", + "twilio": "^6.0.0", "proxyquire": "^2.1.3" }, "devDependencies": { @@ -64,6 +64,6 @@ "tmp": "^0.2.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } } diff --git a/src/base-commands/base-command.js b/src/base-commands/base-command.js index ea789206a..f72e0b77b 100644 --- a/src/base-commands/base-command.js +++ b/src/base-commands/base-command.js @@ -139,9 +139,13 @@ class BaseCommand extends Command { return; } - const limitedData = properties ? this.getLimitedData(dataArray, properties) : null; + // Wrap primitive arrays (e.g. ["id1", "id2"]) into objects for columnar display. + const hasPrimitives = dataArray.length > 0 && typeof dataArray[0] !== 'object'; + const displayArray = hasPrimitives ? dataArray.map((item) => ({ value: item })) : dataArray; - process.stdout.write(`${this.outputProcessor(dataArray, limitedData || dataArray, options)}\n`); + const limitedData = properties ? this.getLimitedData(displayArray, properties) : null; + + process.stdout.write(`${this.outputProcessor(dataArray, limitedData || displayArray, options)}\n`); } getLimitedData(dataArray, properties) { @@ -172,6 +176,10 @@ class BaseCommand extends Command { }); if (invalidPropertyNames.size > 0) { + if (invalidPropertyNames.size === propNames.length) { + // All requested properties are invalid — display all columns instead. + return null; + } const warn = this.logger.warn.bind(this.logger); invalidPropertyNames.forEach((p) => { warn(`"${p}" is not a valid property name.`); diff --git a/src/services/cli-http-client.js b/src/services/cli-http-client.js index d3c0a9ec4..16df3ebed 100644 --- a/src/services/cli-http-client.js +++ b/src/services/cli-http-client.js @@ -44,16 +44,7 @@ class CliRequestClient { * @param {boolean} [opts.allowRedirects] - Should the client follow redirects * @param {boolean} [opts.forever] - Set to true to use the forever-agent */ - async request(opts) { - opts = opts || {}; - if (!opts.method) { - throw new Error('http method is required'); - } - - if (!opts.uri) { - throw new Error('uri is required'); - } - + buildHeaders(opts) { const headers = opts.headers || {}; if (!headers.Connection && !headers.connection) { @@ -64,15 +55,15 @@ class CliRequestClient { const b64Auth = Buffer.from(`${opts.username}:${opts.password}`).toString('base64'); headers.Authorization = `Basic ${b64Auth}`; } - // User-Agent will have these info : / ( ) - const componentInfo = []; - componentInfo.push(`(${os.platform()} ${os.arch()})`); // ( ) - const userAgentArr = (headers['User-Agent'] || ' ').split(' '); // contains twilio-node/version (darwin x64) node/v16.4.2 - componentInfo.push(userAgentArr[0]); // Api client version - componentInfo.push(userAgentArr[3]); // nodejs version - componentInfo.push(this.commandName); // cli-command + + const userAgentArr = (headers['User-Agent'] || ' ').split(' '); + const componentInfo = [`(${os.platform()} ${os.arch()})`, userAgentArr[0], userAgentArr[3], this.commandName]; headers['User-Agent'] = `${this.pluginName} ${pkg.name}/${pkg.version} ${componentInfo.filter(Boolean).join(' ')}`; + return headers; + } + + buildOptions(opts, headers) { const options = { timeout: opts.timeout || 30000, maxRedirects: opts.allowRedirects ? 10 : 0, @@ -87,7 +78,10 @@ class CliRequestClient { }; if (opts.data) { - options.data = qs.stringify(opts.data, { arrayFormat: 'repeat' }); + const contentType = headers['Content-Type'] || headers['content-type'] || ''; + options.data = contentType.includes('application/json') + ? JSON.stringify(opts.data) + : qs.stringify(opts.data, { arrayFormat: 'repeat' }); } if (opts.params) { @@ -97,6 +91,22 @@ class CliRequestClient { }; } + return options; + } + + async request(opts) { + opts = opts || {}; + if (!opts.method) { + throw new Error('http method is required'); + } + + if (!opts.uri) { + throw new Error('uri is required'); + } + + const headers = this.buildHeaders(opts); + const options = this.buildOptions(opts, headers); + this.lastRequest = options; this.logRequest(options); @@ -153,11 +163,30 @@ class CliRequestClient { /* eslint-disable camelcase */ // In the rare event parameters are missing, display a readable message - formatErrorMessage({ code, message, more_info, details }) { - const moreInfoMessage = more_info ? `See ${more_info} for more info.` : ''; - let errorMessage = `Error code ${code || 'N/A'} from Twilio: ${ - message || 'No message provided' - }. ${moreInfoMessage}`; + formatErrorMessage({ code, message, more_info, details, httpStatusCode, userError, params }) { + let errorMessage; + + if (httpStatusCode === undefined) { + const moreInfoMessage = more_info ? `See ${more_info} for more info.` : ''; + errorMessage = `Error code ${code || 'N/A'} from Twilio: ${message || 'No message provided'}. ${moreInfoMessage}`; + } else { + errorMessage = `Error code ${code || 'N/A'} from Twilio: ${message || 'No message provided'}.`; + errorMessage += ` HTTP ${httpStatusCode}.`; + + if (userError !== undefined) { + errorMessage += userError ? ' This is a user error.' : ' This is a system error.'; + } + + if (params) { + const paramEntries = Object.entries(params); + if (paramEntries.length > 0) { + errorMessage += '\n\nAdditional details:'; + paramEntries.forEach(([key, value]) => { + errorMessage += `\n ${key}: ${value}`; + }); + } + } + } // Add hint for regional authentication failures if ((code === 20003 || code === '20003') && message && message.toLowerCase().includes('authenticate')) { diff --git a/src/services/open-api-client.js b/src/services/open-api-client.js index af13edc12..b7a3061ed 100644 --- a/src/services/open-api-client.js +++ b/src/services/open-api-client.js @@ -26,13 +26,25 @@ class OpenApiClient { throw new Error(`Path not found: ${opts.domain}.${opts.path}`); } - const operation = path.operations[opts.method]; + /* + * api-browser.js stores PUT as 'update' and PATCH as 'patch' in path.operations. + * GET, POST, DELETE keep their HTTP method name as the key. + */ + const METHOD_TO_OPERATION_KEY = { put: 'update', patch: 'patch' }; + const operationKey = METHOD_TO_OPERATION_KEY[opts.method] || opts.method; + const operation = path.operations[operationKey]; if (!operation) { throw new Error(`Operation not found: ${opts.domain}.${opts.path}.${opts.method}`); } - const isPost = opts.method.toLowerCase() === 'post'; + const requestBodyContent = (operation.requestBody || {}).content || {}; + if ('application/json' in requestBodyContent) { + opts.headers = opts.headers || {}; + opts.headers['Content-Type'] = 'application/json'; + } + + const isBodyMethod = ['post', 'put', 'patch'].includes(opts.method.toLowerCase()); const params = this.getParams(opts, operation); if (!opts.uri) { @@ -51,8 +63,8 @@ class OpenApiClient { uri.hostname = this.getHost(uri.hostname, opts); opts.uri = uri.href; - opts.params = isPost ? null : params; - opts.data = isPost ? params : null; + opts.params = isBodyMethod ? null : params; + opts.data = isBodyMethod ? params : null; const response = await this.httpClient.request(opts); @@ -69,12 +81,45 @@ class OpenApiClient { if (parameter.in === 'query' && doesObjectHaveProperty(opts.data, parameter.name)) { let value = opts.data[parameter.name]; if (parameter.schema.type === 'boolean') { - value = value.toString(); + value = value === 'true' || value === true; + } else if (parameter.schema.type === 'object' && typeof value === 'string') { + try { + value = JSON.parse(value); + } catch (e) { + logger.debug(`Could not parse value for "${parameter.name}" as JSON: ${e.message}`); + } + } else if (parameter.schema.type === 'array') { + // oclif multiple:true gives an array of strings; parse each element that looks like JSON + if (typeof value === 'string') { + try { + value = JSON.parse(value); + } catch (e) { + logger.debug(`Could not parse value for "${parameter.name}" as JSON: ${e.message}`); + } + } else if (Array.isArray(value)) { + value = value.map((item) => { + if (typeof item !== 'string') return item; + try { + return JSON.parse(item); + } catch (e) { + return item; + } + }); + // Unwrap when a single --flag '[...]' produces [[...]] (parsed JSON array inside oclif array) + if (value.length === 1 && Array.isArray(value[0])) { + value = value[0]; + } + } } params[parameter.name] = value; } }); + // Pass through pagination token if present. + if (doesObjectHaveProperty(opts.data, 'pageToken')) { + params.pageToken = opts.data.pageToken; + } + return params; } @@ -117,6 +162,7 @@ class OpenApiClient { parseResponse(domain, operation, response, requestOpts) { if (response.body) { + response.rawBody = response.body; const responseSchema = this.getResponseSchema(domain, operation, response.statusCode, requestOpts.headers.Accept); // If we were able to find the schema for the response body, convert it. diff --git a/src/services/twilio-api/api-browser.js b/src/services/twilio-api/api-browser.js index 009da165d..9cb3c0c8a 100644 --- a/src/services/twilio-api/api-browser.js +++ b/src/services/twilio-api/api-browser.js @@ -4,7 +4,17 @@ const { camelCase } = require('../naming-conventions'); let apiSpec; // Lazy-loaded below. -const OPERATIONS = ['post', 'get', 'delete']; +const OPERATIONS = ['post', 'get', 'delete', 'put', 'patch']; + +/* + * Maps PUT and PATCH HTTP methods to the operation key used in path.operations. + * get/post/delete keep their HTTP method name as the key so the existing + * open-api-client.js lookup (path.operations[opts.method]) continues to work. + */ +const HTTP_METHOD_TO_OPERATION_KEY = { + put: 'update', + patch: 'patch', +}; class TwilioApiBrowser { constructor(spec) { @@ -23,6 +33,8 @@ class TwilioApiBrowser { const existing = mergedSpec[domainName]; const current = spec[domainNameWithVersion]; Object.assign(existing.components.schemas, current.components.schemas); + existing.components.parameters = existing.components.parameters || {}; + Object.assign(existing.components.parameters, current.components.parameters || {}); Object.assign(existing.paths, current.paths); mergedSpec[domainName] = existing; } else { @@ -70,35 +82,69 @@ class TwilioApiBrowser { const domains = JSON.parse(JSON.stringify(obj)); Object.values(domains).forEach((spec) => { + // Some new-style specs put servers at the top level, not per-path. + const specServer = spec.servers && spec.servers[0] && spec.servers[0].url; + Object.entries(spec.paths).forEach((entry) => { const key = entry[0]; const path = entry[1]; if (key === '/healthcheck') return; - // Naive assumption: The Twilio APIs only have a single server. - if (path.servers === undefined) path.server = spec.servers[0].url; - else path.server = path.servers[0].url; + /* + * Naive assumption: The Twilio APIs only have a single server. + * Fall back to spec-level server when path has no servers array. + */ + const pathServer = path.servers && path.servers[0] && path.servers[0].url; + path.server = pathServer || specServer; delete path.servers; path.operations = {}; - if (path.description === undefined) path.description = ''; - path.description = path.description.replace(/(\r\n|\n|\r)/gm, ' '); + path.description = (path.description || '').replace(/(\r\n|\n|\r)/gm, ' '); - // Move the operations into an operations object. + /* + * Resolve and hoist path-level parameters (shared across all operations on this path). + */ + const pathLevelParams = this.resolveParameterRefs(path.parameters || [], spec); + delete path.parameters; + + /* + * Move the operations into an operations object. + * PUT and PATCH are stored under action-name keys ('update', 'patch'). + * GET, POST, DELETE keep their HTTP method name as the key. + */ OPERATIONS.forEach((operationName) => { if (operationName in path) { const operation = path[operationName]; this.updateTwilioVendorExtensionProperty(operation); - path.operations[operationName] = operation; + const operationKey = HTTP_METHOD_TO_OPERATION_KEY[operationName] || operationName; + operation.httpMethod = operationName; + path.operations[operationKey] = operation; delete path[operationName]; + /* + * Resolve any $ref entries in the operation-level parameters array, + * then merge with path-level parameters (operation-level takes precedence). + */ + const resolvedOpParams = this.resolveParameterRefs(operation.parameters || [], spec); + const mergedParams = [...pathLevelParams]; + resolvedOpParams.forEach((p) => { + if (!mergedParams.find((existing) => existing.name === p.name)) { + mergedParams.push(p); + } + }); + if (mergedParams.length > 0) { + operation.parameters = mergedParams; + } + /* * Convert all the request body properties to query parameters for * simpler parsing downstream. */ - const parameters = this.requestPropertiesToParameters(operation.requestBody); + const parameters = this.requestPropertiesToParameters(operation.requestBody, spec); if (parameters.length > 0) { - operation.parameters = operation.parameters ? operation.parameters.concat(parameters) : parameters; + const existingParams = operation.parameters || []; + const newParams = parameters.filter((p) => !existingParams.find((e) => e.name === p.name)); + operation.parameters = existingParams.concat(newParams); } } }); @@ -111,28 +157,110 @@ class TwilioApiBrowser { return domains; } - requestPropertiesToParameters(requestBody) { + requestPropertiesToParameters(requestBody, spec) { const parameters = []; const content = (requestBody || {}).content || {}; - Object.values(content).forEach((type) => { - const typeSchema = type.schema || {}; - const properties = typeSchema.properties || {}; - const required = typeSchema.required || []; - - Object.entries(properties).forEach(([name, schema]) => { - parameters.push({ - name, - schema, - in: 'query', - required: required.includes(name), - description: schema.description, - }); + // Prefer application/json when present; fall back to the first available type. + const contentType = content['application/json'] ? 'application/json' : Object.keys(content)[0]; + const type = content[contentType]; + if (!type) return parameters; + + const { properties, required } = this.flattenSchema(type.schema || {}, spec); + Object.entries(properties).forEach(([name, schema]) => { + parameters.push({ + name, + schema, + in: 'query', + required: required.includes(name), + description: schema.description, }); }); return parameters; } + + /* + * Resolve a schema to a flat { properties, required } object, handling: + * - $ref → look up in components.schemas, then recurse + * - allOf → merge all branch properties; a field is required if + * any branch requires it + * - oneOf / anyOf → union all branch properties; a field is required only + * if every branch requires it (true discriminator keys + * like `inputSource` satisfy this) + * - plain object → return its properties directly + * + * A visited set prevents infinite loops on circular $ref chains. + */ + flattenSchema(schema, spec, visited) { + visited = visited || new Set(); + + if (!schema || typeof schema !== 'object') { + return { properties: {}, required: [] }; + } + + if (schema.$ref) { + if (visited.has(schema.$ref)) { + return { properties: {}, required: [] }; + } + visited.add(schema.$ref); + const refName = schema.$ref.split('/').pop(); + const resolved = ((spec.components || {}).schemas || {})[refName]; + if (!resolved) return { properties: {}, required: [] }; + return this.flattenSchema(resolved, spec, visited); + } + + if (schema.allOf) { + const merged = { properties: {}, required: [] }; + schema.allOf.forEach((branch) => { + const { properties, required } = this.flattenSchema(branch, spec, new Set(visited)); + Object.assign(merged.properties, properties); + required.forEach((r) => { + if (!merged.required.includes(r)) merged.required.push(r); + }); + }); + if (schema.properties) Object.assign(merged.properties, schema.properties); + if (schema.required) { + schema.required.forEach((r) => { + if (!merged.required.includes(r)) merged.required.push(r); + }); + } + return merged; + } + + if (schema.oneOf || schema.anyOf) { + const branches = (schema.oneOf || schema.anyOf).map((branch) => + this.flattenSchema(branch, spec, new Set(visited)), + ); + const allProperties = {}; + branches.forEach(({ properties }) => Object.assign(allProperties, properties)); + // Mark required only when every branch requires the field + const requiredInAll = + branches.length > 0 + ? Object.keys(allProperties).filter((name) => branches.every(({ required }) => required.includes(name))) + : []; + return { properties: allProperties, required: requiredInAll }; + } + + return { properties: schema.properties || {}, required: schema.required || [] }; + } + + resolveSchemaRef(schema, spec) { + if (!schema || !schema.$ref || !spec) return schema; + const refName = schema.$ref.split('/').pop(); + return ((spec.components || {}).schemas || {})[refName] || schema; + } + + resolveParameterRefs(parameters, spec) { + const componentParameters = (spec.components || {}).parameters || {}; + return parameters.map((param) => { + if (param.$ref) { + const refName = param.$ref.split('/').pop(); + return componentParameters[refName] || param; + } + return param; + }); + } } module.exports = TwilioApiBrowser; diff --git a/src/services/twilio-api/twilio-client.js b/src/services/twilio-api/twilio-client.js index 0be4898a5..7f98a40f3 100644 --- a/src/services/twilio-api/twilio-client.js +++ b/src/services/twilio-api/twilio-client.js @@ -59,7 +59,18 @@ class TwilioApiClient { } async update(opts) { - opts.method = 'post'; + const domainPaths = (this.apiClient.apiBrowser.domains[opts.domain] || {}).paths || {}; + const operations = (domainPaths[opts.path] || {}).operations || {}; + // Sierra APIs use PUT for update; legacy Twilio APIs use POST (no PUT operation). + opts.method = operations.update ? 'put' : 'post'; + + const { body } = await this.request(opts); + + return body; + } + + async patch(opts) { + opts.method = 'patch'; const { body } = await this.request(opts); @@ -69,9 +80,13 @@ class TwilioApiClient { async remove(opts) { opts.method = 'delete'; - const { statusCode } = await this.request(opts); + const { statusCode, body } = await this.request(opts); - return statusCode === 204; + /* + * APIs that return a body (e.g. 202 Accepted) surface it for the caller. + * Existing no-body APIs (204 No Content) continue to return true/false. + */ + return body || statusCode === 204; } async list(opts) { @@ -83,7 +98,7 @@ class TwilioApiClient { opts.method = 'get'; // eslint-disable-next-line no-await-in-loop - const { body } = await this.request(opts); + const { body, rawBody } = await this.request(opts); const pageItems = this.getResponseItems(body); // Append all the items from the next page. @@ -94,14 +109,38 @@ class TwilioApiClient { return items.slice(0, limit); } - // If there's another page of results, "Let's Get It". - const nextPageUri = (body.meta && body.meta.nextPageUrl) || body.nextPageUri; + const nextPage = this.getNextPageInfo(rawBody || body, opts); - if (!nextPageUri) { + if (!nextPage) { break; } - opts = { + opts = nextPage; + } + + return items; + } + + getNextPageInfo(body, opts) { + // Token-based pagination: meta.nextToken at top level or nested under meta.pagination. + const meta = body.meta || {}; + const nextToken = meta.nextToken || (meta.pagination && meta.pagination.nextToken); + + if (nextToken) { + return { + domain: opts.domain, + host: opts.host, + path: opts.path, + pathParams: opts.pathParams, + data: { ...(opts.data || {}), pageToken: nextToken }, + }; + } + + // URL-based pagination: meta.next_page_url / meta.nextPageUrl or nextPageUri. + const nextPageUri = meta.next_page_url || meta.nextPageUrl || body.next_page_uri || body.nextPageUri; + + if (nextPageUri) { + return { domain: opts.domain, host: opts.host, path: opts.path, @@ -109,7 +148,7 @@ class TwilioApiClient { }; } - return items; + return null; } getLimit(options) { @@ -131,7 +170,15 @@ class TwilioApiClient { } getResponseItems(responseBody) { - // Find any properties that are arrays. We expect this to be exactly 1. + // Use meta.key to locate the items array when available. + const meta = responseBody.meta || {}; + const key = meta.key || (meta.pagination && meta.pagination.key); + + if (key && Array.isArray(responseBody[key])) { + return responseBody[key]; + } + + // Fallback: find any properties that are arrays. We expect this to be exactly 1. const arrayProps = Object.values(responseBody).filter(Array.isArray); if (arrayProps.length === 1) { diff --git a/test/services/cli-http-client.test.js b/test/services/cli-http-client.test.js index 31e34bab4..ba6ce155d 100644 --- a/test/services/cli-http-client.test.js +++ b/test/services/cli-http-client.test.js @@ -119,5 +119,62 @@ describe('services', () => { }); expect(response.statusCode).to.equal(200); }); + + test.it('formats legacy error response (more_info schema)', () => { + const client = new CliRequestClient('blah', logger); + const result = client.formatErrorMessage({ + code: 20404, + message: 'Resource not found', + more_info: 'https://www.twilio.com/docs/errors/20404', // eslint-disable-line camelcase + details: { foo: 'bar' }, + }); + expect(result.message).to.contain('Error code 20404 from Twilio: Resource not found.'); + expect(result.message).to.contain('See https://www.twilio.com/docs/errors/20404 for more info.'); + expect(result.code).to.equal(20404); + expect(result.details).to.deep.equal({ foo: 'bar' }); + }); + + test.it('formats new error response (httpStatusCode schema)', () => { + const client = new CliRequestClient('blah', logger); + const result = client.formatErrorMessage({ + code: 50001, + message: 'Invalid store ID', + httpStatusCode: 400, + userError: true, + params: { twilioErrorCodeUrl: 'https://www.twilio.com/docs/errors/50001' }, + }); + expect(result.message).to.contain('Error code 50001 from Twilio: Invalid store ID.'); + expect(result.message).to.contain('HTTP 400.'); + expect(result.message).to.contain('This is a user error.'); + expect(result.message).to.contain('twilioErrorCodeUrl: https://www.twilio.com/docs/errors/50001'); + expect(result.code).to.equal(50001); + }); + + test.it('formats new error response with system error', () => { + const client = new CliRequestClient('blah', logger); + const result = client.formatErrorMessage({ + code: 60001, + message: 'Internal failure', + httpStatusCode: 500, + userError: false, + }); + expect(result.message).to.contain('Error code 60001 from Twilio: Internal failure.'); + expect(result.message).to.contain('HTTP 500.'); + expect(result.message).to.contain('This is a system error.'); + }); + + test.it('formats new error response without optional fields', () => { + const client = new CliRequestClient('blah', logger); + const result = client.formatErrorMessage({ + code: 40001, + message: 'Bad request', + httpStatusCode: 400, + }); + expect(result.message).to.contain('Error code 40001 from Twilio: Bad request.'); + expect(result.message).to.contain('HTTP 400.'); + expect(result.message).not.to.contain('user error'); + expect(result.message).not.to.contain('system error'); + expect(result.message).not.to.contain('Additional details'); + }); }); }); diff --git a/test/services/twilio-api/api-browser.test.js b/test/services/twilio-api/api-browser.test.js index 0b8321637..4c05ff021 100644 --- a/test/services/twilio-api/api-browser.test.js +++ b/test/services/twilio-api/api-browser.test.js @@ -137,8 +137,8 @@ describe('services', () => { }, '/v2/Gadgets.json': { operations: { - post: { createStuff: '' }, - get: { listStuff: '' }, + post: { createStuff: '', httpMethod: 'post' }, + get: { listStuff: '', httpMethod: 'get' }, }, server: 'https://neato.twilio.com', description: 'v2 list Gadgets here', @@ -146,9 +146,9 @@ describe('services', () => { }, '/v2/Gadgets/{Sid}.json': { operations: { - get: { fetchStuff: '' }, - post: { updateStuff: '' }, - delete: { removeStuff: '' }, + get: { fetchStuff: '', httpMethod: 'get' }, + post: { updateStuff: '', httpMethod: 'post' }, + delete: { removeStuff: '', httpMethod: 'delete' }, }, server: 'https://neato.twilio.com', description: 'v2 instance Gadgets here', @@ -188,8 +188,8 @@ describe('services', () => { paths: { '/v2/Services/{ServiceSid}/Entities/{Identity}/Factors.json': { operations: { - post: { createStuff: '', defaultOutputProperties: ['sid', 'status', 'binding'] }, - get: { listStuff: '' }, + post: { createStuff: '', defaultOutputProperties: ['sid', 'status', 'binding'], httpMethod: 'post' }, + get: { listStuff: '', httpMethod: 'get' }, }, server: 'https://api.twilio.com', description: '', diff --git a/test/services/twilio-api/twilio-client.test.js b/test/services/twilio-api/twilio-client.test.js index efb14400e..176639267 100644 --- a/test/services/twilio-api/twilio-client.test.js +++ b/test/services/twilio-api/twilio-client.test.js @@ -180,6 +180,206 @@ describe('services', () => { expect(response).to.be.true; }); + test + .nock('https://api.twilio.com', (api) => { + api.delete(`/2010-04-01/Accounts/${accountSid}/Calls/${callSid}.json`).reply(202, { + message: 'Resource scheduled for deletion', + status: 'accepted', + }); + }) + .it('can remove resources with 202 response body', async () => { + const response = await apiClient.remove({ + domain: 'api', + path: '/2010-04-01/Accounts/{AccountSid}/Calls/{Sid}.json', + pathParams: { Sid: callSid }, + }); + + expect(response).to.eql({ message: 'Resource scheduled for deletion', status: 'accepted' }); + }); + + test + .nock('https://conversations.twilio.com', (api) => { + api.get('/v1/Conversations?PageSize=50').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + nextToken: 'token-page-2', + }, + }); + api.get('/v1/Conversations?PageSize=50&pageToken=token-page-2').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + }, + }); + }) + .it('can list resources with token-based pagination', async () => { + const response = await apiClient.list({ + domain: 'conversations', + path: '/v1/Conversations', + data: { PageSize: 50 }, + }); + + expect(response).to.eql([{ sid: callSid }, { sid: callSid }]); + }); + + test + .nock('https://conversations.twilio.com', (api) => { + api.get('/v1/Conversations?PageSize=50').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + nextToken: 'token-2', + }, + }); + api.get('/v1/Conversations?PageSize=50&pageToken=token-2').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + }, + }); + }) + .it('uses meta.key to locate items array', async () => { + const response = await apiClient.list({ + domain: 'conversations', + path: '/v1/Conversations', + data: { PageSize: 50 }, + }); + + expect(response).to.eql([{ sid: callSid }, { sid: callSid }]); + }); + + test + .nock('https://conversations.twilio.com', (api) => { + api.get('/v1/Conversations?PageSize=50').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + pagination: { + nextToken: 'nested-token-2', + }, + }, + }); + api.get('/v1/Conversations?PageSize=50&pageToken=nested-token-2').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + }, + }); + }) + .it('supports nested meta.pagination.nextToken', async () => { + const response = await apiClient.list({ + domain: 'conversations', + path: '/v1/Conversations', + data: { PageSize: 50 }, + }); + + expect(response).to.eql([{ sid: callSid }, { sid: callSid }]); + }); + + test + .nock('https://studio.twilio.com', (api) => { + api.get('/v1/Flows?PageSize=50').reply(200, { + flows: [{ friendly_name: 'Flow A' }, { friendly_name: 'Flow B' }], // eslint-disable-line camelcase + }); + }) + .it('can list resources without pagination metadata', async () => { + const response = await apiClient.list({ + domain: 'studio', + path: '/v1/Flows', + data: { PageSize: 50 }, + }); + + expect(response).to.eql([{ friendlyName: 'Flow A' }, { friendlyName: 'Flow B' }]); + }); + + test + .nock('https://api.twilio.com', (api) => { + api.get(`/2010-04-01/Accounts/${accountSid}/Calls.json?PageSize=2`).reply(200, { + calls: [{ sid: callSid }], + meta: { + key: 'calls', + pageSize: 1, + nextToken: 'page2-token', + }, + }); + api.get(`/2010-04-01/Accounts/${accountSid}/Calls.json?PageSize=2&pageToken=page2-token`).reply(200, { + calls: [{ sid: callSid }], + meta: { + key: 'calls', + pageSize: 1, + }, + }); + }) + .it('preserves pathParams across token-based paginated requests', async () => { + const response = await apiClient.list({ + domain: 'api', + path: '/2010-04-01/Accounts/{AccountSid}/Calls.json', + data: { PageSize: 2 }, + }); + + expect(response).to.eql([{ sid: callSid }, { sid: callSid }]); + }); + + test.it('getResponseItems uses meta.key to locate items in response', () => { + const body = { + profiles: ['profile_001', 'profile_002'], + meta: { key: 'profiles', pageSize: 2 }, + }; + const items = apiClient.getResponseItems(body); + expect(items).to.eql(['profile_001', 'profile_002']); + }); + + test.it('getResponseItems falls back to finding single array when meta.key is absent', () => { + const body = { + identifiers: [{ idType: 'email', values: ['test@example.com'] }], + }; + const items = apiClient.getResponseItems(body); + expect(items).to.eql([{ idType: 'email', values: ['test@example.com'] }]); + }); + + test + .nock('https://conversations.twilio.com', (api) => { + api.get('/v1/Conversations?PageSize=50').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + nextToken: 'token-abc', + }, + }); + api.get('/v1/Conversations?PageSize=50&pageToken=token-abc').reply(200, { + conversations: [{ sid: callSid }], + meta: { + key: 'conversations', + pageSize: 1, + nextToken: 'token-def', + }, + }); + api.get('/v1/Conversations?PageSize=50&pageToken=token-def').reply(200, { + conversations: [], + meta: { + key: 'conversations', + pageSize: 0, + }, + }); + }) + .it('stops pagination when page returns empty results', async () => { + const response = await apiClient.list({ + domain: 'conversations', + path: '/v1/Conversations', + data: { PageSize: 50 }, + }); + + expect(response).to.eql([{ sid: callSid }, { sid: callSid }]); + }); + test .nock('https://api.dev.twilio.com', (api) => { api.post(`/2010-04-01/Accounts/${accountSid}/Messages.json`).reply(201, {