├── .DS_Store ├── .codeclimate.yml ├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── functional-tests.yml │ ├── linting.yml │ ├── release.yml │ ├── security.yml │ ├── sync-labels.yml │ ├── unit-tests.yml │ └── update-license.yml ├── .gitignore ├── .gitleaks.toml ├── .gitleaksignore ├── .markdownlint.yml ├── .markdownlintignore ├── .pre-commit-config.yaml ├── .tool-versions ├── .yamllint.yml ├── .yarn ├── plugins │ └── @yarnpkg │ │ └── plugin-outdated.cjs └── releases │ └── yarn-4.9.1.cjs ├── .yarnrc.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── action.yml ├── biome.json ├── dist └── index.js ├── example.mp3 ├── jest.config.json ├── package.json ├── src ├── TwilioClient.ts ├── __tests__ │ ├── TwilioClient.spec.ts │ └── index.spec.ts └── index.ts ├── tsconfig.json └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabasoad/twilio-voice-call-action/db5efee4f908c4513b4cf1a33218a74de0cca2aa/.DS_Store -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "2" 3 | checks: 4 | method-complexity: 5 | config: 6 | threshold: 6 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/** linguist-generated=true 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @fabasoad 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom: 3 | [ 4 | "https://www.bitcoinqrcodemaker.com/?style=bitcoin&address=145HwyQAcv4vrzUumJhu7nWGAVBysX9jJH&prefix=on", 5 | "https://paypal.me/fabasoad", 6 | ] 7 | github: ["fabasoad"] 8 | ko_fi: fabasoad 9 | liberapay: fabasoad 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: fabasoad 7 | 8 | --- 9 | 10 | #### Describe the bug 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | #### Steps to Reproduce 15 | 16 | 1. Run '...' 17 | 2. See error 18 | 19 | #### Expected behavior 20 | 21 | A clear and concise description of what you expected to happen. 22 | 23 | #### Actual behavior 24 | 25 | A clear and concise description of what is happening now. 26 | 27 | #### Screenshots 28 | 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | #### Technical information (please complete the following information) 32 | 33 | - OS: [e.g. Windows 10 Enterprise v.1909 (OS Build 18363.720)] 34 | - `twilio-voice-call-action` version [e.g. 1.1.1] 35 | 36 | #### Additional context 37 | 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: fabasoad 7 | 8 | --- 9 | 10 | #### Is your feature request related to a problem? Please describe 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated 13 | when [...] 14 | 15 | #### Describe the solution you'd like 16 | 17 | A clear and concise description of what you want to happen. 18 | 19 | #### Describe alternatives you've considered 20 | 21 | A clear and concise description of any alternative solutions or features you've 22 | considered. 23 | 24 | #### Additional context 25 | 26 | Add any other context or screenshots about the feature request here. 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | reviewers: 9 | - "fabasoad" 10 | labels: 11 | - "dependencies" 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Pull request checklist 4 | 5 | Please check if your PR fulfills the following requirements: 6 | 7 | - [ ] I have read the [CONTRIBUTING](https://github.com/fabasoad/twilio-voice-call-action/CONTRIBUTING.md) 8 | doc. 9 | - [ ] Tests for the changes have been added (for bug fixes / features). 10 | - [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features). 11 | - [ ] Build (`yarn run build`) was run locally and any changes were pushed. 12 | - [ ] Tests (`yarn test`) has passed locally and any fixes were made for failures. 13 | 14 | ## Pull request type 15 | 16 | 17 | 18 | 20 | 21 | Please check the type of change your PR introduces: 22 | 23 | - [ ] Bugfix 24 | - [ ] Feature 25 | - [ ] Code style update (formatting, renaming) 26 | - [ ] Refactoring (no functional changes, no api changes) 27 | - [ ] Build related changes 28 | - [ ] Documentation content changes 29 | - [ ] Other (please describe): 30 | 31 | ## What is the current behavior 32 | 34 | 35 | ## What is the new behavior 36 | 37 | 38 | - 39 | - 40 | - 41 | 42 | ## Does this introduce a breaking change 43 | 44 | - [ ] Yes 45 | - [ ] No 46 | 47 | 49 | 50 | ## Other information 51 | 52 | 54 | 56 | 57 | --- 58 | 59 | Closes #{IssueNumber} 60 | -------------------------------------------------------------------------------- /.github/workflows/functional-tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Functional Tests 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | inputs: 7 | text: 8 | description: "Text for a call" 9 | required: false 10 | default: "Call from functional tests" 11 | type: string 12 | 13 | jobs: 14 | twilio-voice-call: 15 | name: Twilio voice call 16 | timeout-minutes: 5 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout ${{ github.repository }} 20 | uses: actions/checkout@v4 21 | - name: Make a call 22 | uses: ./ 23 | with: 24 | twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} 25 | twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} 26 | text: ${{ inputs.text }} 27 | from: ${{ secrets.TWILIO_NUMBER_FROM }} 28 | to: ${{ secrets.TWILIO_NUMBER_TO }} 29 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Linting 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: {} 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | js-lint: 12 | name: JS Lint 13 | uses: fabasoad/reusable-workflows/.github/workflows/wf-js-lint.yml@main 14 | pre-commit: 15 | name: Pre-commit 16 | uses: fabasoad/reusable-workflows/.github/workflows/wf-pre-commit.yml@main 17 | with: 18 | skip-hooks: "audit, build, lint, test" 19 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | tags: 7 | - "v*.*.*" 8 | 9 | jobs: 10 | github: 11 | name: GitHub 12 | uses: fabasoad/reusable-workflows/.github/workflows/wf-github-release.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: {} 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | sast: 12 | name: SAST 13 | permissions: 14 | contents: read 15 | security-events: write 16 | uses: fabasoad/reusable-workflows/.github/workflows/wf-security-sast.yml@main 17 | with: 18 | code-scanning: true 19 | sca: true 20 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Labels 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: {} 9 | 10 | jobs: 11 | maintenance: 12 | name: Maintenance 13 | uses: fabasoad/reusable-workflows/.github/workflows/wf-sync-labels.yml@main 14 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Unit Tests 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | paths: 7 | - .github/workflows/unit-tests.yml 8 | - .tool-versions 9 | - jest.config.json 10 | - package.json 11 | - src/** 12 | - tsconfig.json 13 | push: 14 | branches: 15 | - main 16 | workflow_dispatch: {} 17 | 18 | jobs: 19 | jest: 20 | name: Jest 21 | uses: fabasoad/reusable-workflows/.github/workflows/wf-js-unit-tests.yml@main 22 | secrets: inherit # pragma: allowlist secret 23 | -------------------------------------------------------------------------------- /.github/workflows/update-license.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: License 3 | 4 | on: # yamllint disable-line rule:truthy 5 | schedule: 6 | # Every January 1st at 14:00 JST 7 | - cron: "0 5 1 1 *" 8 | 9 | jobs: 10 | maintenance: 11 | name: Maintenance 12 | uses: fabasoad/reusable-workflows/.github/workflows/wf-update-license.yml@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | 3 | # yarn 4 | yarn-error.log 5 | .pnp.* 6 | .yarn/* 7 | !.yarn/patches 8 | !.yarn/plugins 9 | !.yarn/releases 10 | !.yarn/sdks 11 | !.yarn/versions 12 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [extend] 2 | useDefault = true 3 | 4 | [allowlist] 5 | paths = [ 6 | '''.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs''' 7 | ] 8 | -------------------------------------------------------------------------------- /.gitleaksignore: -------------------------------------------------------------------------------- 1 | .yarn/releases/yarn-4.9.1.cjs:aws-access-token:149 2 | .yarn/releases/yarn-4.9.1.cjs:generic-api-key:576 3 | dist/index.js:generic-api-key:1 4 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | default: true 3 | MD013: 4 | code_blocks: false 5 | tables: false 6 | MD041: false 7 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_install_hook_types: ["pre-commit", "pre-push"] 3 | default_stages: ["pre-commit", "pre-push"] 4 | exclude: ^(dist/.*\.js|\.yarn/.*)$ 5 | minimum_pre_commit_version: 4.0.0 6 | repos: 7 | - repo: local 8 | hooks: 9 | - id: build 10 | name: Build 11 | entry: make build 12 | language: system 13 | pass_filenames: false 14 | verbose: true 15 | stages: ["pre-push"] 16 | - id: lint 17 | name: Lint 18 | entry: make lint 19 | language: system 20 | pass_filenames: false 21 | verbose: false 22 | stages: ["pre-push"] 23 | - id: test 24 | name: Unit tests 25 | entry: make test 26 | language: system 27 | pass_filenames: false 28 | verbose: true 29 | stages: ["pre-push"] 30 | # Security 31 | - id: audit 32 | name: Yarn audit 33 | entry: make audit 34 | language: system 35 | pass_filenames: false 36 | verbose: false 37 | stages: ["pre-push"] 38 | - repo: https://github.com/Yelp/detect-secrets 39 | rev: v1.5.0 40 | hooks: 41 | - id: detect-secrets 42 | - repo: https://github.com/gitleaks/gitleaks 43 | rev: v8.27.2 44 | hooks: 45 | - id: gitleaks 46 | - repo: https://github.com/fabasoad/pre-commit-snyk 47 | rev: v1.0.2 48 | hooks: 49 | - id: snyk-test 50 | args: 51 | - --snyk-args=--all-projects --severity-threshold=low 52 | - --hook-args=--log-level debug 53 | stages: ["pre-push"] 54 | - repo: https://github.com/fabasoad/pre-commit-grype 55 | rev: v0.6.3 56 | hooks: 57 | - id: grype-dir 58 | args: 59 | - --grype-args=--by-cve --fail-on=low --exclude=**/node_modules 60 | - --hook-args=--log-level debug 61 | stages: ["pre-push"] 62 | - repo: https://github.com/google/osv-scanner 63 | rev: v2.0.2 64 | hooks: 65 | - id: osv-scanner 66 | args: 67 | - --lockfile=yarn.lock 68 | verbose: true 69 | stages: ["pre-push"] 70 | # Markdown 71 | - repo: https://github.com/igorshubovych/markdownlint-cli 72 | rev: v0.45.0 73 | hooks: 74 | - id: markdownlint-fix 75 | stages: ["pre-commit"] 76 | # Yaml 77 | - repo: https://github.com/adrienverge/yamllint 78 | rev: v1.37.1 79 | hooks: 80 | - id: yamllint 81 | stages: ["pre-push"] 82 | # GitHub Actions 83 | - repo: https://github.com/rhysd/actionlint 84 | rev: v1.7.7 85 | hooks: 86 | - id: actionlint 87 | args: ["-pyflakes="] 88 | stages: ["pre-commit"] 89 | # Other 90 | - repo: https://github.com/pre-commit/pre-commit-hooks 91 | rev: v5.0.0 92 | hooks: 93 | - id: check-merge-conflict 94 | - id: check-json 95 | stages: ["pre-push"] 96 | - id: check-toml 97 | stages: ["pre-push"] 98 | - id: detect-private-key 99 | - id: end-of-file-fixer 100 | - id: mixed-line-ending 101 | args: ["--fix=lf"] 102 | - id: no-commit-to-branch 103 | stages: ["pre-commit"] 104 | - id: trailing-whitespace 105 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 24.1.0 2 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | comments: 6 | min-spaces-from-content: 1 7 | line-length: 8 | max: 165 9 | level: error 10 | -------------------------------------------------------------------------------- /.yarn/plugins/@yarnpkg/plugin-outdated.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | //prettier-ignore 3 | module.exports = { 4 | name: "@yarnpkg/plugin-outdated", 5 | factory: function (require) { 6 | var plugin=(()=>{var Cr=Object.create,ge=Object.defineProperty,Er=Object.defineProperties,_r=Object.getOwnPropertyDescriptor,xr=Object.getOwnPropertyDescriptors,br=Object.getOwnPropertyNames,et=Object.getOwnPropertySymbols,Sr=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,vr=Object.prototype.propertyIsEnumerable;var rt=(e,t,r)=>t in e?ge(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,k=(e,t)=>{for(var r in t||(t={}))tt.call(t,r)&&rt(e,r,t[r]);if(et)for(var r of et(t))vr.call(t,r)&&rt(e,r,t[r]);return e},q=(e,t)=>Er(e,xr(t)),Hr=e=>ge(e,"__esModule",{value:!0});var W=e=>{if(typeof require!="undefined")return require(e);throw new Error('Dynamic require of "'+e+'" is not supported')};var U=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),wr=(e,t)=>{for(var r in t)ge(e,r,{get:t[r],enumerable:!0})},Tr=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of br(t))!tt.call(e,n)&&n!=="default"&&ge(e,n,{get:()=>t[n],enumerable:!(r=_r(t,n))||r.enumerable});return e},re=e=>Tr(Hr(ge(e!=null?Cr(Sr(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var ve=U(V=>{"use strict";V.isInteger=e=>typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1;V.find=(e,t)=>e.nodes.find(r=>r.type===t);V.exceedsLimit=(e,t,r=1,n)=>n===!1||!V.isInteger(e)||!V.isInteger(t)?!1:(Number(t)-Number(e))/Number(r)>=n;V.escapeNode=(e,t=0,r)=>{let n=e.nodes[t];!n||(r&&n.type===r||n.type==="open"||n.type==="close")&&n.escaped!==!0&&(n.value="\\"+n.value,n.escaped=!0)};V.encloseBrace=e=>e.type!=="brace"?!1:e.commas>>0+e.ranges>>0==0?(e.invalid=!0,!0):!1;V.isInvalidBrace=e=>e.type!=="brace"?!1:e.invalid===!0||e.dollar?!0:e.commas>>0+e.ranges>>0==0||e.open!==!0||e.close!==!0?(e.invalid=!0,!0):!1;V.isOpenOrClose=e=>e.type==="open"||e.type==="close"?!0:e.open===!0||e.close===!0;V.reduce=e=>e.reduce((t,r)=>(r.type==="text"&&t.push(r.value),r.type==="range"&&(r.type="text"),t),[]);V.flatten=(...e)=>{let t=[],r=n=>{for(let s=0;s{"use strict";var nt=ve();st.exports=(e,t={})=>{let r=(n,s={})=>{let a=t.escapeInvalid&&nt.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o="";if(n.value)return(a||i)&&nt.isOpenOrClose(n)?"\\"+n.value:n.value;if(n.value)return n.value;if(n.nodes)for(let f of n.nodes)o+=r(f);return o};return r(e)}});var it=U((es,at)=>{"use strict";at.exports=function(e){return typeof e=="number"?e-e==0:typeof e=="string"&&e.trim()!==""?Number.isFinite?Number.isFinite(+e):isFinite(+e):!1}});var gt=U((ts,dt)=>{"use strict";var ot=it(),ce=(e,t,r)=>{if(ot(e)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(t===void 0||e===t)return String(e);if(ot(t)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let n=k({relaxZeros:!0},r);typeof n.strictZeros=="boolean"&&(n.relaxZeros=n.strictZeros===!1);let s=String(n.relaxZeros),a=String(n.shorthand),i=String(n.capture),o=String(n.wrap),f=e+":"+t+"="+s+a+i+o;if(ce.cache.hasOwnProperty(f))return ce.cache[f].result;let y=Math.min(e,t),h=Math.max(e,t);if(Math.abs(y-h)===1){let A=e+"|"+t;return n.capture?`(${A})`:n.wrap===!1?A:`(?:${A})`}let R=ht(e)||ht(t),p={min:e,max:t,a:y,b:h},H=[],m=[];if(R&&(p.isPadded=R,p.maxLen=String(p.max).length),y<0){let A=h<0?Math.abs(h):1;m=ut(A,Math.abs(y),p,n),y=p.a=0}return h>=0&&(H=ut(y,h,p,n)),p.negatives=m,p.positives=H,p.result=$r(m,H,n),n.capture===!0?p.result=`(${p.result})`:n.wrap!==!1&&H.length+m.length>1&&(p.result=`(?:${p.result})`),ce.cache[f]=p,p.result};function $r(e,t,r){let n=Ne(e,t,"-",!1,r)||[],s=Ne(t,e,"",!1,r)||[],a=Ne(e,t,"-?",!0,r)||[];return n.concat(a).concat(s).join("|")}function Lr(e,t){let r=1,n=1,s=lt(e,r),a=new Set([t]);for(;e<=s&&s<=t;)a.add(s),r+=1,s=lt(e,r);for(s=pt(t+1,n)-1;e1&&o.count.pop(),o.count.push(h.count[0]),o.string=o.pattern+ft(o.count),i=y+1;continue}r.isPadded&&(R=Pr(y,r,n)),h.string=R+h.pattern+ft(h.count),a.push(h),i=y+1,o=h}return a}function Ne(e,t,r,n,s){let a=[];for(let i of e){let{string:o}=i;!n&&!ct(t,"string",o)&&a.push(r+o),n&&ct(t,"string",o)&&a.push(r+o)}return a}function kr(e,t){let r=[];for(let n=0;nt?1:t>e?-1:0}function ct(e,t,r){return e.some(n=>n[t]===r)}function lt(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function pt(e,t){return e-e%Math.pow(10,t)}function ft(e){let[t=0,r=""]=e;return r||t>1?`{${t+(r?","+r:"")}}`:""}function Ir(e,t,r){return`[${e}${t-e==1?"":"-"}${t}]`}function ht(e){return/^-?(0+)\d/.test(e)}function Pr(e,t,r){if(!t.isPadded)return e;let n=Math.abs(t.maxLen-String(e).length),s=r.relaxZeros!==!1;switch(n){case 0:return"";case 1:return s?"0?":"0";case 2:return s?"0{0,2}":"00";default:return s?`0{0,${n}}`:`0{${n}}`}}ce.cache={};ce.clearCache=()=>ce.cache={};dt.exports=ce});var De=U((rs,xt)=>{"use strict";var Dr=W("util"),yt=gt(),Rt=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Mr=e=>t=>e===!0?Number(t):String(t),Ie=e=>typeof e=="number"||typeof e=="string"&&e!=="",ye=e=>Number.isInteger(+e),Pe=e=>{let t=`${e}`,r=-1;if(t[0]==="-"&&(t=t.slice(1)),t==="0")return!1;for(;t[++r]==="0";);return r>0},Br=(e,t,r)=>typeof e=="string"||typeof t=="string"?!0:r.stringify===!0,Ur=(e,t,r)=>{if(t>0){let n=e[0]==="-"?"-":"";n&&(e=e.slice(1)),e=n+e.padStart(n?t-1:t,"0")}return r===!1?String(e):e},At=(e,t)=>{let r=e[0]==="-"?"-":"";for(r&&(e=e.slice(1),t--);e.length{e.negatives.sort((i,o)=>io?1:0),e.positives.sort((i,o)=>io?1:0);let r=t.capture?"":"?:",n="",s="",a;return e.positives.length&&(n=e.positives.join("|")),e.negatives.length&&(s=`-(${r}${e.negatives.join("|")})`),n&&s?a=`${n}|${s}`:a=n||s,t.wrap?`(${r}${a})`:a},mt=(e,t,r,n)=>{if(r)return yt(e,t,k({wrap:!1},n));let s=String.fromCharCode(e);if(e===t)return s;let a=String.fromCharCode(t);return`[${s}-${a}]`},Ct=(e,t,r)=>{if(Array.isArray(e)){let n=r.wrap===!0,s=r.capture?"":"?:";return n?`(${s}${e.join("|")})`:e.join("|")}return yt(e,t,r)},Et=(...e)=>new RangeError("Invalid range arguments: "+Dr.inspect(...e)),_t=(e,t,r)=>{if(r.strictRanges===!0)throw Et([e,t]);return[]},Fr=(e,t)=>{if(t.strictRanges===!0)throw new TypeError(`Expected step "${e}" to be a number`);return[]},jr=(e,t,r=1,n={})=>{let s=Number(e),a=Number(t);if(!Number.isInteger(s)||!Number.isInteger(a)){if(n.strictRanges===!0)throw Et([e,t]);return[]}s===0&&(s=0),a===0&&(a=0);let i=s>a,o=String(e),f=String(t),y=String(r);r=Math.max(Math.abs(r),1);let h=Pe(o)||Pe(f)||Pe(y),R=h?Math.max(o.length,f.length,y.length):0,p=h===!1&&Br(e,t,n)===!1,H=n.transform||Mr(p);if(n.toRegex&&r===1)return mt(At(e,R),At(t,R),!0,n);let m={negatives:[],positives:[]},A=O=>m[O<0?"negatives":"positives"].push(Math.abs(O)),E=[],b=0;for(;i?s>=a:s<=a;)n.toRegex===!0&&r>1?A(s):E.push(Ur(H(s,b),R,p)),s=i?s-r:s+r,b++;return n.toRegex===!0?r>1?Gr(m,n):Ct(E,null,k({wrap:!1},n)):E},Kr=(e,t,r=1,n={})=>{if(!ye(e)&&e.length>1||!ye(t)&&t.length>1)return _t(e,t,n);let s=n.transform||(p=>String.fromCharCode(p)),a=`${e}`.charCodeAt(0),i=`${t}`.charCodeAt(0),o=a>i,f=Math.min(a,i),y=Math.max(a,i);if(n.toRegex&&r===1)return mt(f,y,!1,n);let h=[],R=0;for(;o?a>=i:a<=i;)h.push(s(a,R)),a=o?a-r:a+r,R++;return n.toRegex===!0?Ct(h,null,{wrap:!1,options:n}):h},we=(e,t,r,n={})=>{if(t==null&&Ie(e))return[e];if(!Ie(e)||!Ie(t))return _t(e,t,n);if(typeof r=="function")return we(e,t,1,{transform:r});if(Rt(r))return we(e,t,0,r);let s=k({},n);return s.capture===!0&&(s.wrap=!0),r=r||s.step||1,ye(r)?ye(e)&&ye(t)?jr(e,t,r,s):Kr(e,t,Math.max(Math.abs(r),1),s):r!=null&&!Rt(r)?Fr(r,s):we(e,t,1,r)};xt.exports=we});var vt=U((ns,St)=>{"use strict";var qr=De(),bt=ve(),Wr=(e,t={})=>{let r=(n,s={})=>{let a=bt.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o=a===!0||i===!0,f=t.escapeInvalid===!0?"\\":"",y="";if(n.isOpen===!0||n.isClose===!0)return f+n.value;if(n.type==="open")return o?f+n.value:"(";if(n.type==="close")return o?f+n.value:")";if(n.type==="comma")return n.prev.type==="comma"?"":o?n.value:"|";if(n.value)return n.value;if(n.nodes&&n.ranges>0){let h=bt.reduce(n.nodes),R=qr(...h,q(k({},t),{wrap:!1,toRegex:!0}));if(R.length!==0)return h.length>1&&R.length>1?`(${R})`:R}if(n.nodes)for(let h of n.nodes)y+=r(h,n);return y};return r(e)};St.exports=Wr});var Tt=U((ss,wt)=>{"use strict";var Qr=De(),Ht=He(),pe=ve(),le=(e="",t="",r=!1)=>{let n=[];if(e=[].concat(e),t=[].concat(t),!t.length)return e;if(!e.length)return r?pe.flatten(t).map(s=>`{${s}}`):t;for(let s of e)if(Array.isArray(s))for(let a of s)n.push(le(a,t,r));else for(let a of t)r===!0&&typeof a=="string"&&(a=`{${a}}`),n.push(Array.isArray(a)?le(s,a,r):s+a);return pe.flatten(n)},Xr=(e,t={})=>{let r=t.rangeLimit===void 0?1e3:t.rangeLimit,n=(s,a={})=>{s.queue=[];let i=a,o=a.queue;for(;i.type!=="brace"&&i.type!=="root"&&i.parent;)i=i.parent,o=i.queue;if(s.invalid||s.dollar){o.push(le(o.pop(),Ht(s,t)));return}if(s.type==="brace"&&s.invalid!==!0&&s.nodes.length===2){o.push(le(o.pop(),["{}"]));return}if(s.nodes&&s.ranges>0){let R=pe.reduce(s.nodes);if(pe.exceedsLimit(...R,t.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let p=Qr(...R,t);p.length===0&&(p=Ht(s,t)),o.push(le(o.pop(),p)),s.nodes=[];return}let f=pe.encloseBrace(s),y=s.queue,h=s;for(;h.type!=="brace"&&h.type!=="root"&&h.parent;)h=h.parent,y=h.queue;for(let R=0;R{"use strict";$t.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:` 7 | `,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var Pt=U((is,It)=>{"use strict";var zr=He(),{MAX_LENGTH:Ot,CHAR_BACKSLASH:Me,CHAR_BACKTICK:Zr,CHAR_COMMA:Vr,CHAR_DOT:Yr,CHAR_LEFT_PARENTHESES:Jr,CHAR_RIGHT_PARENTHESES:en,CHAR_LEFT_CURLY_BRACE:tn,CHAR_RIGHT_CURLY_BRACE:rn,CHAR_LEFT_SQUARE_BRACKET:kt,CHAR_RIGHT_SQUARE_BRACKET:Nt,CHAR_DOUBLE_QUOTE:nn,CHAR_SINGLE_QUOTE:sn,CHAR_NO_BREAK_SPACE:an,CHAR_ZERO_WIDTH_NOBREAK_SPACE:on}=Lt(),un=(e,t={})=>{if(typeof e!="string")throw new TypeError("Expected a string");let r=t||{},n=typeof r.maxLength=="number"?Math.min(Ot,r.maxLength):Ot;if(e.length>n)throw new SyntaxError(`Input length (${e.length}), exceeds max characters (${n})`);let s={type:"root",input:e,nodes:[]},a=[s],i=s,o=s,f=0,y=e.length,h=0,R=0,p,H={},m=()=>e[h++],A=E=>{if(E.type==="text"&&o.type==="dot"&&(o.type="text"),o&&o.type==="text"&&E.type==="text"){o.value+=E.value;return}return i.nodes.push(E),E.parent=i,E.prev=o,o=E,E};for(A({type:"bos"});h0){if(i.ranges>0){i.ranges=0;let E=i.nodes.shift();i.nodes=[E,{type:"text",value:zr(i)}]}A({type:"comma",value:p}),i.commas++;continue}if(p===Yr&&R>0&&i.commas===0){let E=i.nodes;if(R===0||E.length===0){A({type:"text",value:p});continue}if(o.type==="dot"){if(i.range=[],o.value+=p,o.type="range",i.nodes.length!==3&&i.nodes.length!==5){i.invalid=!0,i.ranges=0,o.type="text";continue}i.ranges++,i.args=[];continue}if(o.type==="range"){E.pop();let b=E[E.length-1];b.value+=o.value+p,o=b,i.ranges--;continue}A({type:"dot",value:p});continue}A({type:"text",value:p})}do if(i=a.pop(),i.type!=="root"){i.nodes.forEach(O=>{O.nodes||(O.type==="open"&&(O.isOpen=!0),O.type==="close"&&(O.isClose=!0),O.nodes||(O.type="text"),O.invalid=!0)});let E=a[a.length-1],b=E.nodes.indexOf(i);E.nodes.splice(b,1,...i.nodes)}while(a.length>0);return A({type:"eos"}),s};It.exports=un});var Bt=U((os,Mt)=>{"use strict";var Dt=He(),cn=vt(),ln=Tt(),pn=Pt(),z=(e,t={})=>{let r=[];if(Array.isArray(e))for(let n of e){let s=z.create(n,t);Array.isArray(s)?r.push(...s):r.push(s)}else r=[].concat(z.create(e,t));return t&&t.expand===!0&&t.nodupes===!0&&(r=[...new Set(r)]),r};z.parse=(e,t={})=>pn(e,t);z.stringify=(e,t={})=>typeof e=="string"?Dt(z.parse(e,t),t):Dt(e,t);z.compile=(e,t={})=>(typeof e=="string"&&(e=z.parse(e,t)),cn(e,t));z.expand=(e,t={})=>{typeof e=="string"&&(e=z.parse(e,t));let r=ln(e,t);return t.noempty===!0&&(r=r.filter(Boolean)),t.nodupes===!0&&(r=[...new Set(r)]),r};z.create=(e,t={})=>e===""||e.length<3?[e]:t.expand!==!0?z.compile(e,t):z.expand(e,t);Mt.exports=z});var Re=U((us,Kt)=>{"use strict";var fn=W("path"),ne="\\\\/",Ut=`[^${ne}]`,ae="\\.",hn="\\+",dn="\\?",Te="\\/",gn="(?=.)",Gt="[^/]",Be=`(?:${Te}|$)`,Ft=`(?:^|${Te})`,Ue=`${ae}{1,2}${Be}`,yn=`(?!${ae})`,Rn=`(?!${Ft}${Ue})`,An=`(?!${ae}{0,1}${Be})`,mn=`(?!${Ue})`,Cn=`[^.${Te}]`,En=`${Gt}*?`,jt={DOT_LITERAL:ae,PLUS_LITERAL:hn,QMARK_LITERAL:dn,SLASH_LITERAL:Te,ONE_CHAR:gn,QMARK:Gt,END_ANCHOR:Be,DOTS_SLASH:Ue,NO_DOT:yn,NO_DOTS:Rn,NO_DOT_SLASH:An,NO_DOTS_SLASH:mn,QMARK_NO_DOT:Cn,STAR:En,START_ANCHOR:Ft},_n=q(k({},jt),{SLASH_LITERAL:`[${ne}]`,QMARK:Ut,STAR:`${Ut}*?`,DOTS_SLASH:`${ae}{1,2}(?:[${ne}]|$)`,NO_DOT:`(?!${ae})`,NO_DOTS:`(?!(?:^|[${ne}])${ae}{1,2}(?:[${ne}]|$))`,NO_DOT_SLASH:`(?!${ae}{0,1}(?:[${ne}]|$))`,NO_DOTS_SLASH:`(?!${ae}{1,2}(?:[${ne}]|$))`,QMARK_NO_DOT:`[^.${ne}]`,START_ANCHOR:`(?:^|[${ne}])`,END_ANCHOR:`(?:[${ne}]|$)`}),xn={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};Kt.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:xn,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:fn.sep,extglobChars(e){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${e.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(e){return e===!0?_n:jt}}});var Ae=U(Q=>{"use strict";var bn=W("path"),Sn=process.platform==="win32",{REGEX_BACKSLASH:vn,REGEX_REMOVE_BACKSLASH:Hn,REGEX_SPECIAL_CHARS:wn,REGEX_SPECIAL_CHARS_GLOBAL:Tn}=Re();Q.isObject=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);Q.hasRegexChars=e=>wn.test(e);Q.isRegexChar=e=>e.length===1&&Q.hasRegexChars(e);Q.escapeRegex=e=>e.replace(Tn,"\\$1");Q.toPosixSlashes=e=>e.replace(vn,"/");Q.removeBackslashes=e=>e.replace(Hn,t=>t==="\\"?"":t);Q.supportsLookbehinds=()=>{let e=process.version.slice(1).split(".").map(Number);return e.length===3&&e[0]>=9||e[0]===8&&e[1]>=10};Q.isWindows=e=>e&&typeof e.windows=="boolean"?e.windows:Sn===!0||bn.sep==="\\";Q.escapeLast=(e,t,r)=>{let n=e.lastIndexOf(t,r);return n===-1?e:e[n-1]==="\\"?Q.escapeLast(e,t,n-1):`${e.slice(0,n)}\\${e.slice(n)}`};Q.removePrefix=(e,t={})=>{let r=e;return r.startsWith("./")&&(r=r.slice(2),t.prefix="./"),r};Q.wrapOutput=(e,t={},r={})=>{let n=r.contains?"":"^",s=r.contains?"":"$",a=`${n}(?:${e})${s}`;return t.negated===!0&&(a=`(?:^(?!${a}).*$)`),a}});var Yt=U((ls,Vt)=>{"use strict";var qt=Ae(),{CHAR_ASTERISK:Ge,CHAR_AT:$n,CHAR_BACKWARD_SLASH:me,CHAR_COMMA:Ln,CHAR_DOT:Fe,CHAR_EXCLAMATION_MARK:je,CHAR_FORWARD_SLASH:Wt,CHAR_LEFT_CURLY_BRACE:Ke,CHAR_LEFT_PARENTHESES:qe,CHAR_LEFT_SQUARE_BRACKET:On,CHAR_PLUS:kn,CHAR_QUESTION_MARK:Qt,CHAR_RIGHT_CURLY_BRACE:Nn,CHAR_RIGHT_PARENTHESES:Xt,CHAR_RIGHT_SQUARE_BRACKET:In}=Re(),zt=e=>e===Wt||e===me,Zt=e=>{e.isPrefix!==!0&&(e.depth=e.isGlobstar?Infinity:1)},Pn=(e,t)=>{let r=t||{},n=e.length-1,s=r.parts===!0||r.scanToEnd===!0,a=[],i=[],o=[],f=e,y=-1,h=0,R=0,p=!1,H=!1,m=!1,A=!1,E=!1,b=!1,O=!1,N=!1,J=!1,G=!1,ie=0,F,C,v={value:"",depth:0,isGlob:!1},B=()=>y>=n,l=()=>f.charCodeAt(y+1),$=()=>(F=C,f.charCodeAt(++y));for(;y0&&(oe=f.slice(0,h),f=f.slice(h),R-=h),w&&m===!0&&R>0?(w=f.slice(0,R),u=f.slice(R)):m===!0?(w="",u=f):w=f,w&&w!==""&&w!=="/"&&w!==f&&zt(w.charCodeAt(w.length-1))&&(w=w.slice(0,-1)),r.unescape===!0&&(u&&(u=qt.removeBackslashes(u)),w&&O===!0&&(w=qt.removeBackslashes(w)));let c={prefix:oe,input:e,start:h,base:w,glob:u,isBrace:p,isBracket:H,isGlob:m,isExtglob:A,isGlobstar:E,negated:N,negatedExtglob:J};if(r.tokens===!0&&(c.maxDepth=0,zt(C)||i.push(v),c.tokens=i),r.parts===!0||r.tokens===!0){let j;for(let S=0;S{"use strict";var $e=Re(),Z=Ae(),{MAX_LENGTH:Le,POSIX_REGEX_SOURCE:Dn,REGEX_NON_SPECIAL_CHARS:Mn,REGEX_SPECIAL_CHARS_BACKREF:Bn,REPLACEMENTS:Jt}=$e,Un=(e,t)=>{if(typeof t.expandRange=="function")return t.expandRange(...e,t);e.sort();let r=`[${e.join("-")}]`;try{new RegExp(r)}catch(n){return e.map(s=>Z.escapeRegex(s)).join("..")}return r},fe=(e,t)=>`Missing ${e}: "${t}" - use "\\\\${t}" to match literal characters`,er=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");e=Jt[e]||e;let r=k({},t),n=typeof r.maxLength=="number"?Math.min(Le,r.maxLength):Le,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);let a={type:"bos",value:"",output:r.prepend||""},i=[a],o=r.capture?"":"?:",f=Z.isWindows(t),y=$e.globChars(f),h=$e.extglobChars(y),{DOT_LITERAL:R,PLUS_LITERAL:p,SLASH_LITERAL:H,ONE_CHAR:m,DOTS_SLASH:A,NO_DOT:E,NO_DOT_SLASH:b,NO_DOTS_SLASH:O,QMARK:N,QMARK_NO_DOT:J,STAR:G,START_ANCHOR:ie}=y,F=g=>`(${o}(?:(?!${ie}${g.dot?A:R}).)*?)`,C=r.dot?"":E,v=r.dot?N:J,B=r.bash===!0?F(r):G;r.capture&&(B=`(${B})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let l={input:e,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:i};e=Z.removePrefix(e,l),s=e.length;let $=[],w=[],oe=[],u=a,c,j=()=>l.index===s-1,S=l.peek=(g=1)=>e[l.index+g],ee=l.advance=()=>e[++l.index]||"",te=()=>e.slice(l.index+1),X=(g="",T=0)=>{l.consumed+=g,l.index+=T},_e=g=>{l.output+=g.output!=null?g.output:g.value,X(g.value)},Ar=()=>{let g=1;for(;S()==="!"&&(S(2)!=="("||S(3)==="?");)ee(),l.start++,g++;return g%2==0?!1:(l.negated=!0,l.start++,!0)},xe=g=>{l[g]++,oe.push(g)},ue=g=>{l[g]--,oe.pop()},x=g=>{if(u.type==="globstar"){let T=l.braces>0&&(g.type==="comma"||g.type==="brace"),d=g.extglob===!0||$.length&&(g.type==="pipe"||g.type==="paren");g.type!=="slash"&&g.type!=="paren"&&!T&&!d&&(l.output=l.output.slice(0,-u.output.length),u.type="star",u.value="*",u.output=B,l.output+=u.output)}if($.length&&g.type!=="paren"&&($[$.length-1].inner+=g.value),(g.value||g.output)&&_e(g),u&&u.type==="text"&&g.type==="text"){u.value+=g.value,u.output=(u.output||"")+g.value;return}g.prev=u,i.push(g),u=g},be=(g,T)=>{let d=q(k({},h[T]),{conditions:1,inner:""});d.prev=u,d.parens=l.parens,d.output=l.output;let _=(r.capture?"(":"")+d.open;xe("parens"),x({type:g,value:T,output:l.output?"":m}),x({type:"paren",extglob:!0,value:ee(),output:_}),$.push(d)},mr=g=>{let T=g.close+(r.capture?")":""),d;if(g.type==="negate"){let _=B;g.inner&&g.inner.length>1&&g.inner.includes("/")&&(_=F(r)),(_!==B||j()||/^\)+$/.test(te()))&&(T=g.close=`)$))${_}`),g.inner.includes("*")&&(d=te())&&/^\.[^\\/.]+$/.test(d)&&(T=g.close=`)${d})${_})`),g.prev.type==="bos"&&(l.negatedExtglob=!0)}x({type:"paren",extglob:!0,value:c,output:T}),ue("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(e)){let g=!1,T=e.replace(Bn,(d,_,I,K,M,ke)=>K==="\\"?(g=!0,d):K==="?"?_?_+K+(M?N.repeat(M.length):""):ke===0?v+(M?N.repeat(M.length):""):N.repeat(I.length):K==="."?R.repeat(I.length):K==="*"?_?_+K+(M?B:""):B:_?d:`\\${d}`);return g===!0&&(r.unescape===!0?T=T.replace(/\\/g,""):T=T.replace(/\\+/g,d=>d.length%2==0?"\\\\":d?"\\":"")),T===e&&r.contains===!0?(l.output=e,l):(l.output=Z.wrapOutput(T,l,t),l)}for(;!j();){if(c=ee(),c==="\0")continue;if(c==="\\"){let d=S();if(d==="/"&&r.bash!==!0||d==="."||d===";")continue;if(!d){c+="\\",x({type:"text",value:c});continue}let _=/^\\+/.exec(te()),I=0;if(_&&_[0].length>2&&(I=_[0].length,l.index+=I,I%2!=0&&(c+="\\")),r.unescape===!0?c=ee():c+=ee(),l.brackets===0){x({type:"text",value:c});continue}}if(l.brackets>0&&(c!=="]"||u.value==="["||u.value==="[^")){if(r.posix!==!1&&c===":"){let d=u.value.slice(1);if(d.includes("[")&&(u.posix=!0,d.includes(":"))){let _=u.value.lastIndexOf("["),I=u.value.slice(0,_),K=u.value.slice(_+2),M=Dn[K];if(M){u.value=I+M,l.backtrack=!0,ee(),!a.output&&i.indexOf(u)===1&&(a.output=m);continue}}}(c==="["&&S()!==":"||c==="-"&&S()==="]")&&(c=`\\${c}`),c==="]"&&(u.value==="["||u.value==="[^")&&(c=`\\${c}`),r.posix===!0&&c==="!"&&u.value==="["&&(c="^"),u.value+=c,_e({value:c});continue}if(l.quotes===1&&c!=='"'){c=Z.escapeRegex(c),u.value+=c,_e({value:c});continue}if(c==='"'){l.quotes=l.quotes===1?0:1,r.keepQuotes===!0&&x({type:"text",value:c});continue}if(c==="("){xe("parens"),x({type:"paren",value:c});continue}if(c===")"){if(l.parens===0&&r.strictBrackets===!0)throw new SyntaxError(fe("opening","("));let d=$[$.length-1];if(d&&l.parens===d.parens+1){mr($.pop());continue}x({type:"paren",value:c,output:l.parens?")":"\\)"}),ue("parens");continue}if(c==="["){if(r.nobracket===!0||!te().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(fe("closing","]"));c=`\\${c}`}else xe("brackets");x({type:"bracket",value:c});continue}if(c==="]"){if(r.nobracket===!0||u&&u.type==="bracket"&&u.value.length===1){x({type:"text",value:c,output:`\\${c}`});continue}if(l.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(fe("opening","["));x({type:"text",value:c,output:`\\${c}`});continue}ue("brackets");let d=u.value.slice(1);if(u.posix!==!0&&d[0]==="^"&&!d.includes("/")&&(c=`/${c}`),u.value+=c,_e({value:c}),r.literalBrackets===!1||Z.hasRegexChars(d))continue;let _=Z.escapeRegex(u.value);if(l.output=l.output.slice(0,-u.value.length),r.literalBrackets===!0){l.output+=_,u.value=_;continue}u.value=`(${o}${_}|${u.value})`,l.output+=u.value;continue}if(c==="{"&&r.nobrace!==!0){xe("braces");let d={type:"brace",value:c,output:"(",outputIndex:l.output.length,tokensIndex:l.tokens.length};w.push(d),x(d);continue}if(c==="}"){let d=w[w.length-1];if(r.nobrace===!0||!d){x({type:"text",value:c,output:c});continue}let _=")";if(d.dots===!0){let I=i.slice(),K=[];for(let M=I.length-1;M>=0&&(i.pop(),I[M].type!=="brace");M--)I[M].type!=="dots"&&K.unshift(I[M].value);_=Un(K,r),l.backtrack=!0}if(d.comma!==!0&&d.dots!==!0){let I=l.output.slice(0,d.outputIndex),K=l.tokens.slice(d.tokensIndex);d.value=d.output="\\{",c=_="\\}",l.output=I;for(let M of K)l.output+=M.output||M.value}x({type:"brace",value:c,output:_}),ue("braces"),w.pop();continue}if(c==="|"){$.length>0&&$[$.length-1].conditions++,x({type:"text",value:c});continue}if(c===","){let d=c,_=w[w.length-1];_&&oe[oe.length-1]==="braces"&&(_.comma=!0,d="|"),x({type:"comma",value:c,output:d});continue}if(c==="/"){if(u.type==="dot"&&l.index===l.start+1){l.start=l.index+1,l.consumed="",l.output="",i.pop(),u=a;continue}x({type:"slash",value:c,output:H});continue}if(c==="."){if(l.braces>0&&u.type==="dot"){u.value==="."&&(u.output=R);let d=w[w.length-1];u.type="dots",u.output+=c,u.value+=c,d.dots=!0;continue}if(l.braces+l.parens===0&&u.type!=="bos"&&u.type!=="slash"){x({type:"text",value:c,output:R});continue}x({type:"dot",value:c,output:R});continue}if(c==="?"){if(!(u&&u.value==="(")&&r.noextglob!==!0&&S()==="("&&S(2)!=="?"){be("qmark",c);continue}if(u&&u.type==="paren"){let _=S(),I=c;if(_==="<"&&!Z.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(u.value==="("&&!/[!=<:]/.test(_)||_==="<"&&!/<([!=]|\w+>)/.test(te()))&&(I=`\\${c}`),x({type:"text",value:c,output:I});continue}if(r.dot!==!0&&(u.type==="slash"||u.type==="bos")){x({type:"qmark",value:c,output:J});continue}x({type:"qmark",value:c,output:N});continue}if(c==="!"){if(r.noextglob!==!0&&S()==="("&&(S(2)!=="?"||!/[!=<:]/.test(S(3)))){be("negate",c);continue}if(r.nonegate!==!0&&l.index===0){Ar();continue}}if(c==="+"){if(r.noextglob!==!0&&S()==="("&&S(2)!=="?"){be("plus",c);continue}if(u&&u.value==="("||r.regex===!1){x({type:"plus",value:c,output:p});continue}if(u&&(u.type==="bracket"||u.type==="paren"||u.type==="brace")||l.parens>0){x({type:"plus",value:c});continue}x({type:"plus",value:p});continue}if(c==="@"){if(r.noextglob!==!0&&S()==="("&&S(2)!=="?"){x({type:"at",extglob:!0,value:c,output:""});continue}x({type:"text",value:c});continue}if(c!=="*"){(c==="$"||c==="^")&&(c=`\\${c}`);let d=Mn.exec(te());d&&(c+=d[0],l.index+=d[0].length),x({type:"text",value:c});continue}if(u&&(u.type==="globstar"||u.star===!0)){u.type="star",u.star=!0,u.value+=c,u.output=B,l.backtrack=!0,l.globstar=!0,X(c);continue}let g=te();if(r.noextglob!==!0&&/^\([^?]/.test(g)){be("star",c);continue}if(u.type==="star"){if(r.noglobstar===!0){X(c);continue}let d=u.prev,_=d.prev,I=d.type==="slash"||d.type==="bos",K=_&&(_.type==="star"||_.type==="globstar");if(r.bash===!0&&(!I||g[0]&&g[0]!=="/")){x({type:"star",value:c,output:""});continue}let M=l.braces>0&&(d.type==="comma"||d.type==="brace"),ke=$.length&&(d.type==="pipe"||d.type==="paren");if(!I&&d.type!=="paren"&&!M&&!ke){x({type:"star",value:c,output:""});continue}for(;g.slice(0,3)==="/**";){let Se=e[l.index+4];if(Se&&Se!=="/")break;g=g.slice(3),X("/**",3)}if(d.type==="bos"&&j()){u.type="globstar",u.value+=c,u.output=F(r),l.output=u.output,l.globstar=!0,X(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&!K&&j()){l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=F(r)+(r.strictSlashes?")":"|$)"),u.value+=c,l.globstar=!0,l.output+=d.output+u.output,X(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&g[0]==="/"){let Se=g[1]!==void 0?"|$":"";l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=`${F(r)}${H}|${H}${Se})`,u.value+=c,l.output+=d.output+u.output,l.globstar=!0,X(c+ee()),x({type:"slash",value:"/",output:""});continue}if(d.type==="bos"&&g[0]==="/"){u.type="globstar",u.value+=c,u.output=`(?:^|${H}|${F(r)}${H})`,l.output=u.output,l.globstar=!0,X(c+ee()),x({type:"slash",value:"/",output:""});continue}l.output=l.output.slice(0,-u.output.length),u.type="globstar",u.output=F(r),u.value+=c,l.output+=u.output,l.globstar=!0,X(c);continue}let T={type:"star",value:c,output:B};if(r.bash===!0){T.output=".*?",(u.type==="bos"||u.type==="slash")&&(T.output=C+T.output),x(T);continue}if(u&&(u.type==="bracket"||u.type==="paren")&&r.regex===!0){T.output=c,x(T);continue}(l.index===l.start||u.type==="slash"||u.type==="dot")&&(u.type==="dot"?(l.output+=b,u.output+=b):r.dot===!0?(l.output+=O,u.output+=O):(l.output+=C,u.output+=C),S()!=="*"&&(l.output+=m,u.output+=m)),x(T)}for(;l.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing","]"));l.output=Z.escapeLast(l.output,"["),ue("brackets")}for(;l.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing",")"));l.output=Z.escapeLast(l.output,"("),ue("parens")}for(;l.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing","}"));l.output=Z.escapeLast(l.output,"{"),ue("braces")}if(r.strictSlashes!==!0&&(u.type==="star"||u.type==="bracket")&&x({type:"maybe_slash",value:"",output:`${H}?`}),l.backtrack===!0){l.output="";for(let g of l.tokens)l.output+=g.output!=null?g.output:g.value,g.suffix&&(l.output+=g.suffix)}return l};er.fastpaths=(e,t)=>{let r=k({},t),n=typeof r.maxLength=="number"?Math.min(Le,r.maxLength):Le,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);e=Jt[e]||e;let a=Z.isWindows(t),{DOT_LITERAL:i,SLASH_LITERAL:o,ONE_CHAR:f,DOTS_SLASH:y,NO_DOT:h,NO_DOTS:R,NO_DOTS_SLASH:p,STAR:H,START_ANCHOR:m}=$e.globChars(a),A=r.dot?R:h,E=r.dot?p:h,b=r.capture?"":"?:",O={negated:!1,prefix:""},N=r.bash===!0?".*?":H;r.capture&&(N=`(${N})`);let J=C=>C.noglobstar===!0?N:`(${b}(?:(?!${m}${C.dot?y:i}).)*?)`,G=C=>{switch(C){case"*":return`${A}${f}${N}`;case".*":return`${i}${f}${N}`;case"*.*":return`${A}${N}${i}${f}${N}`;case"*/*":return`${A}${N}${o}${f}${E}${N}`;case"**":return A+J(r);case"**/*":return`(?:${A}${J(r)}${o})?${E}${f}${N}`;case"**/*.*":return`(?:${A}${J(r)}${o})?${E}${N}${i}${f}${N}`;case"**/.*":return`(?:${A}${J(r)}${o})?${i}${f}${N}`;default:{let v=/^(.*?)\.(\w+)$/.exec(C);if(!v)return;let B=G(v[1]);return B?B+i+v[2]:void 0}}},ie=Z.removePrefix(e,O),F=G(ie);return F&&r.strictSlashes!==!0&&(F+=`${o}?`),F};tr.exports=er});var sr=U((fs,nr)=>{"use strict";var Gn=W("path"),Fn=Yt(),We=rr(),Qe=Ae(),jn=Re(),Kn=e=>e&&typeof e=="object"&&!Array.isArray(e),P=(e,t,r=!1)=>{if(Array.isArray(e)){let h=e.map(p=>P(p,t,r));return p=>{for(let H of h){let m=H(p);if(m)return m}return!1}}let n=Kn(e)&&e.tokens&&e.input;if(e===""||typeof e!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let s=t||{},a=Qe.isWindows(t),i=n?P.compileRe(e,t):P.makeRe(e,t,!1,!0),o=i.state;delete i.state;let f=()=>!1;if(s.ignore){let h=q(k({},t),{ignore:null,onMatch:null,onResult:null});f=P(s.ignore,h,r)}let y=(h,R=!1)=>{let{isMatch:p,match:H,output:m}=P.test(h,i,t,{glob:e,posix:a}),A={glob:e,state:o,regex:i,posix:a,input:h,output:m,match:H,isMatch:p};return typeof s.onResult=="function"&&s.onResult(A),p===!1?(A.isMatch=!1,R?A:!1):f(h)?(typeof s.onIgnore=="function"&&s.onIgnore(A),A.isMatch=!1,R?A:!1):(typeof s.onMatch=="function"&&s.onMatch(A),R?A:!0)};return r&&(y.state=o),y};P.test=(e,t,r,{glob:n,posix:s}={})=>{if(typeof e!="string")throw new TypeError("Expected input to be a string");if(e==="")return{isMatch:!1,output:""};let a=r||{},i=a.format||(s?Qe.toPosixSlashes:null),o=e===n,f=o&&i?i(e):e;return o===!1&&(f=i?i(e):e,o=f===n),(o===!1||a.capture===!0)&&(a.matchBase===!0||a.basename===!0?o=P.matchBase(e,t,r,s):o=t.exec(f)),{isMatch:Boolean(o),match:o,output:f}};P.matchBase=(e,t,r,n=Qe.isWindows(r))=>(t instanceof RegExp?t:P.makeRe(t,r)).test(Gn.basename(e));P.isMatch=(e,t,r)=>P(t,r)(e);P.parse=(e,t)=>Array.isArray(e)?e.map(r=>P.parse(r,t)):We(e,q(k({},t),{fastpaths:!1}));P.scan=(e,t)=>Fn(e,t);P.compileRe=(e,t,r=!1,n=!1)=>{if(r===!0)return e.output;let s=t||{},a=s.contains?"":"^",i=s.contains?"":"$",o=`${a}(?:${e.output})${i}`;e&&e.negated===!0&&(o=`^(?!${o}).*$`);let f=P.toRegex(o,t);return n===!0&&(f.state=e),f};P.makeRe=(e,t={},r=!1,n=!1)=>{if(!e||typeof e!="string")throw new TypeError("Expected a non-empty string");let s={negated:!1,fastpaths:!0};return t.fastpaths!==!1&&(e[0]==="."||e[0]==="*")&&(s.output=We.fastpaths(e,t)),s.output||(s=We(e,t)),P.compileRe(s,t,r,n)};P.toRegex=(e,t)=>{try{let r=t||{};return new RegExp(e,r.flags||(r.nocase?"i":""))}catch(r){if(t&&t.debug===!0)throw r;return/$^/}};P.constants=jn;nr.exports=P});var ir=U((hs,ar)=>{"use strict";ar.exports=sr()});var pr=U((ds,lr)=>{"use strict";var or=W("util"),ur=Bt(),se=ir(),Xe=Ae(),cr=e=>e===""||e==="./",L=(e,t,r)=>{t=[].concat(t),e=[].concat(e);let n=new Set,s=new Set,a=new Set,i=0,o=h=>{a.add(h.output),r&&r.onResult&&r.onResult(h)};for(let h=0;h!n.has(h));if(r&&y.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${t.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?t.map(h=>h.replace(/\\/g,"")):t}return y};L.match=L;L.matcher=(e,t)=>se(e,t);L.isMatch=(e,t,r)=>se(t,r)(e);L.any=L.isMatch;L.not=(e,t,r={})=>{t=[].concat(t).map(String);let n=new Set,s=[],a=o=>{r.onResult&&r.onResult(o),s.push(o.output)},i=L(e,t,q(k({},r),{onResult:a}));for(let o of s)i.includes(o)||n.add(o);return[...n]};L.contains=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${or.inspect(e)}"`);if(Array.isArray(t))return t.some(n=>L.contains(e,n,r));if(typeof t=="string"){if(cr(e)||cr(t))return!1;if(e.includes(t)||e.startsWith("./")&&e.slice(2).includes(t))return!0}return L.isMatch(e,t,q(k({},r),{contains:!0}))};L.matchKeys=(e,t,r)=>{if(!Xe.isObject(e))throw new TypeError("Expected the first argument to be an object");let n=L(Object.keys(e),t,r),s={};for(let a of n)s[a]=e[a];return s};L.some=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=se(String(s),r);if(n.some(i=>a(i)))return!0}return!1};L.every=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=se(String(s),r);if(!n.every(i=>a(i)))return!1}return!0};L.all=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${or.inspect(e)}"`);return[].concat(t).every(n=>se(n,r)(e))};L.capture=(e,t,r)=>{let n=Xe.isWindows(r),a=se.makeRe(String(e),q(k({},r),{capture:!0})).exec(n?Xe.toPosixSlashes(t):t);if(a)return a.slice(1).map(i=>i===void 0?"":i)};L.makeRe=(...e)=>se.makeRe(...e);L.scan=(...e)=>se.scan(...e);L.parse=(e,t)=>{let r=[];for(let n of[].concat(e||[]))for(let s of ur(String(n),t))r.push(se.parse(s,t));return r};L.braces=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return t&&t.nobrace===!0||!/\{.*\}/.test(e)?[e]:ur(e,t)};L.braceExpand=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return L.braces(e,q(k({},t),{expand:!0}))};lr.exports=L});var Zn={};wr(Zn,{default:()=>zn});var Oe=re(W("@yarnpkg/cli")),D=re(W("@yarnpkg/core")),Y=re(W("clipanion")),Rr=re(pr()),Ye=re(W("semver")),Je=re(W("typanion"));var he=re(W("@yarnpkg/core")),gr=re(W("@yarnpkg/plugin-essentials"));var Ce=re(W("semver")),fr=Boolean;function qn(e){var s;let[t,r,n]=(s=e.match(/(github|bitbucket|gitlab):(.+)/))!=null?s:[];return r?`https://${r}.${r==="bitbucket"?"org":"com"}/${n}`:`https://github.com/${e}`}function hr(e){let{homepage:t,repository:r}=e.raw;return t||(typeof r=="string"?qn(r):r==null?void 0:r.url)}function dr(e,t){return Ce.default.parse(t).prerelease.length?Ce.default.lt(e,t):Ce.default.lt(Ce.default.coerce(e),t)}var ze=class{constructor(t,r,n,s){this.configuration=t;this.project=r;this.workspace=n;this.cache=s}async fetch({pkg:t,range:r,url:n}){let s=gr.suggestUtils.fetchDescriptorFrom(t,r,{cache:this.cache,preserveModifier:!1,project:this.project,workspace:this.workspace}),a=n?this.fetchURL(t):Promise.resolve(void 0),[i,o]=await Promise.all([s,a]);if(!i){let f=he.structUtils.prettyIdent(this.configuration,t);throw new Error(`Could not fetch candidate for ${f}.`)}return{url:o,version:i.range}}async fetchURL(t){var a;let r=this.configuration.makeFetcher(),n=await r.fetch(t,{cache:this.cache,checksums:this.project.storedChecksums,fetcher:r,project:this.project,report:new he.ThrowReport,skipIntegrityCheck:!0}),s;try{s=await he.Manifest.find(n.prefixPath,{baseFs:n.packageFs})}finally{(a=n.releaseFs)==null||a.call(n)}return hr(s)}};var de=re(W("@yarnpkg/core")),Wn=/^([0-9]+\.)([0-9]+\.)(.+)$/,Qn=["name","current","latest","workspace","type","url"],Ze=class{constructor(t,r,n,s){this.report=t;this.configuration=r;this.dependencies=n;this.extraColumns=s;this.sizes=null;this.headers={current:"Current",latest:"Latest",name:"Package",type:"Package Type",url:"URL",workspace:"Workspace"}}print(){this.sizes=this.getColumnSizes(),this.printHeader(),this.dependencies.forEach(t=>{var n,s;let r=this.getDiffColor(t);this.printRow({current:t.current.padEnd(this.sizes.current),latest:this.formatVersion(t,"latest",r),name:this.applyColor(t.name.padEnd(this.sizes.name),r),type:t.type.padEnd(this.sizes.type),url:(n=t.url)==null?void 0:n.padEnd(this.sizes.url),workspace:(s=t.workspace)==null?void 0:s.padEnd(this.sizes.workspace)})})}applyColor(t,r){return de.formatUtils.pretty(this.configuration,t,r)}formatVersion(t,r,n){let s=t[r].padEnd(this.sizes[r]),a=s.match(Wn);if(!a)return s;let i=["red","yellow","green"].indexOf(n)+1,o=a.slice(1,i).join(""),f=a.slice(i).join("");return o+de.formatUtils.pretty(this.configuration,this.applyColor(f,n),"bold")}getDiffColor(t){return{major:"red",minor:"yellow",patch:"green"}[t.severity]}getColumnSizes(){let t={current:this.headers.current.length,latest:this.headers.latest.length,name:this.headers.name.length,type:this.headers.type.length,url:this.headers.url.length,workspace:this.headers.workspace.length};for(let r of this.dependencies)for(let[n,s]of Object.entries(r)){let a=t[n],i=(s||"").length;t[n]=a>i?a:i}return t}formatColumnHeader(t){return de.formatUtils.pretty(this.configuration,this.headers[t].padEnd(this.sizes[t]),"bold")}printHeader(){this.printRow({current:this.formatColumnHeader("current"),latest:this.formatColumnHeader("latest"),name:this.formatColumnHeader("name"),type:this.formatColumnHeader("type"),url:this.formatColumnHeader("url"),workspace:this.formatColumnHeader("workspace")})}printRow(t){let r=Qn.filter(n=>{var s;return(s=this.extraColumns[n])!=null?s:!0}).map(n=>t[n]).join(" ").trim();this.report.reportInfo(de.MessageName.UNNAMED,r)}};var Ve=["dependencies","devDependencies"],yr=["major","minor","patch"];var Ee=class extends Oe.BaseCommand{constructor(){super(...arguments);this.patterns=Y.Option.Rest();this.workspace=Y.Option.Boolean("-w,--workspace",!1,{description:"Only include outdated dependencies in the current workspace"});this.check=Y.Option.Boolean("-c,--check",!1,{description:"Exit with exit code 1 when outdated dependencies are found"});this.json=Y.Option.Boolean("--json",!1,{description:"Format the output as JSON"});this.severity=Y.Option.String("-s,--severity",{description:"Filter results based on the severity of the update",validator:Je.default.isEnum(yr)});this.type=Y.Option.String("-t,--type",{description:"Filter results based on the dependency type",validator:Je.default.isEnum(Ve)});this.url=Y.Option.Boolean("--url",!1,{description:"Include the homepage URL of each package in the output"})}async execute(){let{cache:t,configuration:r,project:n,workspace:s}=await this.loadProject(),a=new ze(r,n,s,t),i=this.getWorkspaces(n,s),o=this.getDependencies(r,i);if(this.json){let y=await this.getOutdatedDependencies(n,a,o);this.context.stdout.write(JSON.stringify(y)+` 8 | `);return}return(await D.StreamReport.start({configuration:r,stdout:this.context.stdout},async y=>{await this.checkOutdatedDependencies(r,n,o,a,y)})).exitCode()}async checkOutdatedDependencies(t,r,n,s,a){let i=null;await a.startTimerPromise("Checking for outdated dependencies",async()=>{let o=n.length,f=D.StreamReport.progressViaCounter(o);a.reportProgress(f),i=await this.getOutdatedDependencies(r,s,n,f)}),a.reportSeparator(),i.length?(new Ze(a,t,i,{url:this.url,workspace:this.includeWorkspace(r)}).print(),a.reportSeparator(),this.printOutdatedCount(a,i.length)):this.printUpToDate(t,a)}async loadProject(){let t=await D.Configuration.find(this.context.cwd,this.context.plugins),[r,{project:n,workspace:s}]=await Promise.all([D.Cache.find(t),D.Project.find(t,this.context.cwd)]);if(await n.restoreInstallState(),!s)throw new Oe.WorkspaceRequiredError(n.cwd,this.context.cwd);return{cache:r,configuration:t,project:n,workspace:s}}getWorkspaces(t,r){return this.workspace?[r]:t.workspaces}includeWorkspace(t){return!this.workspace&&t.workspaces.length>1}get dependencyTypes(){return this.type?[this.type]:Ve}getDependencies(t,r){let n=[];for(let a of r){let{anchoredLocator:i,project:o}=a,f=o.storedPackages.get(i.locatorHash);f||this.throw(t,i);for(let y of this.dependencyTypes)for(let h of a.manifest[y].values()){let{range:R}=h;if(R.includes(":")&&!/(npm|patch):/.test(R))continue;let p=f.dependencies.get(h.identHash);p||this.throw(t,h);let H=o.storedResolutions.get(p.descriptorHash);H||this.throw(t,p);let m=o.storedPackages.get(H);m||this.throw(t,p),n.push({dependencyType:y,name:D.structUtils.stringifyIdent(h),pkg:m,workspace:a})}}if(!this.patterns.length)return n;let s=n.filter(({name:a})=>Rr.default.isMatch(a,this.patterns));if(!s.length)throw new Y.UsageError(`Pattern ${D.formatUtils.prettyList(t,this.patterns,D.FormatType.CODE)} doesn't match any packages referenced by any workspace`);return s}throw(t,r){let n=D.structUtils.prettyIdent(t,r);throw new Error(`Package for ${n} not found in the project`)}getSeverity(t,r){let n=Ye.default.coerce(t),s=Ye.default.coerce(r);return n.major===0||s.major>n.major?"major":s.minor>n.minor?"minor":"patch"}async getOutdatedDependencies(t,r,n,s){let a=n.map(async({dependencyType:i,name:o,pkg:f,workspace:y})=>{if(y.project.tryWorkspaceByLocator(f))return;let{url:h,version:R}=await r.fetch({pkg:f,range:"latest",url:this.url});if(s==null||s.tick(),dr(f.version,R))return{current:f.version,latest:R,name:o,severity:this.getSeverity(f.version,R),type:i,url:h,workspace:this.includeWorkspace(t)?this.getWorkspaceName(y):void 0}});return(await Promise.all(a)).filter(fr).filter(({severity:i})=>!this.severity||i===this.severity).sort((i,o)=>i.name.localeCompare(o.name))}getWorkspaceName(t){return t.manifest.name?D.structUtils.stringifyIdent(t.manifest.name):t.computeCandidateName()}printOutdatedCount(t,r){let n=[D.MessageName.UNNAMED,r===1?"1 dependency is out of date":`${r} dependencies are out of date`];this.check?t.reportError(...n):t.reportWarning(...n)}printUpToDate(t,r){let n="\u2728 All your dependencies are up to date!";r.reportInfo(D.MessageName.UNNAMED,D.formatUtils.pretty(t,n,"green"))}};Ee.paths=[["outdated"]],Ee.usage=Y.Command.Usage({description:"view outdated dependencies",details:` 9 | This command finds outdated dependencies in a project and prints the result in a table or JSON format. 10 | 11 | This command accepts glob patterns as arguments to filter the output. Make sure to escape the patterns, to prevent your own shell from trying to expand them. 12 | `,examples:[["View outdated dependencies","yarn outdated"],["View outdated dependencies with the `@babel` scope","yarn outdated '@babel/*'"],["Filter results to only include devDependencies","yarn outdated --type devDependencies"],["Filter results to only include major version updates","yarn outdated --severity major"]]});var Xn={commands:[Ee]},zn=Xn;return Zn;})(); 13 | /*! 14 | * fill-range 15 | * 16 | * Copyright (c) 2014-present, Jon Schlinkert. 17 | * Licensed under the MIT License. 18 | */ 19 | /*! 20 | * is-number 21 | * 22 | * Copyright (c) 2014-present, Jon Schlinkert. 23 | * Released under the MIT License. 24 | */ 25 | /*! 26 | * to-regex-range 27 | * 28 | * Copyright (c) 2015-present, Jon Schlinkert. 29 | * Released under the MIT License. 30 | */ 31 | return plugin; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | plugins: 8 | - path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs 9 | spec: "https://mskelton.dev/yarn-outdated/v2" 10 | 11 | yarnPath: .yarn/releases/yarn-4.9.1.cjs 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidance 2 | 3 | We love your input! We want to make contributing to this project as easy and 4 | transparent as possible, whether it's: 5 | 6 | - Reporting a bug 7 | - Discussing the current state of the code 8 | - Submitting a fix 9 | - Proposing new features 10 | - Becoming a maintainer 11 | 12 | ## We develop with GitHub 13 | 14 | We use GitHub to host code, to track issues and feature requests, as well as 15 | accept pull requests. 16 | 17 | ## We use GitHub flow, so all code changes happen through pull requests 18 | 19 | Pull requests are the best way to propose changes to the codebase (we use 20 | [GitHub flow](https://guides.github.com/introduction/flow/index.html)). We 21 | actively welcome your pull requests: 22 | 23 | 1. Fork the repo and create your branch from `main`. 24 | 2. If you've added code that should be tested, add tests. 25 | 3. If you've changed APIs, update the documentation. 26 | 4. Ensure the test suite passes. 27 | 5. Make sure your code lints. 28 | 6. Issue that pull request! 29 | 30 | ## Any contributions you make will be under the MIT Software License 31 | 32 | In short, when you submit code changes, your submissions are understood to be 33 | under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers 34 | the project. Feel free to contact the maintainers if that's a concern. 35 | 36 | ## Report bugs using [GitHub Issues](https://github.com/fabasoad/twilio-voice-call-action/issues) 37 | 38 | We use GitHub issues to track public bugs. Report a bug by opening a new issue. 39 | It's that easy! 40 | 41 | ## Create issue using provided GitHub issue templates 42 | 43 | This repository has issue templates for bug report and feature request. Please 44 | use them to create an issue and fill all required fields. 45 | 46 | ## Use a consistent coding style 47 | 48 | Please follow all the rules from [this](https://google.github.io/styleguide/jsguide.html) 49 | great guide provided by Google for coding style except of following coding styles: 50 | 51 | - File names must be all lowercase and may include dashes (-). 52 | 53 | ## License 54 | 55 | By contributing, you agree that your contributions will be licensed under its 56 | MIT License. 57 | 58 | ## References 59 | 60 | This document was adapted from the open-source contribution guidelines provided 61 | by [briandk](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62). 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2025 Yevhen Fabizhevskyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: audit build clean install reinstall lint test upgrade 2 | 3 | .DEFAULT_GOAL := build 4 | 5 | audit: 6 | @yarn npm audit --all 7 | 8 | build: 9 | @yarn run build 10 | 11 | clean: 12 | @rm -f yarn.lock 13 | @rm -rf node_modules 14 | 15 | install: 16 | @yarn install 17 | 18 | reinstall: clean install 19 | 20 | lint: 21 | @yarn run lint 22 | 23 | test: 24 | @yarn run test 25 | 26 | upgrade: 27 | @yarn upgrade-interactive 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Twilio voice call action 2 | 3 | [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) 4 | ![Releases](https://img.shields.io/github/v/release/fabasoad/twilio-voice-call-action?include_prereleases) 5 | ![unit-tests](https://github.com/fabasoad/twilio-voice-call-action/actions/workflows/unit-tests.yml/badge.svg) 6 | ![functional-tests](https://github.com/fabasoad/twilio-voice-call-action/actions/workflows/functional-tests.yml/badge.svg) 7 | ![security](https://github.com/fabasoad/twilio-voice-call-action/actions/workflows/security.yml/badge.svg) 8 | ![linting](https://github.com/fabasoad/twilio-voice-call-action/actions/workflows/linting.yml/badge.svg) 9 | [![codecov](https://codecov.io/gh/fabasoad/twilio-voice-call-action/branch/main/graph/badge.svg?token=50U8UQWIKB)](https://codecov.io/gh/fabasoad/twilio-voice-call-action) 10 | 11 | This action sends Twilio voice call. 12 | 13 | ## Supported OS 14 | 15 | 16 | | OS | | 17 | |---------|--------------------| 18 | | Windows | :white_check_mark: | 19 | | Linux | :white_check_mark: | 20 | | macOS | :white_check_mark: | 21 | 22 | 23 | ## Prerequisites 24 | 25 | Sign up to [Twilio](https://twilio.com) official web page. Then [register a new 26 | number](https://www.twilio.com/console/voice/numbers) to use it as `from` parameter. 27 | If you use free trial account you have to [add verified phone number](https://support.twilio.com/hc/en-us/articles/223180048-Adding-a-Verified-Phone-Number-or-Caller-ID-with-Twilio) 28 | to use it as `to` parameter. Account SID and Auth token you can find on a [Dashboard 29 | page](https://www.twilio.com/console). 30 | 31 | ## Inputs 32 | 33 | ```yaml 34 | - uses: fabasoad/twilio-voice-call-action@v1 35 | with: 36 | # (Required) Twilio Account SID. 37 | twilio_account_sid: "${{ secrets.TWILIO_ACCOUNT_SID }}" 38 | # (Required) Twilio Auth token. 39 | twilio_auth_token: "${{ secrets.TWILIO_AUTH_TOKEN }}" 40 | # (Required) Text that will be sent by voice call. 41 | text: "GitHub actions build number ${{ github.run_number }} passed successfully." 42 | # (Required) Phone number in your Twilio account to send the voice call from. 43 | from: '+1(123)4567890' 44 | # (Required) Phone number to send the voice call to. 45 | to: '+1(123)4567809' 46 | # (Optional) Call voice. Possible values: man, woman, alice. Defaults to "alice". 47 | voice: "alice" 48 | # (Optional) Twilio log level. Defaults to "debug". 49 | twilio_log_level: "debug" 50 | ``` 51 | 52 | ## Outputs 53 | 54 | None. 55 | 56 | ## Example phone call 57 | 58 | Download [example.mp3](https://raw.githubusercontent.com/fabasoad/twilio-voice-call-action/main/example.mp3) 59 | file to listen. 60 | 61 | ## Contributions 62 | 63 | ![Alt](https://repobeats.axiom.co/api/embed/379948bad849c09fd6752bbdcd4aec28c0c25617.svg "Repobeats analytics image") 64 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Twilio Voice Call 3 | author: Yevhen Fabizhevskyi 4 | description: This GitHub action sends voice call with the defined text. 5 | branding: 6 | icon: phone-incoming 7 | color: white 8 | inputs: 9 | twilio_account_sid: 10 | description: Twilio Account SID. 11 | required: true 12 | twilio_auth_token: 13 | description: Twilio Auth token. 14 | required: true 15 | text: 16 | description: Text that will be sent by voice call. 17 | required: true 18 | from: 19 | description: Phone number in your Twilio account to send the voice call from. 20 | required: true 21 | to: 22 | description: Phone number to send the voice call to. 23 | required: true 24 | voice: 25 | description: "Call voice. Possible values: man, woman, alice." 26 | required: false 27 | default: alice 28 | twilio_log_level: 29 | description: Twilio log level. 30 | required: false 31 | default: debug 32 | runs: 33 | using: node20 34 | main: dist/index.js 35 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "linter": { 3 | "enabled": true, 4 | "rules": { 5 | "recommended": false, 6 | "a11y": { "noBlankTarget": "error" }, 7 | "complexity": { 8 | "noExtraBooleanCast": "error", 9 | "noMultipleSpacesInRegularExpressionLiterals": "error", 10 | "noUselessCatch": "error", 11 | "noWith": "error" 12 | }, 13 | "correctness": { 14 | "noChildrenProp": "error", 15 | "noConstAssign": "error", 16 | "noConstantCondition": "error", 17 | "noEmptyCharacterClassInRegex": "error", 18 | "noEmptyPattern": "error", 19 | "noGlobalObjectCalls": "error", 20 | "noInnerDeclarations": "error", 21 | "noInvalidConstructorSuper": "error", 22 | "noNewSymbol": "error", 23 | "noNonoctalDecimalEscape": "error", 24 | "noPrecisionLoss": "error", 25 | "noSelfAssign": "error", 26 | "noSetterReturn": "error", 27 | "noSwitchDeclarations": "error", 28 | "noUndeclaredVariables": "error", 29 | "noUnreachable": "error", 30 | "noUnreachableSuper": "error", 31 | "noUnsafeFinally": "error", 32 | "noUnsafeOptionalChaining": "error", 33 | "noUnusedLabels": "error", 34 | "noUnusedVariables": "off", 35 | "useIsNan": "error", 36 | "useJsxKeyInIterable": "error", 37 | "useValidForDirection": "error", 38 | "useYield": "error" 39 | }, 40 | "security": { "noDangerouslySetInnerHtml": "error" }, 41 | "style": { 42 | "noArguments": "error", 43 | "noVar": "error", 44 | "useBlockStatements": "error", 45 | "useConst": "error", 46 | "useSingleVarDeclarator": "error" 47 | }, 48 | "suspicious": { 49 | "noAsyncPromiseExecutor": "error", 50 | "noCatchAssign": "error", 51 | "noClassAssign": "error", 52 | "noCommentText": "error", 53 | "noCompareNegZero": "error", 54 | "noControlCharactersInRegex": "error", 55 | "noDebugger": "error", 56 | "noDuplicateCase": "error", 57 | "noDuplicateClassMembers": "error", 58 | "noDuplicateJsxProps": "error", 59 | "noDuplicateObjectKeys": "error", 60 | "noDuplicateParameters": "error", 61 | "noEmptyBlockStatements": "error", 62 | "noFallthroughSwitchClause": "error", 63 | "noFunctionAssign": "error", 64 | "noGlobalAssign": "error", 65 | "noImportAssign": "error", 66 | "noMisleadingCharacterClass": "error", 67 | "noPrototypeBuiltins": "error", 68 | "noRedeclare": "error", 69 | "noShadowRestrictedNames": "error", 70 | "noUnsafeNegation": "error", 71 | "useGetterReturn": "error", 72 | "useValidTypeof": "error" 73 | } 74 | }, 75 | "ignore": ["dist/**"] 76 | }, 77 | "javascript": { 78 | "globals": ["Atomics", "SharedArrayBuffer", "jest", "afterEach", "beforeAll", "beforeEach", "expect", "test", "describe"] 79 | }, 80 | "overrides": [{ "include": ["**/*.spec.ts", "**/*.spec.tsx"] }] 81 | } 82 | -------------------------------------------------------------------------------- /example.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabasoad/twilio-voice-call-action/db5efee4f908c4513b4cf1a33218a74de0cca2aa/example.mp3 -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "clearMocks": true, 3 | "collectCoverageFrom": [ 4 | "**/*.ts", 5 | "!**/*.d.ts", 6 | "!**/node_modules/**" 7 | ], 8 | "coverageReporters": [ 9 | "lcov", "text", "text-summary" 10 | ], 11 | "coverageThreshold": { 12 | "global": { 13 | "branches": 100, 14 | "functions": 100, 15 | "lines": 100, 16 | "statements": 100 17 | } 18 | }, 19 | "moduleFileExtensions": [ 20 | "js", 21 | "ts" 22 | ], 23 | "testEnvironment": "node", 24 | "testMatch": [ 25 | "**/*.spec.ts" 26 | ], 27 | "testRunner": "jest-circus/runner", 28 | "testTimeout": 20000, 29 | "transform": { 30 | "^.+\\.ts$": "ts-jest" 31 | }, 32 | "verbose": true 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twilio-voice-call-action", 3 | "version": "2.1.2", 4 | "description": "This GitHub action sends voice call with the defined text.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "ncc build -m src/index.ts", 8 | "lint": "biome lint --write src", 9 | "test": "jest --config=jest.config.json --json --outputFile=jest-report.json --coverage" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fabasoad/twilio-voice-call-action.git" 14 | }, 15 | "keywords": [ 16 | "actions", 17 | "twilio", 18 | "voip", 19 | "call", 20 | "phone" 21 | ], 22 | "author": "Yevhen Fabizhevskyi", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/fabasoad/twilio-voice-call-action/issues" 26 | }, 27 | "homepage": "https://github.com/fabasoad/twilio-voice-call-action#readme", 28 | "dependencies": { 29 | "@actions/core": "1.11.1", 30 | "@actions/github": "6.0.1", 31 | "twilio": "5.7.0" 32 | }, 33 | "devDependencies": { 34 | "@biomejs/biome": "1.9.4", 35 | "@types/jest": "29.5.14", 36 | "@vercel/ncc": "0.38.3", 37 | "jest": "29.7.0", 38 | "jest-circus": "30.0.0-beta.3", 39 | "ts-jest": "29.3.4", 40 | "typescript": "5.8.3" 41 | }, 42 | "packageManager": "yarn@4.7.0" 43 | } 44 | -------------------------------------------------------------------------------- /src/TwilioClient.ts: -------------------------------------------------------------------------------- 1 | import { Twilio } from 'twilio' 2 | 3 | export class TwilioVoiceNotSupportedException extends Error { 4 | constructor(voice: string, supportedVoices: string[]) { 5 | super(`'${voice}' voice is not supported. Possible values: ${supportedVoices.join(', ')}.`); 6 | } 7 | } 8 | 9 | export type TwilioClientProps = { 10 | accountSid: string, 11 | authToken: string, 12 | logLevel: string 13 | } 14 | 15 | export class TwilioClient { 16 | private readonly client: Twilio 17 | private readonly supportedVoices: string[] 18 | 19 | constructor({ accountSid, authToken, logLevel }: TwilioClientProps) { 20 | this.client = new Twilio(accountSid, authToken, { 21 | lazyLoading: true, 22 | logLevel 23 | }) 24 | this.supportedVoices = ['man', 'woman', 'alice'] 25 | } 26 | 27 | async call(voice: string, text: string, from: string, to: string): Promise { 28 | if (this.supportedVoices.includes(voice)) { 29 | await this.client.calls.create({ 30 | from: from, 31 | to: to, 32 | twiml: `${text}` 33 | }); 34 | } else { 35 | throw new TwilioVoiceNotSupportedException(voice, this.supportedVoices) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/TwilioClient.spec.ts: -------------------------------------------------------------------------------- 1 | import { TwilioClient, TwilioVoiceNotSupportedException } from '../TwilioClient' 2 | 3 | const twilioFn = jest.fn() 4 | const twilioCallsCreateFn = jest.fn() 5 | 6 | jest.mock('twilio', () => ({ 7 | Twilio: jest.fn((...ctrArgs: any[]) => { 8 | twilioFn(...ctrArgs) 9 | return { 10 | calls: { 11 | create: jest.fn((...createArgs: any[]) => 12 | twilioCallsCreateFn(...createArgs) 13 | ) 14 | } 15 | } 16 | }) 17 | })) 18 | 19 | describe('Twilio Client', () => { 20 | const expectedAccountSid = 'account-sid-test' 21 | const expectedAuthToken = 'auth-token-test' 22 | const expectedLogLevel = 'log-level-test' 23 | const expectedText = 'text-test' 24 | const expectedFrom = 'from-test' 25 | const expectedTo = 'to-test' 26 | 27 | describe('Positive scenarios', () => { 28 | test.each(['man', 'woman', 'alice'])('when %s voice is set then should pass', async (expectedVoice: string) => { 29 | // Act 30 | const twilioClient = new TwilioClient({ accountSid: expectedAccountSid, authToken: expectedAuthToken, logLevel: expectedLogLevel }) 31 | await twilioClient.call(expectedVoice, expectedText, expectedFrom, expectedTo) 32 | // Assert 33 | expect(twilioFn).toHaveBeenCalledWith( 34 | expectedAccountSid, expectedAuthToken, { lazyLoading: true, logLevel: expectedLogLevel } 35 | ) 36 | expect(twilioCallsCreateFn).toHaveBeenCalledWith({ 37 | from: expectedFrom, 38 | to: expectedTo, 39 | twiml: `${expectedText}` 40 | }) 41 | }) 42 | }) 43 | 44 | describe('Negative scenarios', () => { 45 | test('when unsupported voice is set then should throw error', async () => { 46 | // Act 47 | const twilioClient = new TwilioClient({ accountSid: expectedAccountSid, authToken: expectedAuthToken, logLevel: expectedLogLevel }) 48 | const fnCall = () => twilioClient.call( 49 | 'unsupported', expectedText, expectedFrom, expectedTo 50 | ) 51 | // Assert 52 | expect(twilioFn).toHaveBeenCalledWith( 53 | expectedAccountSid, expectedAuthToken, { lazyLoading: true, logLevel: expectedLogLevel } 54 | ) 55 | await expect(fnCall).rejects.toThrow(TwilioVoiceNotSupportedException) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | const setFailedFn = jest.fn() 2 | const twilioClientCallFn = jest.fn() 3 | 4 | import run from '../index' 5 | import { TwilioClient } from '../TwilioClient' 6 | 7 | jest.mock('@actions/core', () => ({ 8 | getInput: jest.fn((name: string) => { 9 | switch (name) { 10 | case 'twilio_account_sid': return 'account-sid-test' 11 | case 'twilio_auth_token': return 'auth-token-test' 12 | case 'twilio_log_level': return 'log-level-test' 13 | case 'voice': return 'voice-test' 14 | case 'text': return 'text-test' 15 | case 'from': return 'from-test' 16 | case 'to': return 'to-test' 17 | default: return '' 18 | } 19 | }), 20 | setFailed: jest.fn((message: string) => setFailedFn(message)) 21 | })) 22 | 23 | jest.mock('../TwilioClient', () => ({ 24 | TwilioClient: jest.fn(() => ({ 25 | call: jest.fn((voice: string, text: string, from: string, to: string) => 26 | twilioClientCallFn(voice, text, from, to) 27 | ) 28 | })) 29 | })) 30 | 31 | describe('Main entrypoint', () => { 32 | describe('Positive scenarios', () => { 33 | test('when all set then should not fail', () => { 34 | // Arrange 35 | twilioClientCallFn.mockImplementation( 36 | (voice: string, text: string, from: string, to: string) => { 37 | expect(voice).toEqual('voice-test') 38 | expect(text).toEqual('text-test') 39 | expect(from).toEqual('from-test') 40 | expect(to).toEqual('to-test') 41 | return Promise.resolve() 42 | } 43 | ) 44 | // Act 45 | run() 46 | // Assert 47 | expect(TwilioClient).toHaveBeenCalledWith({ 48 | accountSid: 'account-sid-test', 49 | authToken: 'auth-token-test', 50 | logLevel: 'log-level-test' 51 | }) 52 | expect(setFailedFn).not.toHaveBeenCalled() 53 | }) 54 | }) 55 | 56 | describe('Negative scenarios', () => { 57 | test('when Twilio client throws error then should fail the CI', () => { 58 | // Arrange 59 | const expectedErrorMessage = 'error-test' 60 | twilioClientCallFn.mockImplementation(() => { 61 | throw new Error(expectedErrorMessage) 62 | }) 63 | // Act 64 | run() 65 | // Assert 66 | expect(TwilioClient).toHaveBeenCalledWith({ 67 | accountSid: 'account-sid-test', 68 | authToken: 'auth-token-test', 69 | logLevel: 'log-level-test' 70 | }) 71 | expect(setFailedFn).toHaveBeenCalledWith(expectedErrorMessage) 72 | }) 73 | }) 74 | 75 | afterEach(() => { 76 | setFailedFn.mockClear() 77 | twilioClientCallFn.mockClear() 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { getInput, setFailed } from '@actions/core' 2 | import { TwilioClient } from './TwilioClient' 3 | 4 | export default async function run() { 5 | const twilioClient = new TwilioClient({ 6 | accountSid: getInput('twilio_account_sid', { required: true, trimWhitespace: true }), 7 | authToken: getInput('twilio_auth_token', { required: true, trimWhitespace: true }), 8 | logLevel: getInput('twilio_log_level', { required: false, trimWhitespace: true }) 9 | }) 10 | try { 11 | await twilioClient.call( 12 | getInput('voice', { required: false, trimWhitespace: true }), 13 | getInput('text', { required: true, trimWhitespace: true }), 14 | getInput('from', { required: true, trimWhitespace: true }), 15 | getInput('to', { required: true, trimWhitespace: true }) 16 | ) 17 | } catch (e) { 18 | setFailed((e).message) 19 | } 20 | } 21 | 22 | run() 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2024", 4 | "module": "commonjs", 5 | "lib": [ 6 | "esnext" 7 | ], 8 | "baseUrl": "./", 9 | "paths": { 10 | "*": ["src/types/*"] 11 | }, 12 | "moduleResolution": "node", 13 | "outDir": "./lib", 14 | "rootDir": "./src", 15 | "strict": true, 16 | "noImplicitAny": false, 17 | "esModuleInterop": true 18 | }, 19 | "exclude": ["node_modules", "src/__tests__"] 20 | } 21 | --------------------------------------------------------------------------------