├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── label.yml │ └── npm-publish.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.js ├── .prettierignore ├── .prettierrc.json ├── .versionrc.js ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── docs └── v1.md ├── jestconfig.json ├── package-lock.json ├── package.json ├── src ├── constants.ts ├── index.ts ├── models │ ├── GistFile.ts │ └── GithubGist.ts └── util │ ├── auth-config.ts │ ├── formatted-gist-identifier.ts │ ├── is-string-empty.ts │ └── token-validity.ts ├── tests └── index.test.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | commitlint.config.js 4 | 5 | .eslintrc.js 6 | 7 | package.json 8 | 9 | package-lock.json 10 | 11 | tsconfig.json 12 | 13 | .husky 14 | 15 | .github 16 | 17 | storybook-static 18 | 19 | dist 20 | 21 | build 22 | 23 | .storybook 24 | 25 | *.svg 26 | 27 | *.css 28 | 29 | jest.config.ts 30 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'standard-with-typescript', 4 | 'prettier', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:import/typescript', 7 | ], 8 | plugins: ['prettier', 'import', '@typescript-eslint'], 9 | parser: "@typescript-eslint/parser", 10 | parserOptions: { 11 | project: "./tsconfig.json" 12 | }, 13 | rules: { 14 | '@typescript-eslint/adjacent-overload-signatures': 'warn', 15 | 'prettier/prettier': 'error', 16 | 'no-unused-vars': 'warn', 17 | 'no-console': 'off', 18 | 'func-names': 'off', 19 | 'no-process-exit': 'off', 20 | 'object-shorthand': 'off', 21 | 'class-methods-use-this': 'off', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Build and Test the package 5 | 6 | on: [pull_request] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: 16 16 | - run: npm install 17 | - run: npm test 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '36 1 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | # Because dependabot adds label automatically 17 | if: ${{ github.actor != 'dependabot[bot]' }} 18 | 19 | permissions: 20 | contents: read 21 | pull-requests: write 22 | 23 | steps: 24 | - uses: actions/labeler@v2 25 | with: 26 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 27 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Publish to NPM 5 | 6 | on: 7 | workflow_dispatch: 8 | schedule: 9 | - cron: '0 0 1 * *' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v2 17 | with: 18 | node-version: 14 19 | - run: npm ci 20 | - run: npm test 21 | 22 | publish-npm: 23 | needs: build 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: 16 30 | registry-url: https://registry.npmjs.org/ 31 | - run: npm ci 32 | - run: | 33 | git config --global user.name 'Vighnesh Raut' 34 | git config --global user.email 'me@vighnesh153.com' 35 | npm run release 36 | env: 37 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | 4 | dist/ 5 | 6 | scratch.ts 7 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo "$1" 5 | echo "$2" 6 | echo "$3" 7 | echo "$4" 8 | ./node_modules/.bin/commitlint --edit "$1" 9 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{ts,tsx}': ['eslint --fix'], 3 | '*.{ts,tsx,json,css}': ['prettier --write --ignore-unknown'], 4 | }; 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | README.md 2 | 3 | CHANGELOG.md 4 | 5 | node_modules 6 | 7 | commitlint.config.js 8 | 9 | .eslintrc.js 10 | 11 | package.json 12 | 13 | package-lock.json 14 | 15 | tsconfig.json 16 | 17 | .husky 18 | 19 | .github 20 | 21 | storybook-static 22 | 23 | dist 24 | 25 | build 26 | 27 | .storybook 28 | 29 | *.svg 30 | 31 | *.css 32 | 33 | jest.config.ts 34 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "arrowParens": "always", 6 | "proseWrap": "always", 7 | "singleQuote": true 8 | } 9 | -------------------------------------------------------------------------------- /.versionrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [2.0.124](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.123...v2.0.124) (2022-10-01) 6 | 7 | ### 2.0.123 (2022-09-01) 8 | 9 | ### [2.0.122](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.121...v2.0.122) (2022-08-22) 10 | 11 | ### [2.0.121](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.120...v2.0.121) (2022-08-19) 12 | 13 | ### [2.0.120](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.119...v2.0.120) (2022-08-17) 14 | 15 | ### [2.0.119](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.118...v2.0.119) (2022-08-15) 16 | 17 | ### [2.0.118](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.117...v2.0.118) (2022-08-12) 18 | 19 | ### [2.0.117](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.116...v2.0.117) (2022-08-10) 20 | 21 | ### [2.0.116](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.115...v2.0.116) (2022-08-08) 22 | 23 | ### [2.0.115](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.114...v2.0.115) (2022-08-05) 24 | 25 | ### [2.0.114](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.113...v2.0.114) (2022-08-03) 26 | 27 | ### [2.0.113](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.112...v2.0.113) (2022-08-01) 28 | 29 | ### [2.0.112](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.111...v2.0.112) (2022-07-29) 30 | 31 | ### [2.0.111](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.110...v2.0.111) (2022-07-27) 32 | 33 | ### 2.0.110 (2022-07-25) 34 | 35 | ### [2.0.109](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.108...v2.0.109) (2022-07-22) 36 | 37 | ### [2.0.108](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.107...v2.0.108) (2022-07-20) 38 | 39 | ### [2.0.107](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.106...v2.0.107) (2022-07-18) 40 | 41 | ### [2.0.106](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.105...v2.0.106) (2022-07-15) 42 | 43 | ### [2.0.105](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.104...v2.0.105) (2022-07-13) 44 | 45 | ### [2.0.104](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.103...v2.0.104) (2022-07-11) 46 | 47 | ### 2.0.103 (2022-07-08) 48 | 49 | ### [2.0.102](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.101...v2.0.102) (2022-07-06) 50 | 51 | ### [2.0.101](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.100...v2.0.101) (2022-07-04) 52 | 53 | ### [2.0.100](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.99...v2.0.100) (2022-07-01) 54 | 55 | ### 2.0.99 (2022-06-29) 56 | 57 | ### 2.0.98 (2022-06-27) 58 | 59 | ### [2.0.97](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.96...v2.0.97) (2022-06-24) 60 | 61 | ### [2.0.96](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.95...v2.0.96) (2022-06-22) 62 | 63 | ### [2.0.95](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.94...v2.0.95) (2022-06-20) 64 | 65 | ### 2.0.94 (2022-06-17) 66 | 67 | ### [2.0.93](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.92...v2.0.93) (2022-06-15) 68 | 69 | ### [2.0.92](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.91...v2.0.92) (2022-06-13) 70 | 71 | ### 2.0.91 (2022-06-10) 72 | 73 | ### 2.0.90 (2022-06-08) 74 | 75 | ### [2.0.89](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.88...v2.0.89) (2022-06-06) 76 | 77 | ### 2.0.88 (2022-06-03) 78 | 79 | ### [2.0.87](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.86...v2.0.87) (2022-06-01) 80 | 81 | ### 2.0.86 (2022-05-30) 82 | 83 | ### 2.0.85 (2022-05-27) 84 | 85 | ### 2.0.84 (2022-05-25) 86 | 87 | ### [2.0.83](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.82...v2.0.83) (2022-05-23) 88 | 89 | ### [2.0.82](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.81...v2.0.82) (2022-05-20) 90 | 91 | ### 2.0.81 (2022-05-18) 92 | 93 | ### [2.0.80](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.79...v2.0.80) (2022-05-16) 94 | 95 | ### 2.0.79 (2022-05-13) 96 | 97 | ### [2.0.78](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.77...v2.0.78) (2022-05-11) 98 | 99 | ### [2.0.77](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.76...v2.0.77) (2022-05-09) 100 | 101 | ### 2.0.76 (2022-05-06) 102 | 103 | ### [2.0.75](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.74...v2.0.75) (2022-05-04) 104 | 105 | ### 2.0.74 (2022-05-02) 106 | 107 | ### 2.0.73 (2022-04-29) 108 | 109 | ### [2.0.72](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.71...v2.0.72) (2022-04-27) 110 | 111 | ### [2.0.71](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.70...v2.0.71) (2022-04-25) 112 | 113 | ### 2.0.70 (2022-04-22) 114 | 115 | ### 2.0.69 (2022-04-20) 116 | 117 | ### [2.0.68](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.67...v2.0.68) (2022-04-18) 118 | 119 | ### [2.0.67](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.66...v2.0.67) (2022-04-15) 120 | 121 | ### [2.0.66](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.65...v2.0.66) (2022-04-13) 122 | 123 | ### [2.0.65](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.64...v2.0.65) (2022-04-11) 124 | 125 | ### 2.0.64 (2022-04-08) 126 | 127 | ### 2.0.63 (2022-04-06) 128 | 129 | ### [2.0.62](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.61...v2.0.62) (2022-04-04) 130 | 131 | ### 2.0.61 (2022-04-01) 132 | 133 | ### 2.0.60 (2022-03-30) 134 | 135 | ### 2.0.59 (2022-03-28) 136 | 137 | ### [2.0.58](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.57...v2.0.58) (2022-03-25) 138 | 139 | ### [2.0.57](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.56...v2.0.57) (2022-03-23) 140 | 141 | ### 2.0.56 (2022-03-21) 142 | 143 | ### 2.0.55 (2022-03-18) 144 | 145 | ### 2.0.54 (2022-03-16) 146 | 147 | ### [2.0.53](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.52...v2.0.53) (2022-03-14) 148 | 149 | ### 2.0.52 (2022-03-11) 150 | 151 | ### 2.0.51 (2022-03-09) 152 | 153 | ### 2.0.50 (2022-03-07) 154 | 155 | ### 2.0.49 (2022-03-04) 156 | 157 | ### 2.0.48 (2022-03-02) 158 | 159 | ### 2.0.47 (2022-02-28) 160 | 161 | ### [2.0.46](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.45...v2.0.46) (2022-02-25) 162 | 163 | ### 2.0.45 (2022-02-23) 164 | 165 | ### [2.0.44](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.43...v2.0.44) (2022-02-21) 166 | 167 | ### [2.0.43](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.42...v2.0.43) (2022-02-18) 168 | 169 | ### 2.0.42 (2022-02-16) 170 | 171 | ### [2.0.41](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.40...v2.0.41) (2022-02-14) 172 | 173 | ### 2.0.40 (2022-02-11) 174 | 175 | ### 2.0.39 (2022-02-09) 176 | 177 | ### 2.0.38 (2022-02-07) 178 | 179 | ### [2.0.37](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.36...v2.0.37) (2022-02-04) 180 | 181 | ### 2.0.36 (2022-02-02) 182 | 183 | ### [2.0.35](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.34...v2.0.35) (2022-01-31) 184 | 185 | ### 2.0.34 (2022-01-28) 186 | 187 | ### [2.0.33](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.32...v2.0.33) (2022-01-26) 188 | 189 | ### 2.0.32 (2022-01-24) 190 | 191 | ### [2.0.31](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.30...v2.0.31) (2022-01-21) 192 | 193 | ### 2.0.30 (2022-01-19) 194 | 195 | ### [2.0.29](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.28...v2.0.29) (2022-01-17) 196 | 197 | ### [2.0.28](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.27...v2.0.28) (2022-01-14) 198 | 199 | ### 2.0.27 (2022-01-12) 200 | 201 | ### [2.0.26](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.25...v2.0.26) (2022-01-10) 202 | 203 | ### 2.0.25 (2022-01-07) 204 | 205 | ### 2.0.24 (2022-01-05) 206 | 207 | ### 2.0.23 (2022-01-03) 208 | 209 | ### 2.0.22 (2021-12-31) 210 | 211 | ### 2.0.21 (2021-12-29) 212 | 213 | ### 2.0.20 (2021-12-27) 214 | 215 | ### [2.0.19](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.18...v2.0.19) (2021-12-25) 216 | 217 | ### 2.0.18 (2021-12-24) 218 | 219 | ### [2.0.17](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.16...v2.0.17) (2021-12-23) 220 | 221 | ### [2.0.16](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.15...v2.0.16) (2021-12-22) 222 | 223 | ### 2.0.15 (2021-12-21) 224 | 225 | ### [2.0.14](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.13...v2.0.14) (2021-12-20) 226 | 227 | ### [2.0.13](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.12...v2.0.13) (2021-12-19) 228 | 229 | ### 2.0.12 (2021-12-18) 230 | 231 | ### [2.0.11](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.10...v2.0.11) (2021-12-17) 232 | 233 | ### [2.0.10](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.9...v2.0.10) (2021-12-16) 234 | 235 | ### 2.0.9 (2021-12-15) 236 | 237 | ### 2.0.8 (2021-12-14) 238 | 239 | ### [2.0.7](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.6...v2.0.7) (2021-12-13) 240 | 241 | ### [2.0.6](https://github.com/vighnesh153/simple-github-gist-api/compare/v2.0.5...v2.0.6) (2021-12-13) 242 | -------------------------------------------------------------------------------- /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 | me@vighnesh153.com. 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 | MIT License 2 | 3 | Copyright (c) 2021 Vighnesh Raut 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Github Gist API 2 | 3 | DEPRECATED 4 | 5 | THIS PACKAGE HAS BEEN DEPRECATED IN FAVOUR OF [@vighnesh153/github-gist](https://www.npmjs.com/package/@vighnesh153/github-gist). IT WAS NOT FUN TO MAINTAIN MULTIPLE REPOSITORIES AND I HAVE NOW MOVED ALL MY PACKAGES TO A SINGLE MONOREPO. APOLOGIES FOR THE INCONVINIENCE. [NEW REPOSITORY](https://github.com/vighnesh153/vighnesh153-turbo) 6 | 7 | 8 | [![npm (scoped)](https://img.shields.io/npm/v/simple-github-gist-api)](https://www.npmjs.com/package/simple-github-gist-api) 9 | [![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/simple-github-gist-api)](https://img.shields.io/bundlephobia/minzip/simple-github-gist-api) 10 | [![npm](https://img.shields.io/npm/dt/simple-github-gist-api)](https://img.shields.io/npm/dt/simple-github-gist-api) 11 | [![GitHub](https://img.shields.io/github/license/vighnesh153/simple-github-gist-api)](https://github.com/vighnesh153/simple-github-gist-api/blob/master/LICENSE) 12 | [![GitHub issues](https://img.shields.io/github/issues/vighnesh153/simple-github-gist-api)](https://github.com/vighnesh153/simple-github-gist-api/issues) 13 | 14 | Use this promise based API to 15 | store data on your github gists without the 16 | need to manually make those tedious HTTP requests. 17 | 18 | > Note: This documentation is for v2 of the lib. v2 has a few breaking changes. You can find the v1 documentation in the `docs` directory (On Github). 19 | 20 | ## Installation 21 | 22 | ```sh 23 | npm i -S simple-github-gist-api 24 | ``` 25 | 26 | ## Usage 27 | 28 | ### Import (In Typescript) 29 | ```ts 30 | import GithubGist from 'simple-github-gist-api' 31 | ``` 32 | 33 | ### Require (In Javascript) 34 | ```js 35 | const GithubGist = require("simple-github-gist-api"); 36 | ``` 37 | 38 | ### Instantiate 39 | * [Generate](https://github.com/settings/tokens/new?scopes=gist) a Github PAT with `gist` 40 | scope selected. Keep this a secret as an environment variable. This will be used 41 | to create and update the gists. 42 | 43 | ```js 44 | const githubGist = new GithubGist({ 45 | // Required 46 | personalAccessToken: 'YOUR-github-personal-access-token', 47 | 48 | // Required: App-Identifier -> You can make use of your Application 49 | // name but make sure it is hyphen/underscore separated. 50 | appIdentifier: 'MyTestApp', 51 | 52 | // Optional: Doing this will allow the gist to be read by anyone. Just like a public 53 | // repository on Github. Setting it false, will allow both read and write 54 | // only with Github PAT. 55 | isPublic: false, 56 | 57 | // Optional: If using on the server side, you can set it to false. But if you are, for 58 | // some reason, using this package on front-end app, set it to `true`. This 59 | // will, behind the scenes, use `https://cors-anywhere.herokuapp.com/` prefix 60 | // to avoid CORS error. Note: Heroku may sometimes be slow. If you have your own 61 | // proxy server, you can use that as your custom prefix instead, too. 62 | cors: { 63 | addPrefix: true, 64 | customPrefix: (someURl) => `YourCustomPrefix` + someURl, 65 | }, 66 | }); 67 | ``` 68 | 69 | > Do use same identifier when re-starting the application. This is the thing 70 | > that will be used to identify your previously stored Github Gist. For 71 | > different applications, use different identifiers, unless you want to share 72 | > the Github gist among applications. 73 | 74 | 75 | ### Gist initialization 76 | The following just syncs up with the Github Gist server to fetch all the latest 77 | data. If running for the first time, it will create the gist for you with the 78 | above configurations. 79 | ```js 80 | try { 81 | await githubGist.touch(); 82 | 83 | console.log("Gist ID", githubGist.id); 84 | console.log("Github Owner Username", githubGist.ownerUsername); 85 | } catch (e) { 86 | // gist initialization failed. 87 | // console.log(e) 88 | } 89 | ``` 90 | 91 | ### Get all file names 92 | ```ts 93 | const fileNames: string[] = githubGist.getFileNames(); 94 | ``` 95 | 96 | ### Create a file 97 | The content of any file will always be string. If you want to have a json file, 98 | store it's content by deserializing it via `JSON.stringify` or any method you prefer. 99 | ```ts 100 | gist.createFile('projects.json', '{ "a": 123 }') 101 | ``` 102 | 103 | > Creates a file in the gist. If file already exists, it over-writes the 104 | > content of the file. 105 | > Returns true, if the file was newly created. 106 | > Returns false, if the file already exists and over-writes its content 107 | 108 | 109 | ### Get a file 110 | Returns the file instance. 111 | ```ts 112 | const projectsFile = gist.getFile('projects.json'); 113 | ``` 114 | 115 | ### Get content of the file 116 | Returns the file content. 117 | ```ts 118 | const content: string = projectsFile.getContent(); 119 | ``` 120 | 121 | ### Overwrite the file with new content 122 | ```ts 123 | projectsFile.overwrite('{ "a": 456 }'); 124 | ``` 125 | 126 | ### Save the file on the server. 127 | ```ts 128 | await projectsFile.save(); 129 | ``` 130 | 131 | ### If multiple files have updates, you can bulk save all the files 132 | ```ts 133 | await gist.save(); 134 | ``` 135 | > Only files that have un-saved changes will be saved. 136 | 137 | ### Gotchas 138 | 139 | Github Gist's API work on commit-id basis. If you save anything, 140 | it is a new commit and the commit-id changes. So, when you save, 141 | don't do that simultaneously. 142 | 143 | For instance, assume you have an endpoint `/create-file`. And if multiple people making request at the same time: 144 | ``` 145 | /create-file?name=1.json 146 | /create-file?name=2.json 147 | /create-file?name=3.json 148 | ... 149 | ``` 150 | 151 | then we cannot guarantee that the 152 | all the files will be saved. When creating `2.json` and `3.json`, there is a possibility 153 | that we make use of the same commit-id at HEAD. Both will work but 1 will over-write 154 | the other commit. 155 | 156 | But if you do the following, it will work as the latest commit-id will be fetched when 157 | saving each file. 158 | ```ts 159 | const file1 = gist.createFile('1.json', "{}") 160 | const file2 = gist.createFile('2.json', "{}") 161 | const file3 = gist.createFile('3.json', "{}") 162 | 163 | await file1.save(); 164 | await file2.save(); 165 | await file3.save(); 166 | ``` 167 | 168 | Or even this will work as well because all the changes will be pushed in a single commit. 169 | ```ts 170 | gist.save(); 171 | ``` 172 | 173 | ### Sample 174 | ```ts 175 | import GithubGist from "./src"; 176 | 177 | const personalAccessToken = ""; 178 | 179 | const githubGist = new GithubGist({ 180 | appIdentifier: 'MyTestApp', 181 | personalAccessToken, 182 | cors: { 183 | addPrefix: true, 184 | customPrefix: (someURl) => `YourCustomPrefix` + someURl, 185 | }, 186 | }); 187 | 188 | (async () => { 189 | await githubGist.touch(); 190 | 191 | console.log("Gist ID", githubGist.id); 192 | console.log("Github Username", githubGist.ownerUsername); 193 | 194 | console.log("Original File names", githubGist.getFileNames()); 195 | 196 | const created = githubGist.createFile("projects.json", "{}"); 197 | if (created) { 198 | console.log('Created new file in gist'); 199 | } else { 200 | console.log('Updated existing file in gist'); 201 | } 202 | 203 | // Note: All the creates and updates happen in-memory. You have to 204 | // explicitly invoke the `save` method on either the entire gist instance 205 | // or the individual file instance. 206 | 207 | // Saves all the files in the gist. Only the un-saved changes will be 208 | // added to the payload. 209 | await githubGist.save(); 210 | 211 | // Save individual file. 212 | // const file = githubGist.getFile('projects.json'); 213 | // await file.save(); 214 | 215 | console.log("File names", githubGist.getFileNames()); 216 | })(); 217 | ``` 218 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@commitlint/config-conventional', 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /docs/v1.md: -------------------------------------------------------------------------------- 1 | # Simple Github Gist API 2 | 3 | > This documentation is solely for v1. There are breaking changes in v2 4 | 5 | Use this promise, async-await based API to 6 | store data on your github gists without the 7 | need to make those tedious HTTP requests. 8 | 9 | ## Installation 10 | 11 | ``` 12 | npm i -S simple-github-gist-api 13 | ``` 14 | 15 | ## Usage 16 | 17 | ### Import (In Typescript) 18 | ```ts 19 | import { GithubGistApi } from 'simple-github-gist-api' 20 | ``` 21 | 22 | ### Require (In Javascript) 23 | ```js 24 | const { GithubGistApi } = require("simple-github-gist-api"); 25 | ``` 26 | 27 | ### Instantiate 28 | ```js 29 | const gist = new GithubGistApi('PAT', 'APP-IDENTIFIER'); 30 | ``` 31 | * [Generate](https://github.com/settings/tokens/new) a Github PAT with `gist` 32 | scope selected. Keep this a secret as an environment variable. This will be used 33 | to create and update the gists. 34 | * App-Identifier. You can make use of your Application name but make sure 35 | it is hyphen/underscore separated. 36 | > Do use same identifier when re-starting the application. This is the thing 37 | > that will be used to identify your previously stored Github Gist. For 38 | > different applications, use different identifiers, unless you want to share 39 | > the Github gist among applications. 40 | 41 | ### Read protection 42 | ```js 43 | gist.isPublic = true; 44 | ``` 45 | Doing this will allow the gist to be read by anyone. Just like a public 46 | repository on Github. Setting it false, will allow both read and write 47 | only with Github PAT. 48 | 49 | ### CORS 50 | ```js 51 | gist.allowCors = true; 52 | ``` 53 | If using on the server side, you can set it to false. But if you are, for 54 | some reason, using this package on front-end app, set it to `true`. This 55 | will, behind the scenes, use `https://cors-anywhere.herokuapp.com/` prefix 56 | to avoid CORS error. Heroku may sometimes be slow. Currently, working on a better 57 | way to make the request from front-end without getting CORS error. 58 | 59 | ### Gist initialization 60 | The following just syncs up with the Github Gist server to fetch all the latest 61 | data. If running for the first time, it will create the gist for you with the 62 | above configurations. 63 | ```js 64 | try { 65 | await gist.touch(); 66 | } catch (e) { 67 | // gist initialization failed. 68 | // console.log(e) 69 | } 70 | ``` 71 | 72 | ### Get all file names 73 | ```ts 74 | const fileNames: string[] = gist.getFileNames(); 75 | ``` 76 | 77 | ### Create a file 78 | The content of any file will always be string. If you want to have a json file, 79 | store its content as string marshalling(JSON.stringify) it to string. 80 | ```ts 81 | gist.createFile('projects.json', '{ "a": 123 }') 82 | ``` 83 | 84 | ### Get a file 85 | ```ts 86 | const projectsFile = gist.getFile('projects.json'); 87 | ``` 88 | 89 | ### Get content of the file 90 | ```ts 91 | const content = projectsFile.getContent(); 92 | ``` 93 | 94 | ### Overwrite the file with new content 95 | ```ts 96 | projectsFile.overwrite('{ "a": 456 }'); 97 | ``` 98 | 99 | ### Save the file on the server. 100 | ```ts 101 | await projectsFile.save(); 102 | ``` 103 | 104 | ### If multiple files have updates, you can bulk save all the files by 105 | ```ts 106 | await gist.save(); 107 | ``` 108 | -------------------------------------------------------------------------------- /jestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.(t|j)sx?$": "ts-jest" 4 | }, 5 | "coveragePathIgnorePatterns": [ 6 | "node_modules", 7 | "src/interfaces", 8 | "src/helpers" 9 | ], 10 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 11 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"] 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-github-gist-api", 3 | "version": "2.0.124", 4 | "description": "A way to store data on Github Gist.", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "test": "jest --config jestconfig.json", 9 | "prepare": "husky install", 10 | "cleanup": "rimraf dist", 11 | "build:pre-requisite": "npm run test", 12 | "build:declaration": "tsc", 13 | "build": "npm run cleanup && npm run build:pre-requisite && npm-run-all build:*", 14 | "test:watch": "jest --watch", 15 | "prettier:fix": "prettier -w ./src/**/*.ts -w ./src/**/*.tsx --no-error-on-unmatched-pattern true", 16 | "lint:fix": "eslint --fix ./src/**/*.ts --fix ./src/**/*.tsx --no-error-on-unmatched-pattern true", 17 | "git:rebase": "git fetch && git rebase origin/master", 18 | "release": "npm run git:rebase && npm run build && standard-version && git push --follow-tags && npm publish --access=public", 19 | "lint": "eslint src/*" 20 | }, 21 | "files": [ 22 | "dist/**/*" 23 | ], 24 | "repository": { 25 | "type": "git", 26 | "url": "git+ssh://git@github.com:vighnesh153/simple-github-gist-api.git" 27 | }, 28 | "keywords": [ 29 | "gist", 30 | "storage", 31 | "gist db", 32 | "database", 33 | "github", 34 | "free" 35 | ], 36 | "author": "Vighnesh Raut", 37 | "license": "GPL-3.0-or-later", 38 | "bugs": { 39 | "url": "https://github.com/vighnesh153/simple-github-gist-api/issues" 40 | }, 41 | "homepage": "https://github.com/vighnesh153/simple-github-gist-api#readme", 42 | "devDependencies": { 43 | "@commitlint/cli": "^17.1.2", 44 | "@commitlint/config-conventional": "^17.1.0", 45 | "@types/jest": "^29.1.1", 46 | "@typescript-eslint/eslint-plugin": "^5.39.0", 47 | "@typescript-eslint/parser": "^5.39.0", 48 | "eslint": "^8.24.0", 49 | "eslint-config-airbnb": "^19.0.4", 50 | "eslint-config-node": "^4.1.0", 51 | "eslint-config-prettier": "^8.5.0", 52 | "eslint-config-standard-with-typescript": "^23.0.0", 53 | "eslint-plugin-import": "^2.26.0", 54 | "eslint-plugin-node": "^11.1.0", 55 | "eslint-plugin-prettier": "^4.2.1", 56 | "eslint-plugin-promise": "^6.0.1", 57 | "husky": "^8.0.1", 58 | "jest": "^29.1.2", 59 | "lint-staged": "^13.0.3", 60 | "npm-run-all": "^4.1.5", 61 | "prettier": "^2.7.1", 62 | "rimraf": "^3.0.2", 63 | "semantic-release": "^19.0.5", 64 | "standard-version": "^9.5.0", 65 | "ts-jest": "^29.0.3", 66 | "typescript": "^4.8.4" 67 | }, 68 | "dependencies": { 69 | "axios": "^1.0.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | const constants = { 2 | githubRateLimit: 'https://api.github.com/rate_limit', 3 | githubGists: 'https://api.github.com/gists', 4 | corsAnywhere: 'https://cors-anywhere.herokuapp.com/', 5 | identifier: { 6 | prefix: '__', 7 | suffix: '-db-from-github-gist-api.txt', 8 | content: 'This is the persistent store for the app. ' + 9 | 'Playing with this gist directly, may have adverse ' + 10 | 'effects in your application.' 11 | }, 12 | }; 13 | 14 | export default constants; 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import GithubGist from "./models/GithubGist"; 2 | 3 | export default GithubGist; 4 | -------------------------------------------------------------------------------- /src/models/GistFile.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import getAuthConfig from '../util/auth-config'; 4 | 5 | import constants from '../constants'; 6 | 7 | interface GistFileOptions { 8 | personalAccessToken: string; 9 | gistId: string; 10 | gistOwner: string; 11 | 12 | fileName: string; 13 | fileContent: string; 14 | 15 | isPublic: boolean; 16 | 17 | cors?: { 18 | addPrefix?: boolean; 19 | customPrefix?: (url: string) => string; 20 | }; 21 | } 22 | 23 | class GistFile { 24 | // Core 25 | private readonly personalAccessToken: string; 26 | private readonly gistId: string; 27 | private readonly gistOwner: string = ''; 28 | private readonly fileName: string; 29 | private fileContent: string; 30 | private fileHasUpdates = false; 31 | 32 | // Config 33 | private readonly isPublic: boolean; 34 | private readonly addCorsPrefix: boolean; 35 | private readonly customCorsPrefix?: (url: string) => string; 36 | 37 | // Getters 38 | get hasUpdates(): boolean { 39 | return this.fileHasUpdates; 40 | } 41 | 42 | get name(): string { 43 | return this.fileName; 44 | } 45 | 46 | get content(): string { 47 | return this.fileContent; 48 | } 49 | 50 | // Setters 51 | set hasUpdates(value: boolean) { 52 | this.fileHasUpdates = value; 53 | } 54 | 55 | constructor(options: GistFileOptions) { 56 | this.personalAccessToken = options.personalAccessToken; 57 | this.gistId = options.gistId; 58 | this.gistOwner = options.gistOwner; 59 | 60 | this.fileName = options.fileName; 61 | this.fileContent = options.fileContent; 62 | 63 | this.isPublic = options.isPublic; 64 | this.fileHasUpdates = true; 65 | 66 | this.addCorsPrefix = Boolean(options.cors?.addPrefix); 67 | this.customCorsPrefix = options.cors?.customPrefix; 68 | } 69 | 70 | /** 71 | * Over-write the file's content with new content 72 | * 73 | * @param newContent 74 | */ 75 | overwrite = (newContent: string): void => { 76 | this.fileContent = newContent; 77 | this.fileHasUpdates = true; 78 | }; 79 | 80 | /** 81 | * Save the gist-file 82 | */ 83 | save = async (): Promise => { 84 | if (!this.hasUpdates) return; 85 | 86 | const url = `${constants.githubGists}/${this.gistId}`; 87 | const body = { 88 | public: this.isPublic, 89 | files: { 90 | [this.fileName]: { 91 | content: this.fileContent, 92 | }, 93 | }, 94 | }; 95 | 96 | await axios.post( 97 | url, 98 | body, 99 | getAuthConfig({ personalAccessToken: this.personalAccessToken }) 100 | ); 101 | this.fileHasUpdates = false; 102 | }; 103 | 104 | /** 105 | * Fetch the latest version of the file 106 | */ 107 | fetchLatest = async (): Promise => { 108 | const latestCommit = await this.getLatestGistCommit(); 109 | const url = this.getLatestGistFileFetchUrl(latestCommit); 110 | const response = await axios.get( 111 | url, 112 | getAuthConfig({ personalAccessToken: this.personalAccessToken }) 113 | ); 114 | this.fileContent = response.data; 115 | this.fileHasUpdates = false; 116 | }; 117 | 118 | /** 119 | * [Private Member] Returns the latest fetch url for the file. It gets updated on commit changes. 120 | * 121 | * @param commitId 122 | */ 123 | private readonly getLatestGistFileFetchUrl = (commitId: string): string => { 124 | const { addCorsPrefix, customCorsPrefix, gistOwner, gistId } = this; 125 | 126 | const url = 127 | `https://gist.githubusercontent.com/${gistOwner}` + 128 | `/${gistId}/raw/${commitId}/${this.fileName}`; 129 | 130 | if (!addCorsPrefix) return url; 131 | if (customCorsPrefix != null) return customCorsPrefix(url); 132 | return constants.corsAnywhere + url; 133 | }; 134 | 135 | /** 136 | * Returns the latest commit of the gist 137 | */ 138 | private readonly getLatestGistCommit = async (): Promise => { 139 | const dummyParam = `dummyParam=${Math.random()}`; // So that we are not a victim of caching 140 | const url = `${constants.githubGists}/${this.gistId}?${dummyParam}`; 141 | try { 142 | const result = await axios.get<{ history: Array<{ version: string }> }>( 143 | url, 144 | getAuthConfig({ personalAccessToken: this.personalAccessToken }) 145 | ); 146 | const response: { history: Array<{ version: string }> } = result.data; 147 | const latestCommit = response.history[0]; 148 | return latestCommit.version; 149 | } catch (e) { 150 | throw new Error('Error while fetching the latest commit.'); 151 | } 152 | }; 153 | } 154 | 155 | export default GistFile; 156 | -------------------------------------------------------------------------------- /src/models/GithubGist.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | import getAuthConfig from "../util/auth-config"; 4 | import validateToken from "../util/token-validity"; 5 | import isStringEmpty from "../util/is-string-empty"; 6 | import formattedGistIdentifier from "../util/formatted-gist-identifier"; 7 | 8 | import constants from "../constants"; 9 | 10 | import GistFile from "./GistFile"; 11 | 12 | interface IGistFile { 13 | [key: string]: { 14 | filename: string; 15 | }; 16 | } 17 | 18 | interface IGist { 19 | id: string; 20 | owner: { login: string }; 21 | files: IGistFile; 22 | } 23 | 24 | interface GithubGistOptions { 25 | /** 26 | * Head over to this link: https://github.com/settings/tokens/new?scopes=gist to create your personalAccessToken. 27 | * The "gist" scope is needed. Keep that token, a secret. 28 | */ 29 | personalAccessToken: string; 30 | 31 | /** 32 | * A unique name to identify your app. This is used to identify which files in the gist are managed by this lib. 33 | * Be sure to use the same name everytime so that you are modifying same files from your previous session. 34 | * 35 | * Examples: my-chat-app, my-first-app 36 | * 37 | * Note: Do use same identifier when re-starting the application. This is the thing that will be used to identify 38 | * your previously stored file in the Gist. For different applications, use different identifiers, unless you want 39 | * to share the files in the gist among different applications. 40 | */ 41 | appIdentifier: string; 42 | 43 | /** 44 | * Whether the gist should be private or public (If public, it would be accessible for reading to everyone) 45 | * 46 | * Default: undefined -> which means, Private 47 | */ 48 | isPublic?: boolean; 49 | 50 | /** 51 | * CORS configuration: Not needed when using server-side. 52 | */ 53 | cors?: { 54 | /** 55 | * Adds the default proxy-server prefix so that the URLs are not CORS blocked. 56 | * 57 | * For example: https://cors-anywhere.herokuapp.com/ as prefix to the Url. 58 | */ 59 | addPrefix?: boolean; 60 | 61 | /** 62 | * Gets the URL as argument. Feel free to decorate with any of your URLs and then 63 | * return the decorated URL. 64 | */ 65 | customPrefix?: (url: string) => string; 66 | } 67 | } 68 | 69 | class GithubGist { 70 | // Core 71 | private readonly personalAccessToken: string; 72 | private readonly appIdentifier: string; 73 | private readonly formattedAppIdentifier: string; 74 | private gistId: string = ''; 75 | private gistOwner: string = ''; 76 | private gistFiles: GistFile[] = []; 77 | 78 | // Config 79 | private readonly isPublic: boolean; 80 | private readonly addCorsPrefix: boolean; 81 | private readonly customCorsPrefix?: (url: string) => string; 82 | 83 | // Getters 84 | get id(): string { return this.gistId }; 85 | get ownerUsername(): string { return this.gistOwner }; 86 | 87 | constructor(options: GithubGistOptions) { 88 | this.personalAccessToken = options.personalAccessToken; 89 | this.appIdentifier = options.appIdentifier; 90 | this.formattedAppIdentifier = formattedGistIdentifier(options.appIdentifier); 91 | 92 | this.isPublic = Boolean(options.isPublic); 93 | this.addCorsPrefix = Boolean(options.cors?.addPrefix); 94 | this.customCorsPrefix = options.cors?.customPrefix; 95 | } 96 | 97 | /** 98 | * Syncs the gistInstance with Github server. Should be done only once, 99 | * right after instantiation of this class. 100 | */ 101 | touch = async (): Promise => { 102 | // Throws error if the token is not valid. 103 | await validateToken(this.personalAccessToken); 104 | 105 | // Fetch and set the gist ID 106 | await this.fetchGist(); 107 | 108 | // If gist ID is not set, it means the Gist doesn't exist. Create a new gist 109 | if (isStringEmpty(this.gistId)) { 110 | await this.createGist(); 111 | } 112 | }; 113 | 114 | /** 115 | * Creates a file in the gist. If file already exists, it over-writes the 116 | * content of the file. 117 | * Returns, true, if file was created. 118 | * Returns, false, if existing file was updated. 119 | * 120 | * @param name 121 | * @param content 122 | */ 123 | createFile = (name: string, content: string): boolean => { 124 | const existingFile = this.gistFiles.find((file) => file.name === name); 125 | if (existingFile) { 126 | existingFile.overwrite(content); 127 | return false; 128 | } 129 | 130 | const file = this.constructGistFile(name, content); 131 | this.gistFiles.push(file); 132 | return true; 133 | } 134 | 135 | /** 136 | * Get a particular file instance. Returns null if file not found. 137 | * 138 | * @param name 139 | */ 140 | getFile = (name: string): GistFile | null => { 141 | const file = this.gistFiles.find((file) => file.name === name); 142 | if (file) return file; 143 | 144 | // File not found 145 | return null; 146 | } 147 | 148 | /** 149 | * Get all file instances 150 | */ 151 | getFiles = (): GistFile[] => { 152 | return [...this.gistFiles]; 153 | }; 154 | 155 | /** 156 | * Returns the names of all the files in the gist. 157 | */ 158 | getFileNames = (): string[] => { 159 | return this.gistFiles.map(file => file.name); 160 | } 161 | 162 | /** 163 | * Saves all the files in the gist, only if they have updates 164 | */ 165 | save = async (): Promise => { 166 | const files: { [fileName: string]: { content: string } } = {}; 167 | for (const file of this.gistFiles) { 168 | if (file.hasUpdates === false) continue; 169 | files[file.name] = { 170 | content: file.content, 171 | } 172 | } 173 | 174 | // No files need updates 175 | if (Object.keys(files).length === 0) return; 176 | 177 | const url = `${constants.githubGists}/${this.gistId}`; 178 | const body = {public: this.isPublic, files}; 179 | 180 | await axios.post(url, body, getAuthConfig({ personalAccessToken: this.personalAccessToken })); 181 | 182 | // Mark the hasUpdates flag in all the files as false. 183 | this.gistFiles.forEach((file) => { 184 | file.hasUpdates = false; 185 | }); 186 | } 187 | 188 | /** 189 | * [Private Member] Fetches the gist information. 190 | */ 191 | private fetchGist = async (): Promise => { 192 | // Gets all the gists basic information 193 | const result = await axios.get(constants.githubGists, getAuthConfig({ personalAccessToken: this.personalAccessToken })); 194 | 195 | const gists = result.data; 196 | for (const gist of gists) { 197 | const fileNames = Object.keys(gist.files); 198 | 199 | // If the formattedAppIdentifier file exists in gist, this is out gist. 200 | if (fileNames.includes(this.formattedAppIdentifier)) { 201 | await this.initialize(gist); 202 | break; 203 | } 204 | } 205 | }; 206 | 207 | /** 208 | * [Private Member] Creates the gist 209 | */ 210 | private createGist = async (): Promise => { 211 | const { identifier: { content }, githubGists } = constants; 212 | 213 | const rootFileContent = content.replace('', this.appIdentifier); 214 | const payload = { 215 | public: this.isPublic, 216 | files: { 217 | [this.formattedAppIdentifier]: { 218 | content: rootFileContent 219 | } 220 | } 221 | }; 222 | 223 | // Create gist API call 224 | const result = await axios.post(githubGists, payload, getAuthConfig({ personalAccessToken: this.personalAccessToken })); 225 | 226 | const gist = result.data; 227 | 228 | // Initialize the current instance with the gist meta-information 229 | await this.initialize(gist); 230 | }; 231 | 232 | /** 233 | * [Private Member] Initializes the instance with the information from server. 234 | * @param gist 235 | * @private 236 | */ 237 | private initialize = async (gist: IGist): Promise => { 238 | // Set gist meta 239 | this.gistId = gist.id; 240 | this.gistOwner = gist.owner.login; 241 | 242 | const fetchFileContent: Promise[] = []; 243 | 244 | // Initialize all the files 245 | this.gistFiles = []; 246 | for (const fileName of Object.keys(gist.files)) { 247 | const file = this.constructGistFile(fileName, ''); 248 | fetchFileContent.push(file.fetchLatest()); 249 | this.gistFiles.push(file); 250 | } 251 | 252 | // Fetching content of all the files. 253 | await Promise.all(fetchFileContent); 254 | } 255 | 256 | /** 257 | * [Private Member] This constructs the GistFile instance 258 | * @param fileName 259 | * @param content 260 | */ 261 | private constructGistFile = (fileName: string, content: string): GistFile => { 262 | return new GistFile({ 263 | fileName, 264 | fileContent: content, 265 | gistId: this.gistId, 266 | gistOwner: this.gistOwner, 267 | cors: { 268 | addPrefix: this.addCorsPrefix, 269 | customPrefix: this.customCorsPrefix, 270 | }, 271 | personalAccessToken: this.personalAccessToken, 272 | isPublic: this.isPublic, 273 | }); 274 | }; 275 | } 276 | 277 | export default GithubGist; 278 | -------------------------------------------------------------------------------- /src/util/auth-config.ts: -------------------------------------------------------------------------------- 1 | import {AxiosRequestConfig} from "axios"; 2 | 3 | interface AuthConfigProps { 4 | personalAccessToken: string; 5 | } 6 | 7 | const getAuthConfig = (props: AuthConfigProps): AxiosRequestConfig => { 8 | return { 9 | headers: {Authorization: `token ${props.personalAccessToken}`} 10 | }; 11 | }; 12 | 13 | export default getAuthConfig; 14 | -------------------------------------------------------------------------------- /src/util/formatted-gist-identifier.ts: -------------------------------------------------------------------------------- 1 | import constants from '../constants'; 2 | 3 | const formattedGistIdentifier = (identifierName: string): string => { 4 | const { prefix, suffix } = constants.identifier; 5 | return prefix + identifierName + suffix; 6 | }; 7 | 8 | export default formattedGistIdentifier; 9 | -------------------------------------------------------------------------------- /src/util/is-string-empty.ts: -------------------------------------------------------------------------------- 1 | const isStringEmpty = (s: string) => s.length === 0; 2 | 3 | export default isStringEmpty; 4 | -------------------------------------------------------------------------------- /src/util/token-validity.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import constants from '../constants'; 3 | 4 | const isTokenStringValid = async (token: string): Promise => { 5 | const rateLimitEndpoint = constants.githubRateLimit; 6 | return axios 7 | .get(rateLimitEndpoint, { 8 | headers: {Authorization: `token ${token}`} 9 | }) 10 | .then(result => result.headers['x-oauth-scopes'].includes('gist')); 11 | }; 12 | 13 | // check if token is valid and has gist access 14 | const validateToken = async (token: string): Promise => { 15 | if (!(await isTokenStringValid(token))) { 16 | throw new Error('Token is invalid or it doesn\'t have gist access.') 17 | } 18 | } 19 | 20 | export default validateToken; 21 | -------------------------------------------------------------------------------- /tests/index.test.ts: -------------------------------------------------------------------------------- 1 | const helloWorld = (): string => { 2 | return 'Hello World!'; 3 | }; 4 | 5 | it('should return Hello World!', function () { 6 | expect(helloWorld()).toBe("Hello World!"); 7 | }); 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "strict": true 8 | }, 9 | "include": ["src/"], 10 | "exclude": [ "node_modules/", "tests/" ] 11 | } 12 | --------------------------------------------------------------------------------