├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── funding.yml ├── stale.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg ├── pre-commit └── pre-push ├── .lintstagedrc.js ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .releaserc.js ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── jest.config.js ├── jest.setup.js ├── lib ├── __tests__ │ └── twilio.module.test.ts ├── index.ts ├── module │ ├── index.ts │ ├── twilio.module.ts │ └── twilio.service.ts └── utils │ ├── index.ts │ ├── twilio.interface.ts │ ├── twilio.module-definition.ts │ └── twilio.utils.ts ├── nest-cli.json ├── package.json ├── pnpm-lock.yaml ├── renovate.json ├── tsconfig.build.json ├── tsconfig.json └── tsconfig.test.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | root = true 5 | 6 | [*] 7 | # We recommend you to keep these unchanged 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # TESTING ENV 2 | 3 | TWILIO_ACCOUNT_SID= 4 | TWILIO_AUTH_TOKEN= 5 | TWILIO_PHONE_NUMBER= 6 | TWILIO_TARGET_PHONE_NUMBER= -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 3 | parserOptions: { 4 | ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features 5 | sourceType: 'module', // Allows for the use of imports 6 | ecmaFeatures: { 7 | jsx: true, // Allows for the parsing of JSX 8 | }, 9 | }, 10 | extends: [ 11 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 12 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 13 | ], 14 | rules: { 15 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Create a bug report 4 | --- 5 | 6 | # Bug Report 7 | 8 | ## Describe the Bug 9 | 10 | A clear and concise description of what the bug is. 11 | 12 | ## How to Reproduce 13 | 14 | Steps to reproduce the behavior, please provide code snippets or a repository: 15 | 16 | 1. Go to '....' 17 | 2. Click on '....' 18 | 3. See error 19 | 20 | ## Expected Behavior 21 | 22 | Tell me what should happen. 23 | 24 | ## Error 25 | 26 | Error resulted by the potential bug. 27 | 28 | ## Your Environment 29 | 30 | - OS: [e.g. macOS, Windows] 31 | - Version of nestjs-twilio: [e.g. v1.0.0] 32 | 33 | ## Error reproducing steps 34 | 35 | Please explain how did your error ocurr, you can also leave gists, repos or any kind of codebase. 36 | 37 | ## Additional Information 38 | 39 | Any other information about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature Request 3 | about: Create a feature request 4 | --- 5 | 6 | # Feature Request 7 | 8 | ## Describe the Feature 9 | 10 | A clear and concise description of what you want and what your use case is. 11 | 12 | ## Describe the Solution you'd like 13 | 14 | A clear and concise description of what you want to happen. 15 | 16 | ## Describe alternatives you've considered 17 | 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | ## Additional Information 21 | 22 | Any other information about the feature here. 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ## What 14 | 15 | What changes are being made? (e.g. feature, bug, docs etc.) 16 | 17 | ## Why 18 | 19 | Why are these changes necessary? 20 | 21 | ## How 22 | 23 | How were these changes implemented? 24 | 25 | ## Checklist 26 | 27 | Have you done all of these things? 28 | 29 | 30 | 31 | 32 | - [ ] Documentation added 33 | - [ ] Tests 34 | - [ ] Typescript definitions updated 35 | - [ ] Ready to be merged 36 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective project name. 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://www.paypal.me/rejvban"] 13 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 30 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - security 16 | 17 | # Set to true to ignore issues in a project (defaults to false) 18 | exemptProjects: false 19 | 20 | # Set to true to ignore issues in a milestone (defaults to false) 21 | exemptMilestones: false 22 | 23 | # Set to true to ignore issues with an assignee (defaults to false) 24 | exemptAssignees: false 25 | 26 | # Label to use when marking as stale 27 | staleLabel: stale 28 | 29 | # Comment to post when marking as stale. Set to `false` to disable 30 | markComment: > 31 | This issue has been automatically marked as stale because it has not had 32 | recent activity. It will be closed if no further activity occurs. Thank you 33 | for your contributions. 34 | 35 | # Comment to post when removing the stale label. 36 | # unmarkComment: > 37 | # Your comment here. 38 | 39 | # Comment to post when closing a stale Issue or Pull Request. 40 | # closeComment: > 41 | # Your comment here. 42 | 43 | # Limit the number of actions per hour, from 1-30. Default is 30 44 | limitPerRun: 30 45 | 46 | # Limit to only `issues` or `pulls` 47 | # only: issues 48 | 49 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 50 | # pulls: 51 | # daysUntilStale: 30 52 | # markComment: > 53 | # This pull request has been automatically marked as stale because it has not had 54 | # recent activity. It will be closed if no further activity occurs. Thank you 55 | # for your contributions. 56 | 57 | issues: 58 | exemptLabels: 59 | - confirmed 60 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: ['**'] 4 | workflow_dispatch: 5 | 6 | jobs: 7 | install: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | 13 | - name: Install Node.js 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: 20 17 | 18 | - name: Install pnpm 19 | uses: pnpm/action-setup@v2 20 | with: 21 | version: 8 22 | run_install: false 23 | 24 | - name: Get pnpm store directory 25 | shell: bash 26 | run: | 27 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 28 | 29 | - name: Setup pnpm cache 30 | uses: actions/cache@v3 31 | with: 32 | path: ${{ env.STORE_PATH }} 33 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 34 | restore-keys: | 35 | ${{ runner.os }}-pnpm-store- 36 | 37 | - name: Install dependencies 38 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 39 | 40 | type-check: 41 | needs: install 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v3 46 | 47 | - name: Install Node.js 48 | uses: actions/setup-node@v3 49 | with: 50 | node-version: 20 51 | 52 | - name: Install pnpm 53 | uses: pnpm/action-setup@v2 54 | with: 55 | version: 8 56 | run_install: false 57 | 58 | - name: Get pnpm store directory 59 | shell: bash 60 | run: | 61 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 62 | 63 | - name: Restore pnpm cache 64 | id: cache 65 | uses: actions/cache/restore@v3 66 | with: 67 | path: ${{ env.STORE_PATH }} 68 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 69 | fail-on-cache-miss: true 70 | 71 | - name: Install dependencies 72 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 73 | 74 | - name: Running type-check 75 | run: pnpm type-check 76 | 77 | lint: 78 | needs: install 79 | runs-on: ubuntu-latest 80 | steps: 81 | - name: Checkout 82 | uses: actions/checkout@v3 83 | 84 | - name: Install Node.js 85 | uses: actions/setup-node@v3 86 | with: 87 | node-version: 20 88 | 89 | - name: Install pnpm 90 | uses: pnpm/action-setup@v2 91 | with: 92 | version: 8 93 | run_install: false 94 | 95 | - name: Get pnpm store directory 96 | shell: bash 97 | run: | 98 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 99 | 100 | - name: Restore pnpm cache 101 | id: cache 102 | uses: actions/cache/restore@v3 103 | with: 104 | path: ${{ env.STORE_PATH }} 105 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 106 | fail-on-cache-miss: true 107 | 108 | - name: Install dependencies 109 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 110 | 111 | - name: Running lint 112 | run: pnpm lint 113 | 114 | test: 115 | needs: install 116 | runs-on: ubuntu-latest 117 | steps: 118 | - name: Checkout 119 | uses: actions/checkout@v3 120 | 121 | - name: Install Node.js 122 | uses: actions/setup-node@v3 123 | with: 124 | node-version: 20 125 | 126 | - name: Install pnpm 127 | uses: pnpm/action-setup@v2 128 | with: 129 | version: 8 130 | run_install: false 131 | 132 | - name: Get pnpm store directory 133 | shell: bash 134 | run: | 135 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 136 | 137 | - name: Restore pnpm cache 138 | id: cache 139 | uses: actions/cache/restore@v3 140 | with: 141 | path: ${{ env.STORE_PATH }} 142 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 143 | fail-on-cache-miss: true 144 | 145 | - name: Install dependencies 146 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 147 | 148 | - name: Running test 149 | run: pnpm test:run 150 | env: 151 | TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} 152 | TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} 153 | TWILIO_PHONE_NUMBER: ${{ secrets.TWILIO_PHONE_NUMBER }} 154 | TWILIO_TARGET_PHONE_NUMBER: ${{ secrets.TWILIO_TARGET_PHONE_NUMBER }} 155 | 156 | build: 157 | needs: install 158 | runs-on: ubuntu-latest 159 | steps: 160 | - name: Checkout 161 | uses: actions/checkout@v3 162 | 163 | - name: Install Node.js 164 | uses: actions/setup-node@v3 165 | with: 166 | node-version: 20 167 | 168 | - name: Install pnpm 169 | uses: pnpm/action-setup@v2 170 | with: 171 | version: 8 172 | run_install: false 173 | 174 | - name: Get pnpm store directory 175 | shell: bash 176 | run: | 177 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 178 | 179 | - name: Restore pnpm cache 180 | id: cache 181 | uses: actions/cache/restore@v3 182 | with: 183 | path: ${{ env.STORE_PATH }} 184 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 185 | fail-on-cache-miss: true 186 | 187 | - name: Install dependencies 188 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 189 | 190 | - name: Building dist 191 | run: pnpm build:dist 192 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | issues: write 17 | pull-requests: write 18 | id-token: write 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Install Node.js 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: 20 27 | 28 | - name: Install pnpm 29 | uses: pnpm/action-setup@v2 30 | with: 31 | version: 8 32 | run_install: false 33 | 34 | - name: Install dependencies 35 | run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts 36 | 37 | - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies 38 | run: pnpm audit 39 | 40 | - name: Build 41 | run: pnpm build:dist 42 | 43 | - name: Release 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 47 | run: npx semantic-release 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | 36 | # ENV 37 | .env -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm lint-staged 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm test:run 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,ts,tsx}': 'eslint --fix', 3 | '*.{json,md,yml}': 'prettier --write', 4 | }; 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.12.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 80, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /.releaserc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('semantic-release').GlobalConfig} 3 | */ 4 | module.exports = { 5 | branches: ['master'], 6 | plugins: [ 7 | '@semantic-release/commit-analyzer', 8 | '@semantic-release/release-notes-generator', 9 | '@semantic-release/npm', 10 | '@semantic-release/git', 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This project adheres to [Semantic Versioning](http://semver.org). 4 | Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/rejvban/twilio-nestjs/releases) page 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [INSERT CONTACT METHOD]. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Lazar Karic 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | nestjs-twilio 4 |

5 | 6 |

7 | 8 |

9 | 10 |

11 | Injectable Twilio client for Nestjs. 12 |

13 |

14 | 15 | [![npm version](https://img.shields.io/npm/v/nestjs-twilio)](https://www.npmjs.com/package/nestjs-twilio) 16 | [![miniziped size](https://badgen.net/bundlephobia/minzip/nestjs-twilio)](https://bundlephobia.com/result?p=nestjs-twilio) 17 | [![tree shaking](https://badgen.net/bundlephobia/tree-shaking/react-colorful)](https://github.com/lkaric/nestjs-twilio) 18 | [![MIT licensed](https://img.shields.io/github/license/rejvban/nestjs-twilio)](https://raw.githubusercontent.com/lkaric/nestjs-twilio/master/LICENSE) 19 | 20 | Implementing the `TwilioModule` from this package you gain access to Twilio client through dependency injection with minimal setup. 21 | 22 | ## Instalation 23 | 24 | ```bash 25 | $ npm install --save nestjs-twilio 26 | ``` 27 | 28 | ```bash 29 | $ yarn add nestjs-twilio 30 | ``` 31 | 32 | ## Getting Started 33 | 34 | To use Twilio client we need to register module for example in app.module.ts 35 | 36 | ```typescript 37 | import { TwilioModule } from 'nestjs-twilio'; 38 | 39 | @Module({ 40 | imports: [ 41 | TwilioModule.forRoot({ 42 | accountSid: process.env.TWILIO_ACCOUNT_SID, 43 | authToken: process.env.TWILIO_AUTH_TOKEN, 44 | }), 45 | ], 46 | }) 47 | export class AppModule {} 48 | ``` 49 | 50 | If you are using the `@nestjs/config package` from nest, you can use the `ConfigModule` using the `registerAsync()` function to inject your environment variables like this in your custom module: 51 | 52 | ```typescript 53 | import { TwilioModule } from 'nestjs-twilio'; 54 | 55 | @Module({ 56 | imports: [ 57 | TwilioModule.forRootAsync({ 58 | imports: [ConfigModule], 59 | useFactory: (cfg: ConfigService) => ({ 60 | accountSid: cfg.get('TWILIO_ACCOUNT_SID'), 61 | authToken: cfg.get('TWILIO_AUTH_TOKEN'), 62 | }), 63 | inject: [ConfigService], 64 | }), 65 | ], 66 | }) 67 | export class AppModule {} 68 | ``` 69 | 70 | Example usage in service. 71 | 72 | ```typescript 73 | import { InjectTwilio, TwilioService } from 'nestjs-twilio'; 74 | 75 | @Injectable() 76 | export class AppService { 77 | public constructor(private readonly twilioService: TwilioService) {} 78 | 79 | async sendSMS() { 80 | return this.twilioService.client.messages.create({ 81 | body: 'SMS Body, sent to the phone!', 82 | from: TWILIO_PHONE_NUMBER, 83 | to: TARGET_PHONE_NUMBER, 84 | }); 85 | } 86 | } 87 | ``` 88 | 89 | For full Client API see Twilio Node SDK reference [here](https://www.twilio.com/docs/libraries/node) 90 | 91 | ## Testing 92 | 93 | Example of testing can be found [here](https://github.com/lkaric/nestjs-twilio/blob/master/lib/__tests__/twilio.module.test.ts). 94 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | transform: { 4 | '.test.ts$': [ 5 | 'ts-jest', 6 | { 7 | tsconfig: './tsconfig.test.json', 8 | }, 9 | ], 10 | }, 11 | testRegex: '.test.ts$', 12 | collectCoverageFrom: ['lib/**/*.*.ts'], 13 | setupFiles: ['./jest.setup.js'], 14 | testEnvironment: 'node', 15 | }; 16 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | require('dotenv').config(); 3 | 4 | const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN } = process.env; 5 | 6 | if (!TWILIO_ACCOUNT_SID || !TWILIO_AUTH_TOKEN) 7 | throw new Error('No testing authorization provided in env!'); 8 | -------------------------------------------------------------------------------- /lib/__tests__/twilio.module.test.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | 3 | import { TwilioModule, TwilioService } from '../module'; 4 | 5 | import { OPTIONS_TYPE } from '../utils'; 6 | 7 | describe('TwiliModule', () => { 8 | const { 9 | TWILIO_ACCOUNT_SID, 10 | TWILIO_AUTH_TOKEN, 11 | TWILIO_PHONE_NUMBER, 12 | TWILIO_TARGET_PHONE_NUMBER, 13 | } = process.env; 14 | 15 | if (!TWILIO_PHONE_NUMBER) 16 | throw new Error('No Twilio phone number defined in `.env`!'); 17 | if (!TWILIO_TARGET_PHONE_NUMBER) 18 | throw new Error('No testing target phone number defined in `.env`!'); 19 | 20 | const config: typeof OPTIONS_TYPE = { 21 | accountSid: TWILIO_ACCOUNT_SID, 22 | authToken: TWILIO_AUTH_TOKEN, 23 | }; 24 | 25 | describe('forRoot', () => { 26 | let twilioService: TwilioService; 27 | 28 | beforeEach(async () => { 29 | const module = await Test.createTestingModule({ 30 | imports: [TwilioModule.forRoot(config)], 31 | }).compile(); 32 | 33 | twilioService = module.get(TwilioService); 34 | }); 35 | 36 | it('should provide sentry client', () => { 37 | expect(twilioService).toBeDefined(); 38 | }); 39 | 40 | it('should send a test sms to the phone number defined in env', async () => { 41 | const response = await twilioService.client.messages.create({ 42 | body: 'Automated testing of https://www.github.com/rejvban/nestjs-twilio forRoot', 43 | from: TWILIO_PHONE_NUMBER, 44 | to: TWILIO_TARGET_PHONE_NUMBER, 45 | }); 46 | 47 | expect(response).toBeDefined(); 48 | }); 49 | }); 50 | 51 | describe('forRootAsync with useFactory', () => { 52 | let twilioService: TwilioService; 53 | 54 | beforeEach(async () => { 55 | const module = await Test.createTestingModule({ 56 | imports: [ 57 | TwilioModule.forRootAsync({ 58 | useFactory: () => config, 59 | }), 60 | ], 61 | }).compile(); 62 | 63 | twilioService = module.get(TwilioService); 64 | }); 65 | 66 | it('should provide sentry client', () => { 67 | expect(twilioService).toBeDefined(); 68 | }); 69 | 70 | it('should send a test sms to the phone number defined in env', async () => { 71 | const response = await twilioService.client.messages.create({ 72 | body: 'Automated testing of https://www.github.com/rejvban/nestjs-twilio forRootAsync with useFactory', 73 | from: TWILIO_PHONE_NUMBER, 74 | to: TWILIO_TARGET_PHONE_NUMBER, 75 | }); 76 | 77 | expect(response).toBeDefined(); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './module'; 2 | export * from './utils/twilio.interface'; 3 | -------------------------------------------------------------------------------- /lib/module/index.ts: -------------------------------------------------------------------------------- 1 | export * from './twilio.module'; 2 | export * from './twilio.service'; 3 | -------------------------------------------------------------------------------- /lib/module/twilio.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { ConfigurableModuleClass } from '../utils'; 4 | 5 | import { TwilioService } from './twilio.service'; 6 | 7 | @Module({ 8 | providers: [TwilioService], 9 | exports: [TwilioService], 10 | }) 11 | export class TwilioModule extends ConfigurableModuleClass {} 12 | -------------------------------------------------------------------------------- /lib/module/twilio.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | 3 | import { 4 | createTwilioClient, 5 | MODULE_OPTIONS_TOKEN, 6 | OPTIONS_TYPE, 7 | } from '../utils'; 8 | 9 | import type { TwilioClient } from '../utils'; 10 | 11 | @Injectable() 12 | export class TwilioService { 13 | private readonly twilioSdk: TwilioClient; 14 | 15 | constructor( 16 | @Inject(MODULE_OPTIONS_TOKEN) private options: typeof OPTIONS_TYPE, 17 | ) { 18 | this.twilioSdk = createTwilioClient(this.options); 19 | } 20 | 21 | public get client(): TwilioClient { 22 | return this.twilioSdk; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './twilio.module-definition'; 2 | export * from './twilio.interface'; 3 | export * from './twilio.utils'; 4 | -------------------------------------------------------------------------------- /lib/utils/twilio.interface.ts: -------------------------------------------------------------------------------- 1 | import Twilio from 'twilio/lib/rest/Twilio'; 2 | 3 | import type { ClientOpts } from 'twilio/lib/base/BaseTwilio'; 4 | 5 | export type TwilioClient = Twilio; 6 | 7 | export interface ExtraConfiguration { 8 | isGlobal?: boolean; 9 | } 10 | export interface TwilioModuleOptions extends ExtraConfiguration { 11 | accountSid: string | undefined; 12 | authToken: string | undefined; 13 | options?: ClientOpts | undefined; 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils/twilio.module-definition.ts: -------------------------------------------------------------------------------- 1 | import { ConfigurableModuleBuilder } from '@nestjs/common'; 2 | 3 | import { ExtraConfiguration, TwilioModuleOptions } from './twilio.interface'; 4 | 5 | export const { 6 | ConfigurableModuleClass, 7 | MODULE_OPTIONS_TOKEN, 8 | OPTIONS_TYPE, 9 | ASYNC_OPTIONS_TYPE, 10 | } = new ConfigurableModuleBuilder() 11 | .setExtras({ isGlobal: false }, (definition, extras) => ({ 12 | ...definition, 13 | global: extras.isGlobal, 14 | })) 15 | .setClassMethodName('forRoot') 16 | .build(); 17 | -------------------------------------------------------------------------------- /lib/utils/twilio.utils.ts: -------------------------------------------------------------------------------- 1 | import { TwilioClient } from './twilio.interface'; 2 | import { OPTIONS_TYPE } from './twilio.module-definition'; 3 | 4 | import { Twilio } from 'twilio'; 5 | 6 | export function createTwilioClient({ 7 | accountSid, 8 | authToken, 9 | options, 10 | }: typeof OPTIONS_TYPE): TwilioClient { 11 | const client = new Twilio(accountSid, authToken, options); 12 | 13 | return client; 14 | } 15 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-twilio", 3 | "version": "4.4.0", 4 | "description": "Injectable Twilio client for Nestjs", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Lazar Karic", 8 | "email": "lazar@mm.st", 9 | "url": "https://lazar.sh" 10 | }, 11 | "repository": "https://github.com/lkaric/nestjs-twilio", 12 | "bugs": "https://github.com/lkaric/nestjs-twilio/issues", 13 | "private": false, 14 | "keywords": [ 15 | "nestjs", 16 | "twilio", 17 | "sms", 18 | "client", 19 | "sdk", 20 | "typescript", 21 | "nestjs-twilio", 22 | "twilio-nestjs" 23 | ], 24 | "source": "lib/index.ts", 25 | "main": "dist/index.js", 26 | "module": "dist/index.esm.js", 27 | "types": "dist/index.d.ts", 28 | "files": [ 29 | "dist" 30 | ], 31 | "scripts": { 32 | "build:dist": "pnpm clean:dist && tsc -p tsconfig.build.json", 33 | "test:run": "NODE_ENV=test jest", 34 | "test:watch": "pnpm test:run --watch", 35 | "test:coverage": "pnpm clean:test && pnpm test:run --coverage", 36 | "type-check": "tsc --noEmit", 37 | "lint": "eslint --fix . --ext .js,.ts,.tsx", 38 | "clean:dist": "rimraf dist", 39 | "clean:test": "rimraf coverage", 40 | "postinstall": "husky install", 41 | "prepublishOnly": "pinst --disable", 42 | "postpublish": "pinst --enable" 43 | }, 44 | "dependencies": { 45 | "twilio": "^5.0.2" 46 | }, 47 | "peerDependencies": { 48 | "@nestjs/common": ">=9.0.0", 49 | "@nestjs/core": ">=9.0.0" 50 | }, 51 | "devDependencies": { 52 | "@commitlint/cli": "^19.2.1", 53 | "@commitlint/config-conventional": "^19.1.0", 54 | "@nestjs/common": "10.3.7", 55 | "@nestjs/core": "10.3.7", 56 | "@nestjs/testing": "10.3.7", 57 | "@semantic-release/changelog": "^6.0.3", 58 | "@semantic-release/git": "^10.0.1", 59 | "@types/jest": "29.5.12", 60 | "@types/node": "20.12.2", 61 | "@typescript-eslint/eslint-plugin": "^7.5.0", 62 | "@typescript-eslint/parser": "^7.5.0", 63 | "dotenv": "^16.4.5", 64 | "eslint": "^8.57.0", 65 | "eslint-config-prettier": "^9.1.0", 66 | "eslint-plugin-prettier": "^5.1.3", 67 | "husky": "^9.0.11", 68 | "jest": "29.7.0", 69 | "lint-staged": "^15.2.2", 70 | "pinst": "^3.0.0", 71 | "prettier": "3.2.5", 72 | "reflect-metadata": "^0.2.2", 73 | "rimraf": "^5.0.5", 74 | "rxjs": "7.8.1", 75 | "semantic-release": "^23.0.6", 76 | "ts-jest": "29.1.2", 77 | "typescript": "5.4.3" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "includeForks": true 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "coverage", "lib/__tests__/*"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "allowSyntheticDefaultImports": true, 7 | "outDir": "dist", 8 | "rootDir": "lib", 9 | "skipLibCheck": true, 10 | "declaration": true, 11 | "sourceMap": false, 12 | "noImplicitAny": true, 13 | "esModuleInterop": true, 14 | "strict": true, 15 | "module": "commonjs" 16 | }, 17 | "include": ["lib/**/*"], 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true 5 | } 6 | } 7 | --------------------------------------------------------------------------------