├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
├── issue_template.md
└── workflows
│ ├── auto-approve-deploys.yml
│ ├── build.yml
│ ├── pr-auto-merge-dependabot.yml
│ └── pr-auto-merge-labeler.yml
├── .gitignore
├── .husky
└── pre-commit
├── .npmignore
├── .nycrc.yaml
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── clean.sh
├── examples
├── basic
│ ├── .npmrc
│ ├── README.md
│ ├── data-files
│ │ ├── glyphicons-halflings-regular.woff2
│ │ ├── index.html
│ │ ├── jpg.jpg
│ │ ├── png.png
│ │ └── subdir
│ │ │ └── png.png
│ ├── handler.js
│ ├── package.json
│ └── serverless.yml
└── serverless-offline
│ ├── .npmrc
│ ├── README.md
│ ├── data-files
│ ├── glyphicons-halflings-regular.woff2
│ ├── index.html
│ ├── jpg.jpg
│ ├── png.png
│ └── subdir
│ │ └── png.png
│ ├── handler.js
│ ├── package.json
│ └── serverless.yml
├── greenkeeper.json
├── package-lock.json
├── package.json
├── plugins
└── BinaryMediaTypes.js
├── pre-commit.sh
├── release.config.js
├── scripts
├── approve-dependabot-deploys
│ ├── .gitignore
│ ├── README.md
│ ├── approve-dependabot-deploys.ts
│ ├── package-lock.json
│ ├── package.json
│ └── tsconfig.json
├── aws-oidc-role-cloudformation-template.yaml
└── aws-oidc-role-provision.sh
├── src
├── StaticFileHandler.js
├── plugins
│ └── BinaryMediaTypes.js
└── test
│ ├── BinaryMediaTypes.js
│ ├── StaticFileHandler.js
│ ├── data
│ └── testfiles
│ │ ├── README.md
│ │ ├── blah.bin
│ │ ├── custom-error.html
│ │ ├── fonts
│ │ └── glyphicons-halflings-regular.woff2
│ │ ├── index.html
│ │ ├── jpg.jpg
│ │ ├── png.png
│ │ ├── unknown-mime-type.unknowntype
│ │ └── vendor
│ │ ├── output.css.map
│ │ └── output.js.map
│ └── e2e.js
└── test-files
├── basic-project
├── .gitignore
├── .npmrc
├── README.md
├── data-files
│ ├── index.html
│ └── png.png
├── handler.js
├── package.json
└── serverless.yml
├── scripts
├── test-http.sh
└── test-local-e2e.sh
└── webpack-project
├── .babelrc
├── .gitignore
├── README.md
├── data-files
├── index.html
└── png.png
├── event.json
├── handler.js
├── package-lock.json
├── package.json
├── serverless.yml
└── webpack.config.js
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | - Using welcoming and inclusive language
12 | - Being respectful of differing viewpoints and experiences
13 | - Gracefully accepting constructive criticism
14 | - Focusing on what is best for the community
15 | - Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | - Trolling, insulting/derogatory comments, and personal or political attacks
21 | - Public or private harassment
22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | - Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at serverless-aws-static-file-handler@willeke.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
4 |
5 | The following is a set of guidelines for contributing to this project. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
6 |
7 | ## How Can I Contribute?
8 |
9 | Feel free to check [issues page](https://github.com/activescott/serverless-aws-static-file-handler/issues) to find an enhancement to implement or bug to fix. You could also **[Improve the documentation](https://github.com/activescott/serverless-aws-static-file-handler/edit/master/README.md)**, **Report a Bug**, or **Suggest an Enhancement**.
10 |
11 | ### Contribute Code
12 |
13 | - Fork this repository and [submit a pull request](https://help.github.com/articles/creating-a-pull-request/).
14 | - Write tests
15 | - Ensure the linter passes with `yarn lint` or `npm run lint`
16 | - Ensure the tests pass at the CI Server by [following the status](https://help.github.com/articles/about-statuses/) of your pull request.
17 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## What did you implement:
2 |
3 | Closes #XXXXX
4 |
5 |
8 |
9 | ## How did you implement it:
10 |
11 |
14 |
15 | ## How can we verify it:
16 |
17 |
27 |
28 | ## Todos:
29 |
30 | - [ ] Write tests for any changed code. See tests at https://github.com/activescott/serverless-aws-static-file-handler/tree/main/src/test for some examples.
31 | - [ ] All tests pass at the CI Server by [following the status](https://help.github.com/articles/about-statuses/) of your pull request.
32 |
33 | If you need a hand with anything at all tag [activescott](https://github.com/activescott/) in a comment and ask!
34 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # docs v2 https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
4 | - package-ecosystem: "npm"
5 | directory: "/"
6 | schedule:
7 | interval: "monthly"
8 | # Check for npm updates on Sundays
9 | day: "saturday"
10 | allow:
11 | - dependency-type: "production"
12 | commit-message:
13 | # for production deps, prefix commit messages with "fix" (trigger a patch release)
14 | prefix: "fix"
15 | # for development deps, prefix commit messages with "chore" (do NOT trigger an npm release)
16 | prefix-development: "chore"
17 | include: "scope"
18 |
19 | - package-ecosystem: "npm"
20 | directory: "/test-files/basic-project"
21 | schedule:
22 | interval: "monthly"
23 | # Check for npm updates on Sundays
24 | day: "saturday"
25 | commit-message:
26 | # since these are tests/samples, we'll not require a release of the package
27 | prefix: "chore"
28 | allow:
29 | # Allow only direct updates for all packages
30 | - dependency-type: "direct"
31 |
32 | - package-ecosystem: "npm"
33 | directory: "/test-files/webpack-project"
34 | schedule:
35 | interval: "monthly"
36 | # Check for npm updates on Sundays
37 | day: "saturday"
38 | commit-message:
39 | # since these are tests/samples, we'll not require a release of the package
40 | prefix: "chore"
41 | allow:
42 | # Allow only direct updates for all packages
43 | - dependency-type: "direct"
44 |
45 | - package-ecosystem: "npm"
46 | directory: "/examples/basic"
47 | schedule:
48 | interval: "monthly"
49 | # Check for npm updates on Sundays
50 | day: "saturday"
51 | commit-message:
52 | # since these are tests/samples, we'll not require a release of the package
53 | prefix: "chore"
54 | allow:
55 | # Allow only direct updates for all packages
56 | - dependency-type: "direct"
57 |
58 | - package-ecosystem: "npm"
59 | directory: "/examples/serverless-offline"
60 | schedule:
61 | interval: "monthly"
62 | # Check for npm updates on Sundays
63 | day: "saturday"
64 | commit-message:
65 | # since these are tests/samples, we'll not require a release of the package
66 | prefix: "chore"
67 | allow:
68 | # Allow only direct updates for all packages
69 | - dependency-type: "direct"
70 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | ### Expected behavior:
2 |
3 | ### Actual behavior:
4 |
5 | ### Steps to reproduce the problem:
6 |
7 | ### Environment:
8 |
9 | Please include version of serverless-aws-static-file-handler, version of serverless, version of node, and operating system name and version
10 |
--------------------------------------------------------------------------------
/.github/workflows/auto-approve-deploys.yml:
--------------------------------------------------------------------------------
1 | name: Deployment Auto-Approver
2 | # using triggers for every deployment and allowed manually
3 | # docs on these triggers:
4 | on:
5 | # allows manually triggering (see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)
6 | workflow_dispatch:
7 | schedule:
8 | # 08:30 UTC daily (see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule)
9 | - cron: "30 08 * * *"
10 | # the only problem with the 'deployment' trigger is that dependabot PRs are considered from forked repos and the token doesn't have permission to approve (see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#deployment)
11 | # deployment:
12 |
13 | jobs:
14 | auto_approve:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Auto Approve Deploys
18 | uses: activescott/automate-environment-deployment-approval@main
19 | with:
20 | github_token: ${{ secrets.GH_TOKEN_FOR_AUTO_APPROVING_DEPLOYS }}
21 | environment_allow_list: |
22 | aws
23 | # e.g. "dependabot[bot]"
24 | actor_allow_list: |
25 | dependabot[bot]
26 | activescott
27 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | # We want pushes to main, beta, and next to trigger a publish to npm for the corresponding npm dist-tag.
4 | # NOTE: semantic-release detects pull requests and won't deploy on them so we don't have to deal with that complexity here.
5 | # Any pull request should run all tests.
6 | # NOTE: e2e tests use shared resource (AWS) and if they run concurrently they step on one another and cause errors.
7 | # This can happen for beta & next branches if a PR targeting main also exists for them. There is some logic in e2e_tests job to mitigate this.
8 | on:
9 | push:
10 | branches: [main, beta, next]
11 | pull_request:
12 | branches: [main]
13 |
14 | # Allow one concurrent build for the same branch
15 | concurrency:
16 | group: current-branch-${{ github.ref }}
17 | cancel-in-progress: true
18 |
19 | jobs:
20 | unit_tests:
21 | runs-on: ubuntu-20.04
22 | strategy:
23 | matrix:
24 | node: [20, 22]
25 |
26 | steps:
27 | - uses: actions/checkout@v3
28 | - name: Use Node.js ${{ matrix.node }}
29 | uses: actions/setup-node@v2
30 | with:
31 | node-version: ${{ matrix.node }}
32 | cache: "npm"
33 |
34 | - name: install dependencies
35 | run: |
36 | npm i
37 |
38 | - name: test
39 | env:
40 | CI_NODE_VERSION: ${{ matrix.node }}
41 | run: |
42 | npm run test
43 |
44 | - name: publish coverage
45 | uses: coverallsapp/github-action@master
46 | continue-on-error: true
47 | with:
48 | github-token: ${{ secrets.GITHUB_TOKEN }}
49 | flag-name: nodejs-${{ matrix.node }}
50 | parallel: true
51 |
52 | update_code_coverage:
53 | needs: unit_tests
54 | runs-on: ubuntu-20.04
55 | steps:
56 | - name: Coveralls Finished
57 | uses: coverallsapp/github-action@master
58 | continue-on-error: true
59 | with:
60 | github-token: ${{ secrets.GITHUB_TOKEN }}
61 | parallel-finished: true
62 |
63 | e2e_tests:
64 | # Allow only one concurrent deployment for the target branch:
65 | ## no reason to run the long-running AWS e2e tests until we are sure this one will be viable
66 | ## NOTE: we could separate e2e_tests job into a e2e_tests_local and e2e_tests_remote since the local ones are quite a lot faster
67 | concurrency:
68 | group: target-branch-${{ github.base_ref }}
69 |
70 | permissions:
71 | # NOTE: If you specify the access for any of these scopes, all of those that are not specified are set to none.
72 | # for AWS OIDC Token access per https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#updating-your-github-actions-workflow
73 | id-token: write
74 | contents: read
75 | deployments: read
76 | needs: unit_tests
77 | runs-on: ubuntu-20.04
78 | environment: aws
79 | env:
80 | # e2e tests use a shared resource in AWS. Since we trigger on both a push to beta/next and a PR with destination branch of main, a PR against beta/next will cause them to run concurrently and causes errors. To prevent this contention we use a serverless stage.
81 | # For more information on serverless stages:
82 | # - https://www.serverless.com/framework/docs/providers/aws/cli-reference/deploy/
83 | # - https://www.serverless.com/framework/docs/providers/aws/guide/deploying#tips
84 | # github.run_id: A unique number for each run within a repository. This number does not change if you re-run the workflow run. https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context
85 | # github format: https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#format
86 | SERVERLESS_STAGE: ${{ github.event_name == 'pull_request' && format('pr{0}', github.run_id) || format('push{0}', github.run_id) }}
87 | steps:
88 | - uses: actions/checkout@v3
89 |
90 | - name: Use Node.js
91 | uses: actions/setup-node@v1
92 | with:
93 | # NOTE: in v13.0, serverless-offline removed support for node v14: https://github.com/dherault/serverless-offline/releases/tag/v13.0.0
94 | node-version: 20
95 |
96 | - name: build plugin locally
97 | run: |
98 | cd "$GITHUB_WORKSPACE"
99 | npm i
100 | npm pack
101 |
102 | - name: run local end-to-end test in serverless-offline
103 | run: |
104 | "$GITHUB_WORKSPACE/test-files/scripts/test-local-e2e.sh"
105 |
106 | - name: configure aws credentials
107 | # Configures AWS credential and region environment variables for use in other GitHub Actions.
108 | # The environment variables will be detected by both the AWS SDKs and the AWS CLI to determine the credentials and region to use for AWS API calls.
109 | # More on this action at https://github.com/aws-actions/configure-aws-credentials
110 | uses: aws-actions/configure-aws-credentials@v1
111 | with:
112 | role-to-assume: arn:aws:iam::166901232151:role/serverless-aws-static-file-handler-at-github
113 | aws-region: us-west-2
114 |
115 | - name: prepare to run remote end-to-end test
116 | run: |
117 | cd "$GITHUB_WORKSPACE/examples/basic"
118 | npm i
119 | # update to use the local plugin built above
120 | npm install --save file://../../serverless-aws-static-file-handler-0.0.0.tgz
121 |
122 | - name: run remote end-to-end test
123 | run: |
124 | cd "$GITHUB_WORKSPACE/examples/basic"
125 | echo "Deploying serverless stage $SERVERLESS_STAGE"
126 | ./node_modules/.bin/serverless deploy --stage $SERVERLESS_STAGE
127 |
128 | # get the APIG endpoint URL:
129 | APIG_URL=$(./node_modules/.bin/serverless info --stage $SERVERLESS_STAGE | sed -nr "s#^.*(https://.+/$SERVERLESS_STAGE)\$#\1#p")
130 | echo "Discovered APIG_URL: $APIG_URL"
131 |
132 | # CURL to some known good endpoints expecting 200:
133 | TEST_HTTP_EXEC=$GITHUB_WORKSPACE/test-files/scripts/test-http.sh
134 | ROOT_URL=$APIG_URL
135 |
136 | # 200; these all should succeed
137 | $TEST_HTTP_EXEC $ROOT_URL/binary/png.png
138 | $TEST_HTTP_EXEC $ROOT_URL/binary/jpg.jpg
139 | $TEST_HTTP_EXEC $ROOT_URL/binary/glyphicons-halflings-regular.woff2
140 | $TEST_HTTP_EXEC $ROOT_URL/binary/subdir/png.png
141 |
142 | # 403
143 | $TEST_HTTP_EXEC "$ROOT_URL/ff404.png" 403
144 | $TEST_HTTP_EXEC "$ROOT_URL/jpeg404.jpg" 403
145 | $TEST_HTTP_EXEC "$ROOT_URL/subdir404/ff.png" 403
146 | $TEST_HTTP_EXEC "$ROOT_URL/subdir/ff404.png" 403
147 |
148 | # 404
149 | $TEST_HTTP_EXEC "$ROOT_URL/binary/404-glyphicons-halflings-regular.woff2" 404
150 | $TEST_HTTP_EXEC "$ROOT_URL/binary/subdir/404-png.png" 404
151 |
152 | - name: cleanup remote end-to-end test (destroy serverless stack)
153 | # Run this step even if the prior one failed (to clean up)
154 | if: ${{ always() }}
155 | run: |
156 | cd "$GITHUB_WORKSPACE/examples/basic"
157 | echo "Destroying serverless stage $SERVERLESS_STAGE"
158 | ./node_modules/.bin/serverless remove --stage $SERVERLESS_STAGE
159 |
160 | publish_package:
161 | if: ${{ github.event_name != 'pull_request' }}
162 | needs: [e2e_tests, unit_tests]
163 | runs-on: ubuntu-20.04
164 | environment: npm
165 | steps:
166 | - uses: actions/checkout@v3
167 |
168 | - name: Use Node.js
169 | uses: actions/setup-node@v1
170 | with:
171 | node-version: 20
172 |
173 | #- name: debug publish_package
174 | # uses: actions/bin/debug@master
175 |
176 | - name: publish to npm
177 | env:
178 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
179 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
180 | run: |
181 | npm install
182 | npx semantic-release@17
183 |
--------------------------------------------------------------------------------
/.github/workflows/pr-auto-merge-dependabot.yml:
--------------------------------------------------------------------------------
1 | name: Auto-Merge
2 | on:
3 | pull_request_target:
4 | types: [labeled]
5 |
6 | jobs:
7 | enable-auto-merge:
8 | runs-on: ubuntu-latest
9 |
10 | # Specifically check that dependabot (or another trusted party) created this pull-request, and that it has been labelled correctly.
11 | if: github.event.pull_request.user.login == 'dependabot[bot]' && contains(github.event.pull_request.labels.*.name, 'dependencies')
12 | steps:
13 | - uses: alexwilson/enable-github-automerge-action@f4f9509cc5024102ac8d52d1c1d2d8e194afbbb3
14 | with:
15 | github-token: "${{ secrets.GITHUB_TOKEN }}"
16 |
--------------------------------------------------------------------------------
/.github/workflows/pr-auto-merge-labeler.yml:
--------------------------------------------------------------------------------
1 | name: Label PR with Auto-Merge Status
2 | on:
3 | pull_request_target:
4 | types: [auto_merge_enabled, auto_merge_disabled]
5 |
6 | jobs:
7 | label:
8 | uses: activescott/github-actions-workflows/.github/workflows/pr-auto-merge-labeler.yml@pr-auto-merge-labeler-v1
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | examples/basic/.serverless
61 |
62 | .vscode/
63 | .idea/
64 |
65 | /junk.js
66 | /todo.md
67 |
68 | .serverless/
69 |
70 | .DS_Store
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm test
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/test/
2 | .prettierignore
3 | yarn.lock
4 | .nycrc.yaml
5 |
--------------------------------------------------------------------------------
/.nycrc.yaml:
--------------------------------------------------------------------------------
1 | reporter:
2 | - lcov
3 | - html
4 | check-coverage: true
5 | branches: 90
6 | lines: 90
7 | functions: 90
8 | statements: 90
9 | all: true
10 | exclude:
11 | - src/test/
12 | - coverage/
13 | - examples/
14 | - release.config.js
15 | - test-files/
16 | - scripts/
17 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | ./.vscode/
2 | ./examples/basic/.serverless/
3 | .nyc_output/
4 | coverage/
5 | node_modules/
6 | .serverless/
7 | .webpack/
8 | scripts/approve-dependabot-deploys/responses/
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 scott willeke
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 | [](https://www.npmjs.com/package/serverless-aws-static-file-handler)
2 | [](https://www.npmjs.com/package/serverless-aws-static-file-handler)
3 | [](https://github.com/activescott/serverless-aws-static-file-handler/actions)
4 | [](https://coveralls.io/github/activescott/serverless-aws-static-file-handler)
5 | [](https://github.com/activescott/serverless-aws-static-file-handler/blob/master/LICENSE)
6 | [](https://github.com/activescott/serverless-aws-static-file-handler)
7 |
8 | # serverless-aws-static-file-handler
9 |
10 | Host the front-end of your web applications on [Serverless framework](https://github.com/serverless/serverless) on AWS Lambda right alongside the API.
11 |
12 | It is a fast and easy way to get started and makes it trivial to deploy your web applications. If you need better response time in the future and get concerned about AWS costs of using Lambda to static content, you put CloudFront in front of your Serverless endpoints service static content.
13 |
14 |
15 |
16 | - [Usage / Quick Start](#usage--quick-start)
17 | - [Prerequisites / Usage Requirements](#prerequisites--usage-requirements)
18 | - [Install](#install)
19 | - [Features](#features)
20 | - [Contributing 🤝](#contributing-🤝)
21 | - [Show your support](#show-your-support)
22 | - [Release Process (Deploying to NPM)](#release-process-deploying-to-npm)
23 | - [License 📝](#license-📝)
24 |
25 |
26 |
27 | ## Usage / Quick Start
28 |
29 | Import & initialize:
30 |
31 | const StaticFileHandler = require('serverless-aws-static-file-handler')
32 |
33 | # configure where to serve files from:
34 | const clientFilesPath = path.join(__dirname, "./data-files/")
35 | const fileHandler = new StaticFileHandler(clientFilesPath)
36 |
37 | Define a handler in your code as follows:
38 |
39 | module.exports.html = async (event, context) => {
40 | event.path = "index.html" // forcing a specific page for this handler, ignore requested path. This would serve ./data-files/index.html
41 | return fileHandler.get(event, context)
42 | }
43 |
44 | In your `serverless.yml` file, reference the handler function from above to provide routes to your static files:
45 |
46 | functions:
47 | html:
48 | handler: handler.html
49 | events:
50 | - http:
51 | path: /
52 | method: get
53 |
54 | # Note Binary files work too! See configuration information below
55 | png:
56 | handler: handler.png
57 | events:
58 | - http:
59 | path: png
60 | method: get
61 |
62 | # The following example uses a path placeholder to serve all files directly in the /binary/ directory:
63 | binary:
64 | handler: handler.binary
65 | events:
66 | - http:
67 | path: /binary/{pathvar+}
68 | method: get
69 |
70 | To serve binary content make sure that you setup the plugin in your serverless.yml like so:
71 |
72 | plugins:
73 | - serverless-aws-static-file-handler/plugins/BinaryMediaTypes
74 |
75 | custom:
76 | apiGateway:
77 | binaryMediaTypes:
78 | - "image/png"
79 | - "image/jpeg"
80 |
81 | Some additional real-world examples are demonstrated in the [examples/basic/](examples/basic) directory as well as a [serverless-offline](https://github.com/dherault/serverless-offline)-specific example in [examples/serverless-offline/](examples/serverless-offline).
82 |
83 | ## Prerequisites / Usage Requirements
84 |
85 | Requires Node.js latest, LTS, or v10 ([tested](https://github.com/activescott/serverless-aws-static-file-handler/actions)).
86 |
87 | Requires Serverless Framework v1.x.
88 | If you are new to the Serverless Framework, check out the [Serverless Framework Getting Started Guide](https://serverless.com/framework/docs/getting-started/).
89 |
90 | ## Install
91 |
92 | Install with yarn (`yarn add serverless-aws-static-file-handler`) or npm (`npm install serverless-aws-static-file-handler --save-prod`)
93 |
94 | ## Features
95 |
96 | - Simple to get started
97 | - Works with **text files** such as HTML or **binary** files such as images or fonts
98 |
99 | ## Contributing 🤝
100 |
101 | This is a community project. We invite your participation through issues and pull requests! You can peruse the [contributing guidelines](.github/CONTRIBUTING.md).
102 |
103 | ## Show your support
104 |
105 | Give a ⭐️ if this project helped you!
106 |
107 | ## Release Process (Deploying to NPM)
108 |
109 | We use [semantic-release](https://github.com/semantic-release/semantic-release) to consistently release [semver](https://semver.org/)-compatible versions. This project deploys to multiple [npm distribution tags](https://docs.npmjs.com/cli/dist-tag). Each of the below branches correspond to the following npm distribution tags:
110 |
111 | | branch | npm distribution tag |
112 | | ------ | -------------------- |
113 | | main | latest |
114 | | beta | beta |
115 |
116 | To trigger a release use a Conventional Commit following [Angular Commit Message Conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) on one of the above branches.
117 |
118 | ## License 📝
119 |
120 | Copyright © 2017 [scott@willeke.com](https://github.com/activescott).
121 |
122 | This project is [MIT](https://github.com/activescott/serverless-http-invoker/blob/master/LICENSE) licensed.
123 |
--------------------------------------------------------------------------------
/clean.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | THISDIR=$(cd $(dirname "$0"); pwd) #this script's directory
3 | THISSCRIPT=$(basename $0)
4 |
5 | git clean -f -d -x "$THISDIR"
6 |
--------------------------------------------------------------------------------
/examples/basic/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 | To see this example take the following steps at the shell in the root of this example's folder:
2 |
3 | 1. Run `npm install` to install dependencies
4 | 2. Run `npm run deploy` to deploy to AWS via Serverless Framework
5 | 3. Open the root endpoint displayed by Serverless Framework after the successful deployment (something like https://d3cafbad.execute-api.us-east-1.amazonaws.com/dev/)
6 |
--------------------------------------------------------------------------------
/examples/basic/data-files/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/basic/data-files/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/examples/basic/data-files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
20 |
21 | These should all load and either be a valid image or a font:
22 |
23 |
24 |
25 | These each have a path that does not have a route (http
event)
26 | setup in serverless.yml. They should all be returning
27 | 403 OR
28 | 404: Not Found (API Gateway in prod reports
29 | them as 403, but serverless-offline reports them as 404):
30 |
31 |
32 |
33 | These each have a valid route (http
event) setup in
34 | serverless.yml, but the file doesn't exist on disk. They should all be
35 | returning 404: Not Found :
36 |
37 |
38 |
158 |
159 |
--------------------------------------------------------------------------------
/examples/basic/data-files/jpg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/basic/data-files/jpg.jpg
--------------------------------------------------------------------------------
/examples/basic/data-files/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/basic/data-files/png.png
--------------------------------------------------------------------------------
/examples/basic/data-files/subdir/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/basic/data-files/subdir/png.png
--------------------------------------------------------------------------------
/examples/basic/handler.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const path = require("path")
3 | const StaticFileHandler = require("serverless-aws-static-file-handler")
4 | const clientFilesPath = path.join(__dirname, "./data-files/")
5 | const fileHandler = new StaticFileHandler(clientFilesPath)
6 |
7 | module.exports.root = async (event, context) => {
8 | event.path = "index.html" // forcing a specific page for this handler; ignore requested path
9 | return fileHandler.get(event, context)
10 | }
11 |
12 | module.exports.v2_root = async (event, context) => {
13 | event.rawPath = "index.html" // forcing a specific page for this handler; ignore requested path
14 | return fileHandler.get(event, context)
15 | }
16 |
17 | module.exports.binary = async (event, context) => {
18 | if (!event.path.startsWith("/binary/")) {
19 | throw new Error(`[404] Invalid filepath for this resource`)
20 | }
21 | return fileHandler.get(event, context)
22 | }
23 |
24 | module.exports.v2_binary = async (event, context) => {
25 | if (!event.rawPath.startsWith("/v2/binary/")) {
26 | throw new Error(`[404] Invalid filepath for this resource`)
27 | }
28 | return fileHandler.get(event, context)
29 | }
30 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "static-file-handler-demo-proxy",
3 | "private": true,
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "deploy": "serverless deploy --aws-profile serverless",
9 | "destroy": "serverless remove --aws-profile serverless",
10 | "lint": "prettier --write \"./**/*.{js,md,yml,json,html}\"",
11 | "reset": "rm -rfd ./node_modules/ && npm i && npm run deploy",
12 | "//dev-link": "Useful for debugging but don't commit it in this way; use `npm run dev-unlink` before committing. NOTE: Not using `npm link` or `npm add ../..` because that creates a filesystem symlink and serverless.com freaks out when attempting a deploy with a symlinked dependency.",
13 | "dev-link": "pushd . ; cd ../.. ; npm pack ; popd ; npm add ../../serverless-aws-static-file-handler-0.0.0.tgz",
14 | "dev-unlink": "npm rm serverless-aws-static-file-handler ; npm add serverless-aws-static-file-handler@>=3.0.2-beta.1"
15 | },
16 | "devDependencies": {
17 | "serverless": "^3.37.0"
18 | },
19 | "dependencies": {
20 | "serverless-aws-static-file-handler": ">=3.0.2-beta.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/basic/serverless.yml:
--------------------------------------------------------------------------------
1 | service: static-file-handler-demo
2 |
3 | plugins:
4 | - serverless-aws-static-file-handler/plugins/BinaryMediaTypes
5 |
6 | custom:
7 | apiGateway:
8 | binaryMediaTypes:
9 | # You can use the wildcard character (*) to cover multiple media types per https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
10 | # NOTE: Using */* has a side effect as noted at https://github.com/activescott/serverless-aws-static-file-handler
11 | # IANA descrete type wildcards from: https://www.iana.org/assignments/media-types/media-types.xhtml
12 | - application/*
13 | - audio/*
14 | - font/*
15 | - image/*
16 | - video/*
17 |
18 | provider:
19 | name: aws
20 | runtime: nodejs20.x
21 | lambdaHashingVersion: 20201221
22 |
23 | functions:
24 | html:
25 | handler: handler.root
26 | events:
27 | - http:
28 | path: /
29 | method: get
30 |
31 | binary:
32 | handler: handler.binary
33 | events:
34 | - http:
35 | path: /binary/{pathvar+}
36 | method: get
37 |
38 | # API Gateway V2 Payload or HTTP API are below. As described at https://www.serverless.com/framework/docs/providers/aws/events/http-api
39 | v2_html:
40 | handler: handler.v2_root
41 | events:
42 | - httpApi:
43 | path: /v2
44 | method: get
45 |
46 | v2_binary:
47 | handler: handler.v2_binary
48 | events:
49 | - httpApi:
50 | path: /v2/binary/{pathvar+}
51 | method: get
52 |
--------------------------------------------------------------------------------
/examples/serverless-offline/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/examples/serverless-offline/README.md:
--------------------------------------------------------------------------------
1 | To see this example take the following steps at the shell in the root of this example's folder:
2 |
3 | 1. Run `npm install` to install dependencies
4 | 2. Run `./node_modules/.bin/serverless offline start` to start serverless-offline running locally.
5 | 3. Open the root endpoint displayed by serverless-offline it successfully starts (usually http://localhost:3000)
6 |
--------------------------------------------------------------------------------
/examples/serverless-offline/data-files/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/serverless-offline/data-files/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/examples/serverless-offline/data-files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
20 |
21 | These should all load and either be a valid image or a font:
22 |
23 |
24 |
25 | These each have a path that does not have a route (http
event)
26 | setup in serverless.yml. They should all be returning
27 | 403 OR
28 | 404: Not Found (API Gateway in prod reports
29 | them as 403, but serverless-offline reports them as 404):
30 |
31 |
32 |
33 | These each have a valid route (http
event) setup in
34 | serverless.yml, but the file doesn't exist on disk. They should all be
35 | returning 404: Not Found :
36 |
37 |
38 |
160 |
161 |
--------------------------------------------------------------------------------
/examples/serverless-offline/data-files/jpg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/serverless-offline/data-files/jpg.jpg
--------------------------------------------------------------------------------
/examples/serverless-offline/data-files/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/serverless-offline/data-files/png.png
--------------------------------------------------------------------------------
/examples/serverless-offline/data-files/subdir/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/examples/serverless-offline/data-files/subdir/png.png
--------------------------------------------------------------------------------
/examples/serverless-offline/handler.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const path = require("path")
3 | const StaticFileHandler = require("serverless-aws-static-file-handler")
4 | const clientFilesPath = path.join(__dirname, "./data-files/")
5 | const fileHandler = new StaticFileHandler(clientFilesPath)
6 |
7 | module.exports.root = async (event, context) => {
8 | event.path = "index.html" // forcing a specific page for this handler; ignore requested path
9 | return fileHandler.get(event, context)
10 | }
11 |
12 | module.exports.binary = async (event, context) => {
13 | if (!event.path.startsWith("/binary/")) {
14 | throw new Error(`[404] Invalid filepath for this resource: ${fname}`)
15 | }
16 | return fileHandler.get(event, context)
17 | }
18 |
--------------------------------------------------------------------------------
/examples/serverless-offline/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "static-file-handler-demo-offline",
3 | "private": true,
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "deploy": "serverless deploy --aws-profile serverless",
9 | "destroy": "serverless remove --aws-profile serverless",
10 | "reset": "rm -rfd ./node_modules/ && npm i && npm run deploy",
11 | "offline": "serverless offline",
12 | "dev-link": "npm link ../../",
13 | "dev-unlink": "npm i"
14 | },
15 | "devDependencies": {
16 | "serverless": "^3.37.0",
17 | "serverless-offline": "^13.3.0"
18 | },
19 | "dependencies": {
20 | "serverless-aws-static-file-handler": ">=3.0.2-beta.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/serverless-offline/serverless.yml:
--------------------------------------------------------------------------------
1 | service: static-file-handler-demo-offline
2 |
3 | plugins:
4 | - serverless-offline
5 | - serverless-aws-static-file-handler/plugins/BinaryMediaTypes
6 |
7 | custom:
8 | apiGateway:
9 | binaryMediaTypes:
10 | # You can use the wildcard character (*) to cover multiple media types per https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
11 | # NOTE: Using */* has a side effect as noted at https://github.com/activescott/serverless-aws-static-file-handler
12 | # IANA descrete type wildcards from: https://www.iana.org/assignments/media-types/media-types.xhtml
13 | - application/*
14 | - audio/*
15 | - font/*
16 | - image/*
17 | - video/*
18 |
19 | provider:
20 | name: aws
21 | runtime: nodejs20.x
22 | lambdaHashingVersion: 20201221
23 |
24 | functions:
25 | html:
26 | handler: handler.root
27 | events:
28 | - http:
29 | path: /
30 | method: get
31 |
32 | binary:
33 | handler: handler.binary
34 | events:
35 | - http:
36 | path: /binary/{pathvar+}
37 | method: get
38 |
--------------------------------------------------------------------------------
/greenkeeper.json:
--------------------------------------------------------------------------------
1 | {
2 | "groups": {
3 | "default": {
4 | "packages": [
5 | "examples/basic/package.json",
6 | "examples/serverless-offline/package.json",
7 | "package.json"
8 | ]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serverless-aws-static-file-handler",
3 | "description": "Easily serve static files with the Serverless Framework on AWS Lambda.",
4 | "homepage": "https://github.com/activescott/serverless-aws-static-file-handler#readme",
5 | "version": "0.0.0",
6 | "main": "src/StaticFileHandler.js",
7 | "author": {
8 | "name": "Scott Willeke",
9 | "email": "scott@willeke.com",
10 | "url": "https://scott.willeke.com/"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/activescott/serverless-aws-static-file-handler.git"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/activescott/serverless-aws-static-file-handler/issues"
18 | },
19 | "keywords": [
20 | "aws-lambda",
21 | "binary",
22 | "nodejs",
23 | "serverless",
24 | "serverless-architectures",
25 | "serverless-framework",
26 | "serverless-functions"
27 | ],
28 | "dependencies": {
29 | "mime-types": "^3.0.1",
30 | "mustache": "^4.0.0"
31 | },
32 | "devDependencies": {
33 | "chai": "^4.1.2",
34 | "chai-as-promised": "^7.1.1",
35 | "husky": "^7.0.0",
36 | "js-yaml": "^4.0.0",
37 | "mocha": "^11.1.0",
38 | "nyc": "^15.0.0",
39 | "prettier": "^2.0.0",
40 | "serverless": "^3.37.0",
41 | "sinon": "^11.1.2"
42 | },
43 | "engines": {
44 | "node": ">=20"
45 | },
46 | "files": [
47 | "src/error.html",
48 | "src/plugins/BinaryMediaTypes.js",
49 | "src/StaticFileHandler.js",
50 | "plugins/BinaryMediaTypes.js"
51 | ],
52 | "scripts": {
53 | "test": "nyc ./node_modules/.bin/mocha './src/test/*.js'",
54 | "pretest": "npm run lint && npm version --allow-same-version --no-git-tag-version 0.0.0 && npm pack",
55 | "lint": "[ \"$CI_NODE_VERSION\" = \"8\" ] || prettier -l \"**/*.{js,md,yml,json,html}\"",
56 | "lint-fix": "prettier --write \"**/*.{js,md,yml,yaml,json,html}\"",
57 | "prepare": "husky install"
58 | },
59 | "license": "MIT"
60 | }
61 |
--------------------------------------------------------------------------------
/plugins/BinaryMediaTypes.js:
--------------------------------------------------------------------------------
1 | const BinaryMediaTypes = require("../src/plugins/BinaryMediaTypes")
2 |
3 | module.exports = BinaryMediaTypes
4 |
--------------------------------------------------------------------------------
/pre-commit.sh:
--------------------------------------------------------------------------------
1 | # RUN `ln -sf ../../pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit` to set this up as the git pre-commit hook
2 |
3 | die () {
4 | echo ""
5 | echo "git pre-commit hook FAILED! See above for details."
6 | echo ""
7 | popd
8 | exit $@
9 | }
10 |
11 | pushd .
12 | cd src
13 | npm run lint || die $?
14 | npm run test || die $?
15 |
--------------------------------------------------------------------------------
/release.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // https://github.com/semantic-release/semantic-release/blob/beta/docs/usage/configuration.md
3 | branches: [
4 | "+([0-9])?(.{+([0-9]),x}).x",
5 | "main",
6 | "next",
7 | "next-major",
8 | { name: "beta", prerelease: true },
9 | { name: "alpha", prerelease: true },
10 | ],
11 | }
12 |
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/.gitignore:
--------------------------------------------------------------------------------
1 | responses/
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/README.md:
--------------------------------------------------------------------------------
1 | # approve-dependabot-deploys
2 |
3 | A script to automatically approve deployment requests from dependabot to the aws environment.
4 |
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/approve-dependabot-deploys.ts:
--------------------------------------------------------------------------------
1 | // https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository
2 | // https://docs.github.com/en/rest/actions/workflow-runs#review-pending-deployments-for-a-workflow-run
3 | // https://docs.github.com/en/rest/deployments/statuses
4 | // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#status
5 | // https://github.com/octokit/core.js
6 |
7 | import { writeFile, mkdir} from "node:fs/promises"
8 | import { Octokit } from "octokit"
9 | import { Endpoints } from "@octokit/types"
10 | import * as dotenv from "dotenv"
11 | dotenv.config()
12 |
13 | if (!process.env.GITHUB_TOKEN) {
14 | throw new Error("You must have the `GITHUB_TOKEN` environment variable specified with a github personal access token.")
15 | }
16 |
17 | const baseOptions = {
18 | owner: "activescott",
19 | repo: "serverless-aws-static-file-handler",
20 | }
21 |
22 | const octokit = new Octokit({
23 | auth: process.env.GITHUB_TOKEN,
24 | })
25 |
26 | async function dumpResponse(method: "GET" | "POST" | "PUT", pathAfterRepo: string, response: Promise): Promise {
27 | pathAfterRepo = pathAfterRepo.startsWith("/") ? pathAfterRepo : "/" + pathAfterRepo
28 | pathAfterRepo = pathAfterRepo.replace(/\//g, "-")
29 | await mkdir("./responses", { recursive: true })
30 | await writeFile(
31 | `./responses/${method}-repos-${baseOptions.owner}-${baseOptions.repo}${pathAfterRepo}.json`,
32 | JSON.stringify(await response, null, " ")
33 | )
34 | }
35 |
36 | async function getWaitingWorkflowRuns(): Promise {
37 | const runs = octokit.request("GET /repos/{owner}/{repo}/actions/runs", {
38 | ...baseOptions,
39 | status: "waiting",
40 | })
41 | await dumpResponse("GET", `actions/runs`, runs)
42 | return runs
43 | }
44 |
45 | async function getPendingDeploymentsForRun(run_id: number) {
46 | const deploys = octokit.request(
47 | "GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments",
48 | {
49 | ...baseOptions,
50 | run_id: run_id,
51 | }
52 | )
53 | await dumpResponse("GET", `actions/runs/${run_id}/pending_deployments`, deploys)
54 | return deploys
55 | }
56 |
57 | async function approveDeployment(run, environment) {
58 | console.log(
59 | `Approving deployment to ${environment.name} triggered by ${run.actor.login} for run ${run.display_title}...`
60 | )
61 | await octokit.request(
62 | "POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments",
63 | {
64 | ...baseOptions,
65 | run_id: run.id,
66 | environment_ids: [environment.id],
67 | state: "approved",
68 | comment: "approved by approve-dependabot-deploys script",
69 | }
70 | )
71 | console.log(
72 | `SUCCESS approving deployment to ${environment.name} for run ${run.display_title}.`
73 | )
74 | }
75 |
76 | const waitingRunsResponse = await getWaitingWorkflowRuns()
77 | const runs = waitingRunsResponse.data.workflow_runs
78 |
79 | const deploysToApprove = runs
80 | .map(async (run) => {
81 | // ID for login "dependabot[bot]"
82 | const DEPENDABOT_USER_ID = 49699333
83 | const ACTIVESCOTT_USER_ID = 213716
84 | const approvableActors = [DEPENDABOT_USER_ID, ACTIVESCOTT_USER_ID]
85 | if (!approvableActors.includes(run.actor.id)) {
86 | console.log(`Run ${run.id} is pending approval, but from an unknown actor: ${run.actor.login} (${run.actor.id})`)
87 | return null
88 | }
89 | console.log(`A run created by ${run.actor.login} is awaiting deployment: ${run.display_title}. Confirming that it is an expected environment and this user has permission to approve...`)
90 | const deploys = await getPendingDeploymentsForRun(run.id)
91 | const approvable = deploys.data.filter((deploy) => {
92 | const approvableEnvironments = ["aws"]
93 | if (!approvableEnvironments.includes(deploy.environment.name)) {
94 | console.log(`Environment '${deploy.environment.name}' not approvable for run ${run.display_title}.`)
95 | return false
96 | }
97 | if (!deploy.current_user_can_approve) {
98 | console.log(`The current user does not have permission to approve deployment for environment '${deploy.environment.name}'.`)
99 | return false
100 | }
101 | return true
102 | })
103 | if (approvable.length > 0) {
104 | const deploy = approvable[0]
105 | // console.log(`Found pending deploy for run ${run.id}:`, deploy)
106 | return {
107 | environment: deploy.environment,
108 | run,
109 | }
110 | } else {
111 | return null
112 | }
113 | })
114 | .filter((deploy) => deploy !== null)
115 |
116 | console.log(`Found ${deploysToApprove.length} deploys to approve.`)
117 |
118 | for await (const deploy of deploysToApprove) {
119 | await approveDeployment(deploy.run, deploy.environment)
120 | }
121 |
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "approve-dependabot-deploys",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "approve-dependabot-deploys",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "dotenv": "^16.0.3",
13 | "octokit": "^3.1.2",
14 | "ts-node": "^10.9.1"
15 | }
16 | },
17 | "node_modules/@cspotcode/source-map-support": {
18 | "version": "0.8.1",
19 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
20 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
21 | "dependencies": {
22 | "@jridgewell/trace-mapping": "0.3.9"
23 | },
24 | "engines": {
25 | "node": ">=12"
26 | }
27 | },
28 | "node_modules/@jridgewell/resolve-uri": {
29 | "version": "3.1.0",
30 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
31 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
32 | "engines": {
33 | "node": ">=6.0.0"
34 | }
35 | },
36 | "node_modules/@jridgewell/sourcemap-codec": {
37 | "version": "1.4.14",
38 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
39 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
40 | },
41 | "node_modules/@jridgewell/trace-mapping": {
42 | "version": "0.3.9",
43 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
44 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
45 | "dependencies": {
46 | "@jridgewell/resolve-uri": "^3.0.3",
47 | "@jridgewell/sourcemap-codec": "^1.4.10"
48 | }
49 | },
50 | "node_modules/@octokit/app": {
51 | "version": "14.0.2",
52 | "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.0.2.tgz",
53 | "integrity": "sha512-NCSCktSx+XmjuSUVn2dLfqQ9WIYePGP95SDJs4I9cn/0ZkeXcPkaoCLl64Us3dRKL2ozC7hArwze5Eu+/qt1tg==",
54 | "dependencies": {
55 | "@octokit/auth-app": "^6.0.0",
56 | "@octokit/auth-unauthenticated": "^5.0.0",
57 | "@octokit/core": "^5.0.0",
58 | "@octokit/oauth-app": "^6.0.0",
59 | "@octokit/plugin-paginate-rest": "^9.0.0",
60 | "@octokit/types": "^12.0.0",
61 | "@octokit/webhooks": "^12.0.4"
62 | },
63 | "engines": {
64 | "node": ">= 18"
65 | }
66 | },
67 | "node_modules/@octokit/auth-app": {
68 | "version": "6.0.1",
69 | "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.0.1.tgz",
70 | "integrity": "sha512-tjCD4nzQNZgmLH62+PSnTF6eGerisFgV4v6euhqJik6yWV96e1ZiiGj+NXIqbgnpjLmtnBqVUrNyGKu3DoGEGA==",
71 | "dependencies": {
72 | "@octokit/auth-oauth-app": "^7.0.0",
73 | "@octokit/auth-oauth-user": "^4.0.0",
74 | "@octokit/request": "^8.0.2",
75 | "@octokit/request-error": "^5.0.0",
76 | "@octokit/types": "^12.0.0",
77 | "deprecation": "^2.3.1",
78 | "lru-cache": "^10.0.0",
79 | "universal-github-app-jwt": "^1.1.1",
80 | "universal-user-agent": "^6.0.0"
81 | },
82 | "engines": {
83 | "node": ">= 18"
84 | }
85 | },
86 | "node_modules/@octokit/auth-oauth-app": {
87 | "version": "7.0.1",
88 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.1.tgz",
89 | "integrity": "sha512-RE0KK0DCjCHXHlQBoubwlLijXEKfhMhKm9gO56xYvFmP1QTMb+vvwRPmQLLx0V+5AvV9N9I3lr1WyTzwL3rMDg==",
90 | "dependencies": {
91 | "@octokit/auth-oauth-device": "^6.0.0",
92 | "@octokit/auth-oauth-user": "^4.0.0",
93 | "@octokit/request": "^8.0.2",
94 | "@octokit/types": "^12.0.0",
95 | "@types/btoa-lite": "^1.0.0",
96 | "btoa-lite": "^1.0.0",
97 | "universal-user-agent": "^6.0.0"
98 | },
99 | "engines": {
100 | "node": ">= 18"
101 | }
102 | },
103 | "node_modules/@octokit/auth-oauth-device": {
104 | "version": "6.0.1",
105 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.1.tgz",
106 | "integrity": "sha512-yxU0rkL65QkjbqQedgVx3gmW7YM5fF+r5uaSj9tM/cQGVqloXcqP2xK90eTyYvl29arFVCW8Vz4H/t47mL0ELw==",
107 | "dependencies": {
108 | "@octokit/oauth-methods": "^4.0.0",
109 | "@octokit/request": "^8.0.0",
110 | "@octokit/types": "^12.0.0",
111 | "universal-user-agent": "^6.0.0"
112 | },
113 | "engines": {
114 | "node": ">= 18"
115 | }
116 | },
117 | "node_modules/@octokit/auth-oauth-user": {
118 | "version": "4.0.1",
119 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.1.tgz",
120 | "integrity": "sha512-N94wWW09d0hleCnrO5wt5MxekatqEJ4zf+1vSe8MKMrhZ7gAXKFOKrDEZW2INltvBWJCyDUELgGRv8gfErH1Iw==",
121 | "dependencies": {
122 | "@octokit/auth-oauth-device": "^6.0.0",
123 | "@octokit/oauth-methods": "^4.0.0",
124 | "@octokit/request": "^8.0.2",
125 | "@octokit/types": "^12.0.0",
126 | "btoa-lite": "^1.0.0",
127 | "universal-user-agent": "^6.0.0"
128 | },
129 | "engines": {
130 | "node": ">= 18"
131 | }
132 | },
133 | "node_modules/@octokit/auth-token": {
134 | "version": "4.0.0",
135 | "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
136 | "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
137 | "engines": {
138 | "node": ">= 18"
139 | }
140 | },
141 | "node_modules/@octokit/auth-unauthenticated": {
142 | "version": "5.0.1",
143 | "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz",
144 | "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==",
145 | "dependencies": {
146 | "@octokit/request-error": "^5.0.0",
147 | "@octokit/types": "^12.0.0"
148 | },
149 | "engines": {
150 | "node": ">= 18"
151 | }
152 | },
153 | "node_modules/@octokit/core": {
154 | "version": "5.0.2",
155 | "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz",
156 | "integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==",
157 | "dependencies": {
158 | "@octokit/auth-token": "^4.0.0",
159 | "@octokit/graphql": "^7.0.0",
160 | "@octokit/request": "^8.0.2",
161 | "@octokit/request-error": "^5.0.0",
162 | "@octokit/types": "^12.0.0",
163 | "before-after-hook": "^2.2.0",
164 | "universal-user-agent": "^6.0.0"
165 | },
166 | "engines": {
167 | "node": ">= 18"
168 | }
169 | },
170 | "node_modules/@octokit/endpoint": {
171 | "version": "9.0.6",
172 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
173 | "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
174 | "license": "MIT",
175 | "dependencies": {
176 | "@octokit/types": "^13.1.0",
177 | "universal-user-agent": "^6.0.0"
178 | },
179 | "engines": {
180 | "node": ">= 18"
181 | }
182 | },
183 | "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
184 | "version": "24.2.0",
185 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
186 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
187 | "license": "MIT"
188 | },
189 | "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
190 | "version": "13.10.0",
191 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
192 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
193 | "license": "MIT",
194 | "dependencies": {
195 | "@octokit/openapi-types": "^24.2.0"
196 | }
197 | },
198 | "node_modules/@octokit/graphql": {
199 | "version": "7.0.2",
200 | "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
201 | "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
202 | "dependencies": {
203 | "@octokit/request": "^8.0.1",
204 | "@octokit/types": "^12.0.0",
205 | "universal-user-agent": "^6.0.0"
206 | },
207 | "engines": {
208 | "node": ">= 18"
209 | }
210 | },
211 | "node_modules/@octokit/oauth-app": {
212 | "version": "6.0.0",
213 | "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.0.0.tgz",
214 | "integrity": "sha512-bNMkS+vJ6oz2hCyraT9ZfTpAQ8dZNqJJQVNaKjPLx4ue5RZiFdU1YWXguOPR8AaSHS+lKe+lR3abn2siGd+zow==",
215 | "dependencies": {
216 | "@octokit/auth-oauth-app": "^7.0.0",
217 | "@octokit/auth-oauth-user": "^4.0.0",
218 | "@octokit/auth-unauthenticated": "^5.0.0",
219 | "@octokit/core": "^5.0.0",
220 | "@octokit/oauth-authorization-url": "^6.0.2",
221 | "@octokit/oauth-methods": "^4.0.0",
222 | "@types/aws-lambda": "^8.10.83",
223 | "universal-user-agent": "^6.0.0"
224 | },
225 | "engines": {
226 | "node": ">= 18"
227 | }
228 | },
229 | "node_modules/@octokit/oauth-authorization-url": {
230 | "version": "6.0.2",
231 | "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
232 | "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==",
233 | "engines": {
234 | "node": ">= 18"
235 | }
236 | },
237 | "node_modules/@octokit/oauth-methods": {
238 | "version": "4.0.1",
239 | "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.0.1.tgz",
240 | "integrity": "sha512-1NdTGCoBHyD6J0n2WGXg9+yDLZrRNZ0moTEex/LSPr49m530WNKcCfXDghofYptr3st3eTii+EHoG5k/o+vbtw==",
241 | "dependencies": {
242 | "@octokit/oauth-authorization-url": "^6.0.2",
243 | "@octokit/request": "^8.0.2",
244 | "@octokit/request-error": "^5.0.0",
245 | "@octokit/types": "^12.0.0",
246 | "btoa-lite": "^1.0.0"
247 | },
248 | "engines": {
249 | "node": ">= 18"
250 | }
251 | },
252 | "node_modules/@octokit/openapi-types": {
253 | "version": "20.0.0",
254 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
255 | "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
256 | "license": "MIT"
257 | },
258 | "node_modules/@octokit/plugin-paginate-graphql": {
259 | "version": "4.0.0",
260 | "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz",
261 | "integrity": "sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA==",
262 | "engines": {
263 | "node": ">= 18"
264 | },
265 | "peerDependencies": {
266 | "@octokit/core": ">=5"
267 | }
268 | },
269 | "node_modules/@octokit/plugin-paginate-rest": {
270 | "version": "9.2.2",
271 | "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
272 | "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
273 | "license": "MIT",
274 | "dependencies": {
275 | "@octokit/types": "^12.6.0"
276 | },
277 | "engines": {
278 | "node": ">= 18"
279 | },
280 | "peerDependencies": {
281 | "@octokit/core": "5"
282 | }
283 | },
284 | "node_modules/@octokit/plugin-rest-endpoint-methods": {
285 | "version": "10.2.0",
286 | "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz",
287 | "integrity": "sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==",
288 | "dependencies": {
289 | "@octokit/types": "^12.3.0"
290 | },
291 | "engines": {
292 | "node": ">= 18"
293 | },
294 | "peerDependencies": {
295 | "@octokit/core": ">=5"
296 | }
297 | },
298 | "node_modules/@octokit/plugin-retry": {
299 | "version": "6.0.1",
300 | "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
301 | "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==",
302 | "dependencies": {
303 | "@octokit/request-error": "^5.0.0",
304 | "@octokit/types": "^12.0.0",
305 | "bottleneck": "^2.15.3"
306 | },
307 | "engines": {
308 | "node": ">= 18"
309 | },
310 | "peerDependencies": {
311 | "@octokit/core": ">=5"
312 | }
313 | },
314 | "node_modules/@octokit/plugin-throttling": {
315 | "version": "8.1.3",
316 | "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.1.3.tgz",
317 | "integrity": "sha512-pfyqaqpc0EXh5Cn4HX9lWYsZ4gGbjnSmUILeu4u2gnuM50K/wIk9s1Pxt3lVeVwekmITgN/nJdoh43Ka+vye8A==",
318 | "dependencies": {
319 | "@octokit/types": "^12.2.0",
320 | "bottleneck": "^2.15.3"
321 | },
322 | "engines": {
323 | "node": ">= 18"
324 | },
325 | "peerDependencies": {
326 | "@octokit/core": "^5.0.0"
327 | }
328 | },
329 | "node_modules/@octokit/request": {
330 | "version": "8.4.1",
331 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
332 | "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
333 | "license": "MIT",
334 | "dependencies": {
335 | "@octokit/endpoint": "^9.0.6",
336 | "@octokit/request-error": "^5.1.1",
337 | "@octokit/types": "^13.1.0",
338 | "universal-user-agent": "^6.0.0"
339 | },
340 | "engines": {
341 | "node": ">= 18"
342 | }
343 | },
344 | "node_modules/@octokit/request-error": {
345 | "version": "5.1.1",
346 | "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
347 | "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
348 | "license": "MIT",
349 | "dependencies": {
350 | "@octokit/types": "^13.1.0",
351 | "deprecation": "^2.0.0",
352 | "once": "^1.4.0"
353 | },
354 | "engines": {
355 | "node": ">= 18"
356 | }
357 | },
358 | "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
359 | "version": "24.2.0",
360 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
361 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
362 | "license": "MIT"
363 | },
364 | "node_modules/@octokit/request-error/node_modules/@octokit/types": {
365 | "version": "13.10.0",
366 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
367 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
368 | "license": "MIT",
369 | "dependencies": {
370 | "@octokit/openapi-types": "^24.2.0"
371 | }
372 | },
373 | "node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
374 | "version": "24.2.0",
375 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
376 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
377 | "license": "MIT"
378 | },
379 | "node_modules/@octokit/request/node_modules/@octokit/types": {
380 | "version": "13.10.0",
381 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
382 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
383 | "license": "MIT",
384 | "dependencies": {
385 | "@octokit/openapi-types": "^24.2.0"
386 | }
387 | },
388 | "node_modules/@octokit/types": {
389 | "version": "12.6.0",
390 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
391 | "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
392 | "license": "MIT",
393 | "dependencies": {
394 | "@octokit/openapi-types": "^20.0.0"
395 | }
396 | },
397 | "node_modules/@octokit/webhooks": {
398 | "version": "12.0.10",
399 | "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.0.10.tgz",
400 | "integrity": "sha512-Q8d26l7gZ3L1SSr25NFbbP0B431sovU5r0tIqcvy8Z4PrD1LBv0cJEjvDLOieouzPSTzSzufzRIeXD7S+zAESA==",
401 | "dependencies": {
402 | "@octokit/request-error": "^5.0.0",
403 | "@octokit/webhooks-methods": "^4.0.0",
404 | "@octokit/webhooks-types": "7.1.0",
405 | "aggregate-error": "^3.1.0"
406 | },
407 | "engines": {
408 | "node": ">= 18"
409 | }
410 | },
411 | "node_modules/@octokit/webhooks-methods": {
412 | "version": "4.0.0",
413 | "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz",
414 | "integrity": "sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw==",
415 | "engines": {
416 | "node": ">= 18"
417 | }
418 | },
419 | "node_modules/@octokit/webhooks-types": {
420 | "version": "7.1.0",
421 | "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz",
422 | "integrity": "sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w=="
423 | },
424 | "node_modules/@tsconfig/node10": {
425 | "version": "1.0.9",
426 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
427 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
428 | },
429 | "node_modules/@tsconfig/node12": {
430 | "version": "1.0.11",
431 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
432 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
433 | },
434 | "node_modules/@tsconfig/node14": {
435 | "version": "1.0.3",
436 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
437 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
438 | },
439 | "node_modules/@tsconfig/node16": {
440 | "version": "1.0.3",
441 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
442 | "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
443 | },
444 | "node_modules/@types/aws-lambda": {
445 | "version": "8.10.130",
446 | "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.130.tgz",
447 | "integrity": "sha512-HxTfLeGvD1wTJqIGwcBCpNmHKenja+We1e0cuzeIDFfbEj3ixnlTInyPR/81zAe0Ss/Ip12rFK6XNeMLVucOSg=="
448 | },
449 | "node_modules/@types/btoa-lite": {
450 | "version": "1.0.2",
451 | "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
452 | "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="
453 | },
454 | "node_modules/@types/jsonwebtoken": {
455 | "version": "9.0.5",
456 | "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz",
457 | "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==",
458 | "dependencies": {
459 | "@types/node": "*"
460 | }
461 | },
462 | "node_modules/@types/node": {
463 | "version": "18.8.0",
464 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.0.tgz",
465 | "integrity": "sha512-u+h43R6U8xXDt2vzUaVP3VwjjLyOJk6uEciZS8OSyziUQGOwmk+l+4drxcsDboHXwyTaqS1INebghmWMRxq3LA=="
466 | },
467 | "node_modules/acorn": {
468 | "version": "8.8.0",
469 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
470 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
471 | "bin": {
472 | "acorn": "bin/acorn"
473 | },
474 | "engines": {
475 | "node": ">=0.4.0"
476 | }
477 | },
478 | "node_modules/acorn-walk": {
479 | "version": "8.2.0",
480 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
481 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
482 | "engines": {
483 | "node": ">=0.4.0"
484 | }
485 | },
486 | "node_modules/aggregate-error": {
487 | "version": "3.1.0",
488 | "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
489 | "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
490 | "dependencies": {
491 | "clean-stack": "^2.0.0",
492 | "indent-string": "^4.0.0"
493 | },
494 | "engines": {
495 | "node": ">=8"
496 | }
497 | },
498 | "node_modules/arg": {
499 | "version": "4.1.3",
500 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
501 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
502 | },
503 | "node_modules/before-after-hook": {
504 | "version": "2.2.3",
505 | "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
506 | "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
507 | },
508 | "node_modules/bottleneck": {
509 | "version": "2.19.5",
510 | "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
511 | "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
512 | },
513 | "node_modules/btoa-lite": {
514 | "version": "1.0.0",
515 | "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
516 | "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
517 | },
518 | "node_modules/buffer-equal-constant-time": {
519 | "version": "1.0.1",
520 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
521 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
522 | },
523 | "node_modules/clean-stack": {
524 | "version": "2.2.0",
525 | "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
526 | "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
527 | "engines": {
528 | "node": ">=6"
529 | }
530 | },
531 | "node_modules/create-require": {
532 | "version": "1.1.1",
533 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
534 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
535 | },
536 | "node_modules/deprecation": {
537 | "version": "2.3.1",
538 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
539 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
540 | },
541 | "node_modules/diff": {
542 | "version": "4.0.2",
543 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
544 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
545 | "engines": {
546 | "node": ">=0.3.1"
547 | }
548 | },
549 | "node_modules/dotenv": {
550 | "version": "16.0.3",
551 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
552 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
553 | "engines": {
554 | "node": ">=12"
555 | }
556 | },
557 | "node_modules/ecdsa-sig-formatter": {
558 | "version": "1.0.11",
559 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
560 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
561 | "dependencies": {
562 | "safe-buffer": "^5.0.1"
563 | }
564 | },
565 | "node_modules/indent-string": {
566 | "version": "4.0.0",
567 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
568 | "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
569 | "engines": {
570 | "node": ">=8"
571 | }
572 | },
573 | "node_modules/jsonwebtoken": {
574 | "version": "9.0.2",
575 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
576 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
577 | "dependencies": {
578 | "jws": "^3.2.2",
579 | "lodash.includes": "^4.3.0",
580 | "lodash.isboolean": "^3.0.3",
581 | "lodash.isinteger": "^4.0.4",
582 | "lodash.isnumber": "^3.0.3",
583 | "lodash.isplainobject": "^4.0.6",
584 | "lodash.isstring": "^4.0.1",
585 | "lodash.once": "^4.0.0",
586 | "ms": "^2.1.1",
587 | "semver": "^7.5.4"
588 | },
589 | "engines": {
590 | "node": ">=12",
591 | "npm": ">=6"
592 | }
593 | },
594 | "node_modules/jwa": {
595 | "version": "1.4.1",
596 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
597 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
598 | "dependencies": {
599 | "buffer-equal-constant-time": "1.0.1",
600 | "ecdsa-sig-formatter": "1.0.11",
601 | "safe-buffer": "^5.0.1"
602 | }
603 | },
604 | "node_modules/jws": {
605 | "version": "3.2.2",
606 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
607 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
608 | "dependencies": {
609 | "jwa": "^1.4.1",
610 | "safe-buffer": "^5.0.1"
611 | }
612 | },
613 | "node_modules/lodash.includes": {
614 | "version": "4.3.0",
615 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
616 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
617 | },
618 | "node_modules/lodash.isboolean": {
619 | "version": "3.0.3",
620 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
621 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
622 | },
623 | "node_modules/lodash.isinteger": {
624 | "version": "4.0.4",
625 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
626 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
627 | },
628 | "node_modules/lodash.isnumber": {
629 | "version": "3.0.3",
630 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
631 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
632 | },
633 | "node_modules/lodash.isplainobject": {
634 | "version": "4.0.6",
635 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
636 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
637 | },
638 | "node_modules/lodash.isstring": {
639 | "version": "4.0.1",
640 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
641 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
642 | },
643 | "node_modules/lodash.once": {
644 | "version": "4.1.1",
645 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
646 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
647 | },
648 | "node_modules/lru-cache": {
649 | "version": "10.1.0",
650 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz",
651 | "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==",
652 | "engines": {
653 | "node": "14 || >=16.14"
654 | }
655 | },
656 | "node_modules/make-error": {
657 | "version": "1.3.6",
658 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
659 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
660 | },
661 | "node_modules/ms": {
662 | "version": "2.1.3",
663 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
664 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
665 | },
666 | "node_modules/octokit": {
667 | "version": "3.1.2",
668 | "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz",
669 | "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==",
670 | "dependencies": {
671 | "@octokit/app": "^14.0.2",
672 | "@octokit/core": "^5.0.0",
673 | "@octokit/oauth-app": "^6.0.0",
674 | "@octokit/plugin-paginate-graphql": "^4.0.0",
675 | "@octokit/plugin-paginate-rest": "^9.0.0",
676 | "@octokit/plugin-rest-endpoint-methods": "^10.0.0",
677 | "@octokit/plugin-retry": "^6.0.0",
678 | "@octokit/plugin-throttling": "^8.0.0",
679 | "@octokit/request-error": "^5.0.0",
680 | "@octokit/types": "^12.0.0"
681 | },
682 | "engines": {
683 | "node": ">= 18"
684 | }
685 | },
686 | "node_modules/once": {
687 | "version": "1.4.0",
688 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
689 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
690 | "dependencies": {
691 | "wrappy": "1"
692 | }
693 | },
694 | "node_modules/safe-buffer": {
695 | "version": "5.2.1",
696 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
697 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
698 | "funding": [
699 | {
700 | "type": "github",
701 | "url": "https://github.com/sponsors/feross"
702 | },
703 | {
704 | "type": "patreon",
705 | "url": "https://www.patreon.com/feross"
706 | },
707 | {
708 | "type": "consulting",
709 | "url": "https://feross.org/support"
710 | }
711 | ]
712 | },
713 | "node_modules/semver": {
714 | "version": "7.5.4",
715 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
716 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
717 | "dependencies": {
718 | "lru-cache": "^6.0.0"
719 | },
720 | "bin": {
721 | "semver": "bin/semver.js"
722 | },
723 | "engines": {
724 | "node": ">=10"
725 | }
726 | },
727 | "node_modules/semver/node_modules/lru-cache": {
728 | "version": "6.0.0",
729 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
730 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
731 | "dependencies": {
732 | "yallist": "^4.0.0"
733 | },
734 | "engines": {
735 | "node": ">=10"
736 | }
737 | },
738 | "node_modules/ts-node": {
739 | "version": "10.9.1",
740 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
741 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
742 | "dependencies": {
743 | "@cspotcode/source-map-support": "^0.8.0",
744 | "@tsconfig/node10": "^1.0.7",
745 | "@tsconfig/node12": "^1.0.7",
746 | "@tsconfig/node14": "^1.0.0",
747 | "@tsconfig/node16": "^1.0.2",
748 | "acorn": "^8.4.1",
749 | "acorn-walk": "^8.1.1",
750 | "arg": "^4.1.0",
751 | "create-require": "^1.1.0",
752 | "diff": "^4.0.1",
753 | "make-error": "^1.1.1",
754 | "v8-compile-cache-lib": "^3.0.1",
755 | "yn": "3.1.1"
756 | },
757 | "bin": {
758 | "ts-node": "dist/bin.js",
759 | "ts-node-cwd": "dist/bin-cwd.js",
760 | "ts-node-esm": "dist/bin-esm.js",
761 | "ts-node-script": "dist/bin-script.js",
762 | "ts-node-transpile-only": "dist/bin-transpile.js",
763 | "ts-script": "dist/bin-script-deprecated.js"
764 | },
765 | "peerDependencies": {
766 | "@swc/core": ">=1.2.50",
767 | "@swc/wasm": ">=1.2.50",
768 | "@types/node": "*",
769 | "typescript": ">=2.7"
770 | },
771 | "peerDependenciesMeta": {
772 | "@swc/core": {
773 | "optional": true
774 | },
775 | "@swc/wasm": {
776 | "optional": true
777 | }
778 | }
779 | },
780 | "node_modules/typescript": {
781 | "version": "4.8.4",
782 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
783 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
784 | "peer": true,
785 | "bin": {
786 | "tsc": "bin/tsc",
787 | "tsserver": "bin/tsserver"
788 | },
789 | "engines": {
790 | "node": ">=4.2.0"
791 | }
792 | },
793 | "node_modules/universal-github-app-jwt": {
794 | "version": "1.1.1",
795 | "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz",
796 | "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==",
797 | "dependencies": {
798 | "@types/jsonwebtoken": "^9.0.0",
799 | "jsonwebtoken": "^9.0.0"
800 | }
801 | },
802 | "node_modules/universal-user-agent": {
803 | "version": "6.0.1",
804 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
805 | "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
806 | },
807 | "node_modules/v8-compile-cache-lib": {
808 | "version": "3.0.1",
809 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
810 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
811 | },
812 | "node_modules/wrappy": {
813 | "version": "1.0.2",
814 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
815 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
816 | },
817 | "node_modules/yallist": {
818 | "version": "4.0.0",
819 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
820 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
821 | },
822 | "node_modules/yn": {
823 | "version": "3.1.1",
824 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
825 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
826 | "engines": {
827 | "node": ">=6"
828 | }
829 | }
830 | },
831 | "dependencies": {
832 | "@cspotcode/source-map-support": {
833 | "version": "0.8.1",
834 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
835 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
836 | "requires": {
837 | "@jridgewell/trace-mapping": "0.3.9"
838 | }
839 | },
840 | "@jridgewell/resolve-uri": {
841 | "version": "3.1.0",
842 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
843 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
844 | },
845 | "@jridgewell/sourcemap-codec": {
846 | "version": "1.4.14",
847 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
848 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
849 | },
850 | "@jridgewell/trace-mapping": {
851 | "version": "0.3.9",
852 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
853 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
854 | "requires": {
855 | "@jridgewell/resolve-uri": "^3.0.3",
856 | "@jridgewell/sourcemap-codec": "^1.4.10"
857 | }
858 | },
859 | "@octokit/app": {
860 | "version": "14.0.2",
861 | "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.0.2.tgz",
862 | "integrity": "sha512-NCSCktSx+XmjuSUVn2dLfqQ9WIYePGP95SDJs4I9cn/0ZkeXcPkaoCLl64Us3dRKL2ozC7hArwze5Eu+/qt1tg==",
863 | "requires": {
864 | "@octokit/auth-app": "^6.0.0",
865 | "@octokit/auth-unauthenticated": "^5.0.0",
866 | "@octokit/core": "^5.0.0",
867 | "@octokit/oauth-app": "^6.0.0",
868 | "@octokit/plugin-paginate-rest": "^9.0.0",
869 | "@octokit/types": "^12.0.0",
870 | "@octokit/webhooks": "^12.0.4"
871 | }
872 | },
873 | "@octokit/auth-app": {
874 | "version": "6.0.1",
875 | "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.0.1.tgz",
876 | "integrity": "sha512-tjCD4nzQNZgmLH62+PSnTF6eGerisFgV4v6euhqJik6yWV96e1ZiiGj+NXIqbgnpjLmtnBqVUrNyGKu3DoGEGA==",
877 | "requires": {
878 | "@octokit/auth-oauth-app": "^7.0.0",
879 | "@octokit/auth-oauth-user": "^4.0.0",
880 | "@octokit/request": "^8.0.2",
881 | "@octokit/request-error": "^5.0.0",
882 | "@octokit/types": "^12.0.0",
883 | "deprecation": "^2.3.1",
884 | "lru-cache": "^10.0.0",
885 | "universal-github-app-jwt": "^1.1.1",
886 | "universal-user-agent": "^6.0.0"
887 | }
888 | },
889 | "@octokit/auth-oauth-app": {
890 | "version": "7.0.1",
891 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.1.tgz",
892 | "integrity": "sha512-RE0KK0DCjCHXHlQBoubwlLijXEKfhMhKm9gO56xYvFmP1QTMb+vvwRPmQLLx0V+5AvV9N9I3lr1WyTzwL3rMDg==",
893 | "requires": {
894 | "@octokit/auth-oauth-device": "^6.0.0",
895 | "@octokit/auth-oauth-user": "^4.0.0",
896 | "@octokit/request": "^8.0.2",
897 | "@octokit/types": "^12.0.0",
898 | "@types/btoa-lite": "^1.0.0",
899 | "btoa-lite": "^1.0.0",
900 | "universal-user-agent": "^6.0.0"
901 | }
902 | },
903 | "@octokit/auth-oauth-device": {
904 | "version": "6.0.1",
905 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.1.tgz",
906 | "integrity": "sha512-yxU0rkL65QkjbqQedgVx3gmW7YM5fF+r5uaSj9tM/cQGVqloXcqP2xK90eTyYvl29arFVCW8Vz4H/t47mL0ELw==",
907 | "requires": {
908 | "@octokit/oauth-methods": "^4.0.0",
909 | "@octokit/request": "^8.0.0",
910 | "@octokit/types": "^12.0.0",
911 | "universal-user-agent": "^6.0.0"
912 | }
913 | },
914 | "@octokit/auth-oauth-user": {
915 | "version": "4.0.1",
916 | "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.1.tgz",
917 | "integrity": "sha512-N94wWW09d0hleCnrO5wt5MxekatqEJ4zf+1vSe8MKMrhZ7gAXKFOKrDEZW2INltvBWJCyDUELgGRv8gfErH1Iw==",
918 | "requires": {
919 | "@octokit/auth-oauth-device": "^6.0.0",
920 | "@octokit/oauth-methods": "^4.0.0",
921 | "@octokit/request": "^8.0.2",
922 | "@octokit/types": "^12.0.0",
923 | "btoa-lite": "^1.0.0",
924 | "universal-user-agent": "^6.0.0"
925 | }
926 | },
927 | "@octokit/auth-token": {
928 | "version": "4.0.0",
929 | "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
930 | "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="
931 | },
932 | "@octokit/auth-unauthenticated": {
933 | "version": "5.0.1",
934 | "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz",
935 | "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==",
936 | "requires": {
937 | "@octokit/request-error": "^5.0.0",
938 | "@octokit/types": "^12.0.0"
939 | }
940 | },
941 | "@octokit/core": {
942 | "version": "5.0.2",
943 | "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz",
944 | "integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==",
945 | "requires": {
946 | "@octokit/auth-token": "^4.0.0",
947 | "@octokit/graphql": "^7.0.0",
948 | "@octokit/request": "^8.0.2",
949 | "@octokit/request-error": "^5.0.0",
950 | "@octokit/types": "^12.0.0",
951 | "before-after-hook": "^2.2.0",
952 | "universal-user-agent": "^6.0.0"
953 | }
954 | },
955 | "@octokit/endpoint": {
956 | "version": "9.0.6",
957 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
958 | "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
959 | "requires": {
960 | "@octokit/types": "^13.1.0",
961 | "universal-user-agent": "^6.0.0"
962 | },
963 | "dependencies": {
964 | "@octokit/openapi-types": {
965 | "version": "24.2.0",
966 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
967 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="
968 | },
969 | "@octokit/types": {
970 | "version": "13.10.0",
971 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
972 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
973 | "requires": {
974 | "@octokit/openapi-types": "^24.2.0"
975 | }
976 | }
977 | }
978 | },
979 | "@octokit/graphql": {
980 | "version": "7.0.2",
981 | "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
982 | "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
983 | "requires": {
984 | "@octokit/request": "^8.0.1",
985 | "@octokit/types": "^12.0.0",
986 | "universal-user-agent": "^6.0.0"
987 | }
988 | },
989 | "@octokit/oauth-app": {
990 | "version": "6.0.0",
991 | "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.0.0.tgz",
992 | "integrity": "sha512-bNMkS+vJ6oz2hCyraT9ZfTpAQ8dZNqJJQVNaKjPLx4ue5RZiFdU1YWXguOPR8AaSHS+lKe+lR3abn2siGd+zow==",
993 | "requires": {
994 | "@octokit/auth-oauth-app": "^7.0.0",
995 | "@octokit/auth-oauth-user": "^4.0.0",
996 | "@octokit/auth-unauthenticated": "^5.0.0",
997 | "@octokit/core": "^5.0.0",
998 | "@octokit/oauth-authorization-url": "^6.0.2",
999 | "@octokit/oauth-methods": "^4.0.0",
1000 | "@types/aws-lambda": "^8.10.83",
1001 | "universal-user-agent": "^6.0.0"
1002 | }
1003 | },
1004 | "@octokit/oauth-authorization-url": {
1005 | "version": "6.0.2",
1006 | "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
1007 | "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA=="
1008 | },
1009 | "@octokit/oauth-methods": {
1010 | "version": "4.0.1",
1011 | "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.0.1.tgz",
1012 | "integrity": "sha512-1NdTGCoBHyD6J0n2WGXg9+yDLZrRNZ0moTEex/LSPr49m530WNKcCfXDghofYptr3st3eTii+EHoG5k/o+vbtw==",
1013 | "requires": {
1014 | "@octokit/oauth-authorization-url": "^6.0.2",
1015 | "@octokit/request": "^8.0.2",
1016 | "@octokit/request-error": "^5.0.0",
1017 | "@octokit/types": "^12.0.0",
1018 | "btoa-lite": "^1.0.0"
1019 | }
1020 | },
1021 | "@octokit/openapi-types": {
1022 | "version": "20.0.0",
1023 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
1024 | "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
1025 | },
1026 | "@octokit/plugin-paginate-graphql": {
1027 | "version": "4.0.0",
1028 | "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz",
1029 | "integrity": "sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA==",
1030 | "requires": {}
1031 | },
1032 | "@octokit/plugin-paginate-rest": {
1033 | "version": "9.2.2",
1034 | "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
1035 | "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
1036 | "requires": {
1037 | "@octokit/types": "^12.6.0"
1038 | }
1039 | },
1040 | "@octokit/plugin-rest-endpoint-methods": {
1041 | "version": "10.2.0",
1042 | "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz",
1043 | "integrity": "sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==",
1044 | "requires": {
1045 | "@octokit/types": "^12.3.0"
1046 | }
1047 | },
1048 | "@octokit/plugin-retry": {
1049 | "version": "6.0.1",
1050 | "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
1051 | "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==",
1052 | "requires": {
1053 | "@octokit/request-error": "^5.0.0",
1054 | "@octokit/types": "^12.0.0",
1055 | "bottleneck": "^2.15.3"
1056 | }
1057 | },
1058 | "@octokit/plugin-throttling": {
1059 | "version": "8.1.3",
1060 | "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.1.3.tgz",
1061 | "integrity": "sha512-pfyqaqpc0EXh5Cn4HX9lWYsZ4gGbjnSmUILeu4u2gnuM50K/wIk9s1Pxt3lVeVwekmITgN/nJdoh43Ka+vye8A==",
1062 | "requires": {
1063 | "@octokit/types": "^12.2.0",
1064 | "bottleneck": "^2.15.3"
1065 | }
1066 | },
1067 | "@octokit/request": {
1068 | "version": "8.4.1",
1069 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
1070 | "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
1071 | "requires": {
1072 | "@octokit/endpoint": "^9.0.6",
1073 | "@octokit/request-error": "^5.1.1",
1074 | "@octokit/types": "^13.1.0",
1075 | "universal-user-agent": "^6.0.0"
1076 | },
1077 | "dependencies": {
1078 | "@octokit/openapi-types": {
1079 | "version": "24.2.0",
1080 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1081 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="
1082 | },
1083 | "@octokit/types": {
1084 | "version": "13.10.0",
1085 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1086 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1087 | "requires": {
1088 | "@octokit/openapi-types": "^24.2.0"
1089 | }
1090 | }
1091 | }
1092 | },
1093 | "@octokit/request-error": {
1094 | "version": "5.1.1",
1095 | "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
1096 | "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
1097 | "requires": {
1098 | "@octokit/types": "^13.1.0",
1099 | "deprecation": "^2.0.0",
1100 | "once": "^1.4.0"
1101 | },
1102 | "dependencies": {
1103 | "@octokit/openapi-types": {
1104 | "version": "24.2.0",
1105 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1106 | "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="
1107 | },
1108 | "@octokit/types": {
1109 | "version": "13.10.0",
1110 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1111 | "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1112 | "requires": {
1113 | "@octokit/openapi-types": "^24.2.0"
1114 | }
1115 | }
1116 | }
1117 | },
1118 | "@octokit/types": {
1119 | "version": "12.6.0",
1120 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
1121 | "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
1122 | "requires": {
1123 | "@octokit/openapi-types": "^20.0.0"
1124 | }
1125 | },
1126 | "@octokit/webhooks": {
1127 | "version": "12.0.10",
1128 | "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.0.10.tgz",
1129 | "integrity": "sha512-Q8d26l7gZ3L1SSr25NFbbP0B431sovU5r0tIqcvy8Z4PrD1LBv0cJEjvDLOieouzPSTzSzufzRIeXD7S+zAESA==",
1130 | "requires": {
1131 | "@octokit/request-error": "^5.0.0",
1132 | "@octokit/webhooks-methods": "^4.0.0",
1133 | "@octokit/webhooks-types": "7.1.0",
1134 | "aggregate-error": "^3.1.0"
1135 | }
1136 | },
1137 | "@octokit/webhooks-methods": {
1138 | "version": "4.0.0",
1139 | "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz",
1140 | "integrity": "sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw=="
1141 | },
1142 | "@octokit/webhooks-types": {
1143 | "version": "7.1.0",
1144 | "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz",
1145 | "integrity": "sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w=="
1146 | },
1147 | "@tsconfig/node10": {
1148 | "version": "1.0.9",
1149 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
1150 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
1151 | },
1152 | "@tsconfig/node12": {
1153 | "version": "1.0.11",
1154 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
1155 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
1156 | },
1157 | "@tsconfig/node14": {
1158 | "version": "1.0.3",
1159 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
1160 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
1161 | },
1162 | "@tsconfig/node16": {
1163 | "version": "1.0.3",
1164 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
1165 | "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
1166 | },
1167 | "@types/aws-lambda": {
1168 | "version": "8.10.130",
1169 | "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.130.tgz",
1170 | "integrity": "sha512-HxTfLeGvD1wTJqIGwcBCpNmHKenja+We1e0cuzeIDFfbEj3ixnlTInyPR/81zAe0Ss/Ip12rFK6XNeMLVucOSg=="
1171 | },
1172 | "@types/btoa-lite": {
1173 | "version": "1.0.2",
1174 | "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
1175 | "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="
1176 | },
1177 | "@types/jsonwebtoken": {
1178 | "version": "9.0.5",
1179 | "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz",
1180 | "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==",
1181 | "requires": {
1182 | "@types/node": "*"
1183 | }
1184 | },
1185 | "@types/node": {
1186 | "version": "18.8.0",
1187 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.0.tgz",
1188 | "integrity": "sha512-u+h43R6U8xXDt2vzUaVP3VwjjLyOJk6uEciZS8OSyziUQGOwmk+l+4drxcsDboHXwyTaqS1INebghmWMRxq3LA=="
1189 | },
1190 | "acorn": {
1191 | "version": "8.8.0",
1192 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
1193 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w=="
1194 | },
1195 | "acorn-walk": {
1196 | "version": "8.2.0",
1197 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
1198 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
1199 | },
1200 | "aggregate-error": {
1201 | "version": "3.1.0",
1202 | "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
1203 | "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
1204 | "requires": {
1205 | "clean-stack": "^2.0.0",
1206 | "indent-string": "^4.0.0"
1207 | }
1208 | },
1209 | "arg": {
1210 | "version": "4.1.3",
1211 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
1212 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
1213 | },
1214 | "before-after-hook": {
1215 | "version": "2.2.3",
1216 | "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
1217 | "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
1218 | },
1219 | "bottleneck": {
1220 | "version": "2.19.5",
1221 | "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
1222 | "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
1223 | },
1224 | "btoa-lite": {
1225 | "version": "1.0.0",
1226 | "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
1227 | "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
1228 | },
1229 | "buffer-equal-constant-time": {
1230 | "version": "1.0.1",
1231 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
1232 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
1233 | },
1234 | "clean-stack": {
1235 | "version": "2.2.0",
1236 | "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
1237 | "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
1238 | },
1239 | "create-require": {
1240 | "version": "1.1.1",
1241 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
1242 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
1243 | },
1244 | "deprecation": {
1245 | "version": "2.3.1",
1246 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
1247 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
1248 | },
1249 | "diff": {
1250 | "version": "4.0.2",
1251 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
1252 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
1253 | },
1254 | "dotenv": {
1255 | "version": "16.0.3",
1256 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
1257 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
1258 | },
1259 | "ecdsa-sig-formatter": {
1260 | "version": "1.0.11",
1261 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
1262 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
1263 | "requires": {
1264 | "safe-buffer": "^5.0.1"
1265 | }
1266 | },
1267 | "indent-string": {
1268 | "version": "4.0.0",
1269 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
1270 | "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
1271 | },
1272 | "jsonwebtoken": {
1273 | "version": "9.0.2",
1274 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
1275 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
1276 | "requires": {
1277 | "jws": "^3.2.2",
1278 | "lodash.includes": "^4.3.0",
1279 | "lodash.isboolean": "^3.0.3",
1280 | "lodash.isinteger": "^4.0.4",
1281 | "lodash.isnumber": "^3.0.3",
1282 | "lodash.isplainobject": "^4.0.6",
1283 | "lodash.isstring": "^4.0.1",
1284 | "lodash.once": "^4.0.0",
1285 | "ms": "^2.1.1",
1286 | "semver": "^7.5.4"
1287 | }
1288 | },
1289 | "jwa": {
1290 | "version": "1.4.1",
1291 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
1292 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
1293 | "requires": {
1294 | "buffer-equal-constant-time": "1.0.1",
1295 | "ecdsa-sig-formatter": "1.0.11",
1296 | "safe-buffer": "^5.0.1"
1297 | }
1298 | },
1299 | "jws": {
1300 | "version": "3.2.2",
1301 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
1302 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
1303 | "requires": {
1304 | "jwa": "^1.4.1",
1305 | "safe-buffer": "^5.0.1"
1306 | }
1307 | },
1308 | "lodash.includes": {
1309 | "version": "4.3.0",
1310 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
1311 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
1312 | },
1313 | "lodash.isboolean": {
1314 | "version": "3.0.3",
1315 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
1316 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
1317 | },
1318 | "lodash.isinteger": {
1319 | "version": "4.0.4",
1320 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
1321 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
1322 | },
1323 | "lodash.isnumber": {
1324 | "version": "3.0.3",
1325 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
1326 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
1327 | },
1328 | "lodash.isplainobject": {
1329 | "version": "4.0.6",
1330 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
1331 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
1332 | },
1333 | "lodash.isstring": {
1334 | "version": "4.0.1",
1335 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
1336 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
1337 | },
1338 | "lodash.once": {
1339 | "version": "4.1.1",
1340 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
1341 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
1342 | },
1343 | "lru-cache": {
1344 | "version": "10.1.0",
1345 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz",
1346 | "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag=="
1347 | },
1348 | "make-error": {
1349 | "version": "1.3.6",
1350 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
1351 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
1352 | },
1353 | "ms": {
1354 | "version": "2.1.3",
1355 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1356 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1357 | },
1358 | "octokit": {
1359 | "version": "3.1.2",
1360 | "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz",
1361 | "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==",
1362 | "requires": {
1363 | "@octokit/app": "^14.0.2",
1364 | "@octokit/core": "^5.0.0",
1365 | "@octokit/oauth-app": "^6.0.0",
1366 | "@octokit/plugin-paginate-graphql": "^4.0.0",
1367 | "@octokit/plugin-paginate-rest": "^9.0.0",
1368 | "@octokit/plugin-rest-endpoint-methods": "^10.0.0",
1369 | "@octokit/plugin-retry": "^6.0.0",
1370 | "@octokit/plugin-throttling": "^8.0.0",
1371 | "@octokit/request-error": "^5.0.0",
1372 | "@octokit/types": "^12.0.0"
1373 | }
1374 | },
1375 | "once": {
1376 | "version": "1.4.0",
1377 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1378 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1379 | "requires": {
1380 | "wrappy": "1"
1381 | }
1382 | },
1383 | "safe-buffer": {
1384 | "version": "5.2.1",
1385 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1386 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
1387 | },
1388 | "semver": {
1389 | "version": "7.5.4",
1390 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
1391 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
1392 | "requires": {
1393 | "lru-cache": "^6.0.0"
1394 | },
1395 | "dependencies": {
1396 | "lru-cache": {
1397 | "version": "6.0.0",
1398 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
1399 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
1400 | "requires": {
1401 | "yallist": "^4.0.0"
1402 | }
1403 | }
1404 | }
1405 | },
1406 | "ts-node": {
1407 | "version": "10.9.1",
1408 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
1409 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
1410 | "requires": {
1411 | "@cspotcode/source-map-support": "^0.8.0",
1412 | "@tsconfig/node10": "^1.0.7",
1413 | "@tsconfig/node12": "^1.0.7",
1414 | "@tsconfig/node14": "^1.0.0",
1415 | "@tsconfig/node16": "^1.0.2",
1416 | "acorn": "^8.4.1",
1417 | "acorn-walk": "^8.1.1",
1418 | "arg": "^4.1.0",
1419 | "create-require": "^1.1.0",
1420 | "diff": "^4.0.1",
1421 | "make-error": "^1.1.1",
1422 | "v8-compile-cache-lib": "^3.0.1",
1423 | "yn": "3.1.1"
1424 | }
1425 | },
1426 | "typescript": {
1427 | "version": "4.8.4",
1428 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
1429 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
1430 | "peer": true
1431 | },
1432 | "universal-github-app-jwt": {
1433 | "version": "1.1.1",
1434 | "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz",
1435 | "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==",
1436 | "requires": {
1437 | "@types/jsonwebtoken": "^9.0.0",
1438 | "jsonwebtoken": "^9.0.0"
1439 | }
1440 | },
1441 | "universal-user-agent": {
1442 | "version": "6.0.1",
1443 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
1444 | "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
1445 | },
1446 | "v8-compile-cache-lib": {
1447 | "version": "3.0.1",
1448 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
1449 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
1450 | },
1451 | "wrappy": {
1452 | "version": "1.0.2",
1453 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1454 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
1455 | },
1456 | "yallist": {
1457 | "version": "4.0.0",
1458 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1459 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1460 | },
1461 | "yn": {
1462 | "version": "3.1.1",
1463 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
1464 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
1465 | }
1466 | }
1467 | }
1468 |
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "approve-dependabot-deploys",
3 | "version": "1.0.0",
4 | "description": "",
5 | "type": "module",
6 | "scripts": {
7 | "start": "ts-node-esm approve-dependabot-deploys.ts"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "dotenv": "^16.0.3",
13 | "octokit": "^3.1.2",
14 | "ts-node": "^10.9.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/scripts/approve-dependabot-deploys/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "module": "ES2022",
5 | "target": "ES2022",
6 | "noImplicitAny": false,
7 | "esModuleInterop": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/aws-oidc-role-cloudformation-template.yaml:
--------------------------------------------------------------------------------
1 | # Based on https://github.com/aws-actions/configure-aws-credentials#assuming-a-role
2 | Parameters:
3 | GitHubOrg:
4 | Type: String
5 | RepositoryName:
6 | Type: String
7 | OIDCProviderArn:
8 | Description: Arn for the GitHub OIDC Provider.
9 | Default: ""
10 | Type: String
11 |
12 | Conditions:
13 | CreateOIDCProvider: !Equals
14 | - !Ref OIDCProviderArn
15 | - ""
16 |
17 | Resources:
18 | Role:
19 | Type: AWS::IAM::Role
20 | Properties:
21 | RoleName: serverless-aws-static-file-handler-at-github
22 | AssumeRolePolicyDocument:
23 | Statement:
24 | - Effect: Allow
25 | Action: sts:AssumeRoleWithWebIdentity
26 | Principal:
27 | Federated: !If
28 | - CreateOIDCProvider
29 | - !Ref GithubOidc
30 | - !Ref OIDCProviderArn
31 | Condition:
32 | StringLike:
33 | token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*
34 | ManagedPolicyArns:
35 | - arn:aws:iam::aws:policy/AdministratorAccess
36 |
37 | GithubOidc:
38 | Type: AWS::IAM::OIDCProvider
39 | Condition: CreateOIDCProvider
40 | Properties:
41 | Url: https://token.actions.githubusercontent.com
42 | ClientIdList:
43 | - sts.amazonaws.com
44 | ThumbprintList:
45 | - 6938fd4d98bab03faadb97b34396831e3780aea1
46 |
47 | Outputs:
48 | Role:
49 | Value: !GetAtt Role.Arn
50 |
--------------------------------------------------------------------------------
/scripts/aws-oidc-role-provision.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | THISDIR=$(cd $(dirname "$0"); pwd) #this script's directory
3 | THISSCRIPT=$(basename $0)
4 |
5 | GitHubOrg=activescott
6 | RepositoryName=serverless-aws-static-file-handler
7 | # Get OIDCProviderArn via `aws iam list-open-id-connect-providers` or at https://us-east-1.console.aws.amazon.com/iamv2/home#/identity_providers
8 | # Create one according to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html#manage-oidc-provider-console
9 | # TODO: create it via cloudformation or CLI
10 | OIDCProviderArn=arn:aws:iam::166901232151:oidc-provider/token.actions.githubusercontent.com
11 |
12 | # us-east-1 since IAM is there anyway?
13 | AWS_REGION=us-east-1
14 |
15 | echo "using org '$GitHubOrg' and repo '$RepositoryName'."
16 |
17 | aws cloudformation deploy \
18 | --region $AWS_REGION \
19 | --template-file aws-oidc-role-cloudformation-template.yaml \
20 | --stack-name aws-oidc-role-cloudformation \
21 | --capabilities CAPABILITY_NAMED_IAM \
22 | --parameter-overrides GitHubOrg=$GitHubOrg \
23 | RepositoryName=$RepositoryName \
24 | OIDCProviderArn=$OIDCProviderArn
25 | #--no-execute-changeset
26 |
--------------------------------------------------------------------------------
/src/StaticFileHandler.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const assert = require("assert")
3 | const fs = require("fs")
4 | const mimetypes = require("mime-types")
5 | const Mustache = require("mustache")
6 | const path = require("path")
7 | const util = require("util")
8 | const readFileAsync = util.promisify(fs.readFile)
9 | const accessAsync = util.promisify(fs.access)
10 |
11 | // originally from lodash, but never called with a defaultValue
12 | // https://gist.github.com/jeneg/9767afdcca45601ea44930ea03e0febf
13 | function __get(value, path, defaultValue) {
14 | return String(path)
15 | .split(".")
16 | .reduce((acc, v) => {
17 | if (v.startsWith("[")) {
18 | const [, arrPart] = v.split("[")
19 | v = arrPart.split("]")[0]
20 | }
21 |
22 | if (v.endsWith("]") && !v.startsWith("[")) {
23 | const [objPart, arrPart, ...rest] = v.split("[")
24 | const [firstIndex] = arrPart.split("]")
25 | const otherParts = rest
26 | .join("")
27 | .replaceAll("[", "")
28 | .replaceAll("]", ".")
29 | .split(".")
30 | .filter((str) => str !== "")
31 |
32 | return [...acc, objPart, firstIndex, ...otherParts]
33 | }
34 |
35 | return [...acc, v]
36 | }, [])
37 | .reduce((acc, v) => {
38 | try {
39 | acc = acc[v] !== undefined ? acc[v] : defaultValue
40 | } catch (e) {
41 | return defaultValue
42 | }
43 |
44 | return acc
45 | }, value)
46 | }
47 |
48 | class StaticFileHandler {
49 | /**
50 | * Initializes a new instance of @see StaticFileHandler
51 | * @param {*string} clientFilesPath The fully qualified path to the client files that this module should serve.
52 | * @param {*string} customErrorPagePath Optional path to a custom error page. Must be relative to @see clientFilesPath .
53 | */
54 | constructor(clientFilesPath, customErrorPagePath = null) {
55 | if (clientFilesPath == null || clientFilesPath.length === 0) {
56 | throw new Error("clientFilesPath must be specified")
57 | }
58 | this.clientFilesPath = clientFilesPath
59 | this.customErrorPagePath = customErrorPagePath
60 | }
61 |
62 | static getMimeType(filePath) {
63 | return mimetypes.lookup(filePath) || "application/octet-stream"
64 | }
65 |
66 | static isBinaryType(mimeType) {
67 | const mimeCharset = mimetypes.charset(mimeType)
68 | /* Using https://w3techs.com/technologies/overview/character_encoding/all
69 | * to be more comprehensive go through those at https://www.iana.org/assignments/character-sets/character-sets.xhtml
70 | */
71 | const textualCharSets = [
72 | "UTF-8",
73 | "ISO-8859-1",
74 | "Windows-1251",
75 | "Windows-1252",
76 | "Shift_JIS",
77 | "GB2312",
78 | "EUC-KR",
79 | "ISO-8859-2",
80 | "GBK",
81 | "Windows-1250",
82 | "EUC-JP",
83 | "Big5",
84 | "ISO-8859-15",
85 | "Windows-1256",
86 | "ISO-8859-9",
87 | ]
88 | const found = textualCharSets.find(
89 | (cs) => 0 === cs.localeCompare(mimeCharset, "en", { sensitivity: "base" })
90 | )
91 | return found === undefined || found === null
92 | }
93 |
94 | async get(event, context) {
95 | if (!event) {
96 | throw new Error("event object not specified.")
97 | }
98 |
99 | if (event.rawPath) {
100 | // convert the V2 API to look like v1 like rest of code expects
101 |
102 | // this matches validateLambdaProxyIntegration required props
103 | event.resource = event.requestContext.http.path
104 | event.path = event.rawPath
105 | event.httpMethod = event.requestContext.http.method
106 | // OK as is event.headers
107 | event.multiValueHeaders = event.headers
108 | event.queryStringParameters = event.queryStringParamaters
109 | event.multiValueQueryStringParameters = event.queryStringParameters // Not sure what old code does ?
110 | // OK as is event.pathParameters
111 | event.stageVariables = event.requestContext.stage // Not sure we ever pass these ?
112 | // OK as is event.requestContext
113 | event.body = "" // It is a GET, there never is one ?
114 | // OK as is event.isBase64Encoded
115 | }
116 |
117 | if (!event.path) {
118 | throw new Error("Empty path.")
119 | }
120 | await StaticFileHandler.validateLambdaProxyIntegration(event)
121 | let requestPath
122 | if (event.pathParameters) {
123 | requestPath = ""
124 | /*
125 | * event.path is an object when `integration: lambda` and there is a greedy path parameter
126 | * If there are zero properties, it is just "lambda integration" and no path parameters
127 | * If there are properties, it indicates there are path parameters.
128 | * For example: The path parameter could be mapped like so in serverless.yml:
129 | * - http:
130 | path: fontsdir/{fonts+}
131 | * The {fonts+} in the path indicates the base path and tells APIG to pass along the whole path.
132 | */
133 | // now enumerate the properties of it:
134 | let propNames = Object.getOwnPropertyNames(event.pathParameters)
135 | if (propNames.length === 0) {
136 | const msg =
137 | "The event.path is an object but there are no properties. Check serverless.yml."
138 | throw new Error(msg)
139 | }
140 | if (propNames.length !== 1) {
141 | const msg = `Expected exactly one property name, but found: ${util.inspect(
142 | propNames
143 | )}. Check that you configured the pathParameter in serverless.yml with a plus sign like \`path/{pathparam+}\`.`
144 | throw new Error(msg)
145 | }
146 | requestPath = "/" + event.pathParameters[propNames[0]]
147 | } else {
148 | assert(typeof event.path === "string", "expected path to be string")
149 | requestPath = event.path
150 | }
151 | let filePath = path.join(this.clientFilesPath, requestPath)
152 | return this.readFileAsResponse(filePath, context).catch((err) => {
153 | throw new Error(
154 | `Unable to read client file '${requestPath}'. Error: ${err}`
155 | )
156 | })
157 | }
158 |
159 | /**
160 | * Loads the specified file's content and returns a response that can be called back to lambda for sending the file as the http response.
161 | */
162 | async readFileAsResponse(filePath, context, statusCode = 200) {
163 | let stream
164 | try {
165 | stream = await readFileAsync(filePath)
166 | } catch (err) {
167 | if (err.code === "ENOENT") {
168 | // NOTE: avoid leaking full local path
169 | const fileName = path.basename(filePath)
170 | return this.responseAsError(`File ${fileName} does not exist`, 404)
171 | }
172 | }
173 | let mimeType = StaticFileHandler.getMimeType(filePath)
174 | return StaticFileHandler.readStreamAsResponse(
175 | stream,
176 | context,
177 | statusCode,
178 | mimeType
179 | )
180 | }
181 |
182 | static readStreamAsResponse(stream, context, statusCode, mimeType) {
183 | let body
184 | let isBase64Encoded = false
185 | if (StaticFileHandler.isBinaryType(mimeType)) {
186 | isBase64Encoded = true
187 | body = Buffer.from(stream).toString("base64")
188 | } else {
189 | body = stream.toString("utf8")
190 | }
191 | return StaticFileHandler.readStringAsResponse(
192 | body,
193 | context,
194 | statusCode,
195 | mimeType,
196 | isBase64Encoded
197 | )
198 | }
199 |
200 | static readStringAsResponse(
201 | stringData,
202 | context,
203 | statusCode,
204 | mimeType,
205 | isBase64Encoded
206 | ) {
207 | assert(mimeType, "expected mimeType to always be provided")
208 | if (
209 | context &&
210 | "staticFileHandler" in context &&
211 | "viewData" in context.staticFileHandler
212 | ) {
213 | const viewData = context.staticFileHandler.viewData
214 | stringData = Mustache.render(stringData, viewData)
215 | }
216 | const response = {
217 | statusCode: statusCode,
218 | headers: {
219 | "Content-Type": mimeType,
220 | },
221 | isBase64Encoded,
222 | body: stringData,
223 | }
224 | return response
225 | }
226 |
227 | /**
228 | * Returns a Promise with a response that is an HTML page with the specified error text on it.
229 | * @param {*string} errorText The error to add to the page.
230 | */
231 | async responseAsError(errorText, statusCode) {
232 | const context = {
233 | staticFileHandler: {
234 | viewData: {
235 | errorText: errorText,
236 | },
237 | },
238 | }
239 | if (this.customErrorPagePath) {
240 | let filePath = path.join(this.clientFilesPath, this.customErrorPagePath)
241 | try {
242 | await accessAsync(filePath, fs.constants.R_OK)
243 | return this.readFileAsResponse(filePath, context, statusCode)
244 | } catch (err) {
245 | console.warn(
246 | "serverless-aws-static-file-handler: Error using customErrorPagePath",
247 | this.customErrorPagePath,
248 | ". Using fallback error HTML."
249 | )
250 | }
251 | }
252 |
253 | const DEFAULT_ERROR_HTML = `
254 |
255 |
256 |
257 | Error
258 |
259 |
260 |
261 | {{errorText}}
262 |
263 |
264 | `
265 | return StaticFileHandler.readStringAsResponse(
266 | DEFAULT_ERROR_HTML,
267 | context,
268 | statusCode,
269 | "text/html",
270 | false
271 | )
272 | }
273 |
274 | /**
275 | * Rejects if the specified event is not Lambda Proxy integration
276 | */
277 | static async validateLambdaProxyIntegration(event) {
278 | /*
279 | There are two different event schemas in API Gateway + Lambda Proxy APIs. One is known as "REST API" or the old V1 API and the newer one is the V2 or "HTTP API".
280 | Each are described at https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#services-apigateway-apitypes
281 | You can see examples of each at https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
282 | TLDR:
283 | - V2 has a { version: "2.0" } field and { requestContext.http.method: "..." } field.
284 | - V1 has a { version: "1.0" } field and { requestContext.httpMethod: "..." } field.
285 | To set each up in serverless.com:
286 | - V1: https://www.serverless.com/framework/docs/providers/aws/events/apigateway
287 | - V2: https://www.serverless.com/framework/docs/providers/aws/events/http-api
288 | */
289 | function isV2ProxyAPI(evt) {
290 | return (
291 | evt.version === "2.0" &&
292 | typeof __get(evt, "requestContext.http.method") === "string"
293 | )
294 | }
295 | function isV1ProxyAPI(evt) {
296 | return (
297 | // docs say there is a .version but there isn't!
298 | // evt.version === "1.0" &&
299 | typeof __get(evt, "requestContext.httpMethod") === "string"
300 | )
301 | }
302 | // serverless-offline doesn't provide the `isBase64Encoded` prop, but does add the isOffline. Fixes issue #10: https://github.com/activescott/serverless-aws-static-file-handler/issues/10
303 | const isServerlessOfflineEnvironment = "isOffline" in event
304 | if (!isV1ProxyAPI(event) && !isV2ProxyAPI(event)) {
305 | const logProps = [
306 | "version",
307 | "requestContext.httpMethod",
308 | "requestContext.http.method",
309 | ]
310 | const addendum = logProps
311 | .map((propName) => `event.${propName} was '${__get(event, propName)}'`)
312 | .join(" ")
313 | throw new Error(
314 | "API Gateway method does not appear to be setup for Lambda Proxy Integration. Please confirm that `integration` property of the http event is not specified or set to `integration: proxy`." +
315 | addendum
316 | )
317 | }
318 | }
319 | }
320 |
321 | module.exports = StaticFileHandler
322 |
--------------------------------------------------------------------------------
/src/plugins/BinaryMediaTypes.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const util = require("util")
3 | const _ = require("lodash")
4 |
5 | class BinaryMediaTypes {
6 | constructor(serverless, options) {
7 | if (!serverless) {
8 | throw new Error("Expected serverless to be provided as argument")
9 | }
10 | this.serverless = serverless
11 | this.options = options
12 | this.provider = this.serverless.getProvider("aws")
13 | this.hooks = {
14 | "package:compileEvents": this.packageCompileEvents.bind(this),
15 | }
16 | }
17 |
18 | log(...args) {
19 | args.unshift("aws-static-file-handler (BinaryMediaTypes):")
20 | const msg = util.format(...args)
21 | this.serverless.cli.log(msg)
22 | }
23 |
24 | getRestApi() {
25 | const resources =
26 | this.serverless.service.provider.compiledCloudFormationTemplate.Resources
27 | return _.find(resources, (r) => r.Type === "AWS::ApiGateway::RestApi")
28 | }
29 |
30 | readConfig() {
31 | const service = this.serverless.service
32 | if (
33 | !service.custom ||
34 | !service.custom.apiGateway ||
35 | !service.custom.apiGateway.binaryMediaTypes ||
36 | _.isEmpty(service.custom.apiGateway.binaryMediaTypes)
37 | ) {
38 | throw new Error(BinaryMediaTypes.Strings.CONFIG_ERROR)
39 | }
40 | return service.custom.apiGateway.binaryMediaTypes
41 | }
42 |
43 | addBinaryMediaTypes(restApi) {
44 | if (!restApi) {
45 | this.log(
46 | "Amazon API Gateway RestApi resource not found. No BinaryMediaTypes will be added."
47 | )
48 | return
49 | }
50 | if (!restApi.Properties) {
51 | throw new Error("RestApi Properties property does not exist!")
52 | }
53 | // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-binarymediatypes
54 | const newTypes = this.readConfig()
55 | this.log("Adding the following BinaryMediaTypes to RestApi:", newTypes)
56 | const oldTypes = restApi.Properties["BinaryMediaTypes"] || []
57 | const combined = _.concat(oldTypes, newTypes)
58 | restApi.Properties["BinaryMediaTypes"] = combined
59 | this.log("RestApi BinaryMediaTypes are now:", combined)
60 | }
61 |
62 | packageCompileEvents() {
63 | this.log("Preparing to add binary media types (package:compileEvents)...")
64 | const restApi = this.getRestApi()
65 | this.addBinaryMediaTypes(restApi)
66 | }
67 | }
68 |
69 | BinaryMediaTypes.Strings = {
70 | CONFIG_ERROR:
71 | "No BinaryMediaTypes configured. See https://github.com/activescott/serverless-aws-static-file-handler#usage for information on how to configure",
72 | }
73 |
74 | module.exports = BinaryMediaTypes
75 |
--------------------------------------------------------------------------------
/src/test/BinaryMediaTypes.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | const chai = require("chai")
4 | const chaiAsPromised = require("chai-as-promised")
5 | chai.use(chaiAsPromised)
6 | const expect = chai.expect
7 | const sinon = require("sinon")
8 |
9 | const BinaryMediaTypes = require("../plugins/BinaryMediaTypes")
10 |
11 | describe("BinaryMediaTypes", function () {
12 | afterEach(() => {
13 | // Restore the default sandbox here
14 | sinon.restore()
15 | })
16 |
17 | describe("source path", function () {
18 | it("should load from '/plugins/BinaryMediaTypes'", () => {
19 | // this is the proper path
20 | require("../../plugins/BinaryMediaTypes")
21 | })
22 |
23 | it("should load from 'src/plugins/BinaryMediaTypes'", () => {
24 | // this was inadvertently introduced in v2.0.3 per https://github.com/activescott/serverless-aws-static-file-handler/issues/32
25 | require("../plugins/BinaryMediaTypes")
26 | })
27 | })
28 |
29 | describe("constructor", function () {
30 | it("should not allow empty serverless arg", function () {
31 | expect(() => new BinaryMediaTypes()).to.throw(
32 | /Expected serverless to be provided as argument/
33 | )
34 | })
35 | })
36 |
37 | describe("package:compileEvents", function () {
38 | let logSpy
39 | beforeEach(() => {
40 | //logSpy = sinon.spy(console.log)
41 | logSpy = sinon.spy()
42 | })
43 |
44 | function createPlugin(restApi) {
45 | const provider = {
46 | compiledCloudFormationTemplate: {
47 | Resources: [],
48 | },
49 | }
50 | provider.compiledCloudFormationTemplate.Resources.push(restApi)
51 | const serverless = {
52 | getProvider: (str) => {
53 | str === "aws" ? provider : null
54 | },
55 | cli: {
56 | log: logSpy,
57 | },
58 | service: {
59 | provider,
60 | },
61 | }
62 | return new BinaryMediaTypes(serverless)
63 | }
64 |
65 | function createRestApi() {
66 | return {
67 | Type: "AWS::ApiGateway::RestApi",
68 | Properties: [],
69 | }
70 | }
71 |
72 | const getHook = (plugin) => plugin.hooks["package:compileEvents"]
73 |
74 | it("should gracefully fail with logging when no RestApi", function () {
75 | const api = createRestApi()
76 | const p = createPlugin(api)
77 | // remove Rest API from service (maybe user isn't using http events/APIG?):
78 | p.serverless.service.provider.compiledCloudFormationTemplate.Resources =
79 | []
80 | const hook = getHook(p)
81 | expect(hook).to.not.throw()
82 | expect(logSpy.callCount).to.equal(2)
83 | expect(
84 | logSpy.calledWithExactly(
85 | sinon.match(
86 | /Amazon API Gateway RestApi resource not found. No BinaryMediaTypes will be added.$/
87 | )
88 | )
89 | ).to.be.true
90 | })
91 |
92 | it("should ungracefully fail when RestApi has no Properties property", function () {
93 | const api = createRestApi()
94 | delete api.Properties
95 | const p = createPlugin(api)
96 | const hook = getHook(p)
97 | expect(hook).to.throw(/RestApi Properties property does not exist/)
98 | })
99 |
100 | describe("service configuration", function () {
101 | /**
102 | * expecting serverless.yml yaml:
103 | * custom:
104 | * apiGateway:
105 | * binaryMediaTypes:
106 | * - image/png
107 | * - application/octet-stream
108 | * ...
109 | * Plugin is expecting to read this like: expecting: serverless.service.custom.apiGateway.binaryMediatypes: []
110 | */
111 | const CONFIG_ERROR_REGEX =
112 | /github\.com\/activescott\/serverless\-aws\-static\-file\-handler\#usage for information on how to configure$/
113 | it("should throw helpful messages when no custom config", function () {
114 | const api = createRestApi()
115 | const p = createPlugin(api)
116 | p.serverless.service.custom = null
117 | const hook = getHook(p)
118 | expect(hook).to.throw(CONFIG_ERROR_REGEX)
119 | })
120 |
121 | it("should throw helpful messages when no apiGateway config", function () {
122 | const api = createRestApi()
123 | const p = createPlugin(api)
124 | p.serverless.service.custom = {}
125 | const hook = getHook(p)
126 | expect(hook).to.throw(CONFIG_ERROR_REGEX)
127 | })
128 |
129 | it("should throw helpful messages when no binaryMediatypes config", function () {
130 | const api = createRestApi()
131 | const p = createPlugin(api)
132 | p.serverless.service.custom = {
133 | apiGateway: {},
134 | }
135 | const hook = getHook(p)
136 | expect(hook).to.throw(CONFIG_ERROR_REGEX)
137 | })
138 |
139 | it("should throw helpful messages when binaryMediatypes config null", function () {
140 | const api = createRestApi()
141 | const p = createPlugin(api)
142 | p.serverless.service.custom = {
143 | apiGateway: {
144 | binaryMediaTypes: null,
145 | },
146 | }
147 | const hook = getHook(p)
148 | expect(hook).to.throw(CONFIG_ERROR_REGEX)
149 | })
150 |
151 | it("should throw helpful messages when binaryMediatypes config empty", function () {
152 | const api = createRestApi()
153 | const p = createPlugin(api)
154 | p.serverless.service.custom = {
155 | apiGateway: {
156 | binaryMediaTypes: [],
157 | },
158 | }
159 | const hook = getHook(p)
160 | expect(hook).to.throw(CONFIG_ERROR_REGEX)
161 | })
162 |
163 | it("should read configured media types", function () {
164 | const api = createRestApi()
165 | const p = createPlugin(api)
166 | p.serverless.service.custom = {
167 | apiGateway: {
168 | binaryMediaTypes: ["image/png", "image/jpeg"],
169 | },
170 | }
171 | const hook = getHook(p)
172 | hook()
173 | expect(api.Properties.BinaryMediaTypes).to.deep.equal([
174 | "image/png",
175 | "image/jpeg",
176 | ])
177 | })
178 | })
179 |
180 | it("should not overwrite any existing media types in stack", function () {
181 | const api = createRestApi()
182 | api.Properties.BinaryMediaTypes = ["image/jpeg"]
183 | const p = createPlugin(api)
184 | p.serverless.service.custom = {
185 | apiGateway: {
186 | binaryMediaTypes: ["image/png"],
187 | },
188 | }
189 | const hook = getHook(p)
190 | hook()
191 | expect(api.Properties.BinaryMediaTypes).to.deep.equal([
192 | "image/jpeg",
193 | "image/png",
194 | ])
195 | })
196 |
197 | it("should not add duplicates to existing media types in stack", function () {
198 | const api = createRestApi()
199 | api.Properties = ["image/jpeg"]
200 | const p = createPlugin(api)
201 | p.serverless.service.custom = {
202 | apiGateway: {
203 | binaryMediaTypes: ["image/jpeg"],
204 | },
205 | }
206 | const hook = getHook(p)
207 | hook()
208 | expect(api.Properties.BinaryMediaTypes).to.deep.equal(["image/jpeg"])
209 | })
210 | })
211 | })
212 |
--------------------------------------------------------------------------------
/src/test/StaticFileHandler.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | /* eslint-disable padded-blocks */
3 | "use strict"
4 |
5 | const chai = require("chai")
6 | const chaiAsPromised = require("chai-as-promised")
7 | chai.use(chaiAsPromised)
8 | const expect = chai.expect
9 |
10 | const path = require("path")
11 | const StaticFileHandler = require("../StaticFileHandler.js")
12 |
13 | const STATIC_FILES_PATH = path.join(__dirname, "./data/testfiles/")
14 |
15 | function mockEvent(event) {
16 | return {
17 | resource: null,
18 | httpMethod: "GET",
19 | requestContext: {
20 | httpMethod: "GET",
21 | },
22 | headers: {},
23 | multiValueHeaders: {},
24 | queryStringParameters: null,
25 | multiValueQueryStringParameters: null,
26 | pathParameters: null,
27 | stageVariables: null,
28 | body: null,
29 | isBase64Encoded: false,
30 | ...event,
31 | }
32 | }
33 |
34 | // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
35 | function mockEventV2(path, event) {
36 | return {
37 | version: "2.0",
38 | routeKey: "$default",
39 | rawPath: path,
40 | requestContext: {
41 | http: {
42 | method: "GET",
43 | path: path,
44 | },
45 | },
46 | ...event,
47 | }
48 | }
49 |
50 | describe("StaticFileHandler", function () {
51 | describe("constructor", function () {
52 | it("should not allow empty arg", function () {
53 | expect(() => new StaticFileHandler()).to.throw(
54 | /^clientFilesPath must be specified$/
55 | )
56 | })
57 |
58 | it("should accept string arg", function () {
59 | expect(() => new StaticFileHandler("some/path")).to.not.throw(Error)
60 | })
61 | })
62 |
63 | describe("get", function () {
64 | it("should return index.html", function () {
65 | const event = mockEvent({ path: "index.html" })
66 | let h = new StaticFileHandler(STATIC_FILES_PATH)
67 | return h.get(event, null).then((response) => {
68 | expect(response).to.have.property("statusCode", 200)
69 | expect(response)
70 | .to.have.property("body")
71 | .to.match(/^/)
72 | return response
73 | })
74 | })
75 |
76 | it("should validate event exist", function () {
77 | const event = null
78 | let h = new StaticFileHandler(STATIC_FILES_PATH)
79 | return expect(h.get(event, null)).to.be.rejectedWith(
80 | /event object not specified.$/
81 | )
82 | })
83 |
84 | it("should validate event.path", function () {
85 | const event = mockEvent({ NOPATH: "index.html" })
86 | let h = new StaticFileHandler(STATIC_FILES_PATH)
87 | return expect(h.get(event, null)).to.be.rejectedWith(/Empty path.$/)
88 | })
89 |
90 | it.skip("(integration:lambda no longer supported) should work with non-lambdaproxy requests", function () {
91 | const event = {
92 | path: {
93 | fonts: "fonts/glyphicons-halflings-regular.woff2",
94 | },
95 | }
96 | let h = new StaticFileHandler(STATIC_FILES_PATH)
97 | return h.get(event, null).then((response) => {
98 | return expect(response.body.length).to.equal(24040)
99 | })
100 | })
101 |
102 | it("integration:lambda no longer supported; should fail if not using lambda-proxy", function () {
103 | const event = {
104 | path: {
105 | fonts: "fonts/glyphicons-halflings-regular.woff2",
106 | },
107 | }
108 | let h = new StaticFileHandler(STATIC_FILES_PATH)
109 | return expect(h.get(event, null)).to.be.rejectedWith(
110 | /^API Gateway method does not appear to be setup for Lambda Proxy Integration/
111 | )
112 | })
113 |
114 | it("should succeed in serverless-offline environment", function () {
115 | // see issue #10:
116 | const event = mockEvent({ path: "README.md" })
117 | delete event["isBase64Encoded"]
118 | event["isOffline"] = true
119 | let h = new StaticFileHandler(STATIC_FILES_PATH)
120 | return h.get(event, null).then((response) => {
121 | let expectedContent = "This directory is not empty. Is it?\n"
122 | return expect(response.body).to.equal(expectedContent)
123 | })
124 | })
125 |
126 | it("should return text as text", function () {
127 | const event = mockEvent({ path: "README.md" })
128 | let h = new StaticFileHandler(STATIC_FILES_PATH)
129 | return h.get(event, null).then((response) => {
130 | let expectedContent = "This directory is not empty. Is it?\n"
131 | return expect(response.body).to.equal(expectedContent)
132 | })
133 | })
134 |
135 | it("should insert viewdata", function () {
136 | const event = mockEvent({ path: "index.html" })
137 | let h = new StaticFileHandler(STATIC_FILES_PATH)
138 | const context = {
139 | staticFileHandler: {
140 | viewData: {
141 | csrftoken: "MY_FAKE_CSRF_TOKEN",
142 | },
143 | },
144 | }
145 | return h.get(event, context).then((response) => {
146 | return expect(response.body).to.match(
147 | /.* CUSTOM<\/title>/)
204 | })
205 | })
206 |
207 | /**
208 | * This is to support a greedy path parameter like:
209 | * ```
210 | * events:
211 | * - http:
212 | * path: /binary/{pathvar+}
213 | * ```
214 | */
215 | it("should support path parameters", function () {
216 | const event = mockEvent({
217 | path: "/binary/vendor/output.css.map",
218 | pathParameters: { pathvar: "vendor/output.css.map" },
219 | })
220 | let h = new StaticFileHandler(STATIC_FILES_PATH)
221 | const response = h.get(event, null)
222 | expect(response)
223 | .to.eventually.have.ownProperty("statusCode")
224 | .that.equals(200)
225 | return expect(response)
226 | .to.eventually.haveOwnProperty("body")
227 | .that.is.a("string")
228 | .and.has.length(107)
229 | })
230 |
231 | it("should return 404 with path parameters", function () {
232 | const event = mockEvent({
233 | path: "/binary/does-not-exist.file",
234 | pathParameters: { pathvar: "vendor/does-not-exist.file" },
235 | })
236 | let h = new StaticFileHandler(STATIC_FILES_PATH)
237 | const response = h.get(event, null)
238 | return expect(response)
239 | .to.eventually.haveOwnProperty("statusCode")
240 | .that.equals(404)
241 | })
242 |
243 | /**
244 | * This is a `integration: lambda` test WITH NO path parameters.
245 | * Defined in serverless.yml something like:
246 | *
247 | * myfunc:
248 | * handler: handler.myfunc
249 | * events:
250 | * - http:
251 | * path: /no_path_params_here
252 | * method: get
253 | * integration: lambda
254 | * contentHandling: CONVERT_TO_BINARY
255 | */
256 | it.skip("(integration:lambda no longer supported) should error on lambda integration without path parameters", function () {
257 | const event = {
258 | body: {},
259 | method: "GET",
260 | stage: "dev",
261 | headers: { Accept: "*/*" },
262 | query: {},
263 | path: {}, // <<< this is the critical input to this test. It is an object (indicating lambda integration), but no properties.
264 | }
265 | const context = {}
266 | const h = new StaticFileHandler(STATIC_FILES_PATH)
267 | return expect(h.get(event, context)).to.eventually.be.rejectedWith(
268 | /The event.path is an object but there are no properties. This likely means it is a lambda integration but there are no path parameters\/variables defined. Check your serverless.yml./
269 | )
270 | })
271 |
272 | describe("MIME Types", function () {
273 | it("js.map => application/json", async function () {
274 | const event = mockEvent({ path: "vendor/output.js.map" })
275 | let h = new StaticFileHandler(STATIC_FILES_PATH)
276 | const response = await h.get(event, null)
277 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
278 | expect(response)
279 | .to.have.property("headers")
280 | .that.has.property("Content-Type")
281 | .that.equals("application/json")
282 | expect(response).to.have.property("isBase64Encoded").that.equals(false)
283 | return response
284 | })
285 |
286 | it("css.map => application/json", async function () {
287 | const event = mockEvent({ path: "vendor/output.css.map" })
288 | let h = new StaticFileHandler(STATIC_FILES_PATH)
289 | const response = await h.get(event, null)
290 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
291 | expect(response)
292 | .to.have.property("headers")
293 | .that.has.property("Content-Type")
294 | .that.equals("application/json")
295 | expect(response).to.have.property("isBase64Encoded").that.equals(false)
296 | return response
297 | })
298 |
299 | it("unknown-mime-type => application/octet-stream", async function () {
300 | const event = mockEvent({ path: "unknown-mime-type.unknowntype" })
301 | let h = new StaticFileHandler(STATIC_FILES_PATH)
302 | const response = await h.get(event, null)
303 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
304 | expect(response)
305 | .to.have.property("headers")
306 | .that.has.property("Content-Type")
307 | .that.equals("application/octet-stream")
308 | expect(response).to.have.property("isBase64Encoded").that.equals(true)
309 | return response
310 | })
311 |
312 | describe("Binary types are also base64", async function () {
313 | it(".png => image/png", async function () {
314 | const event = mockEvent({ path: "png.png" })
315 | let h = new StaticFileHandler(STATIC_FILES_PATH)
316 | const response = await h.get(event, null)
317 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
318 | expect(response).to.have.property("headers")
319 | expect(response.headers)
320 | .to.have.property("Content-Type")
321 | .that.equals("image/png")
322 | expect(response).to.have.property("isBase64Encoded").that.equals(true)
323 | return response
324 | })
325 |
326 | it(".jpg => image/jpeg", async function () {
327 | const event = mockEvent({ path: "jpg.jpg" })
328 | let h = new StaticFileHandler(STATIC_FILES_PATH)
329 | const response = await h.get(event, null)
330 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
331 | expect(response).to.have.property("headers")
332 | expect(response.headers)
333 | .to.have.property("Content-Type")
334 | .that.equals("image/jpeg")
335 | expect(response).to.have.property("isBase64Encoded").that.equals(true)
336 | return response
337 | })
338 |
339 | it(".woff2 => font/woff2", async function () {
340 | const event = mockEvent({
341 | path: "fonts/glyphicons-halflings-regular.woff2",
342 | })
343 | let h = new StaticFileHandler(STATIC_FILES_PATH)
344 | const response = await h.get(event, null)
345 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
346 | expect(response).to.have.property("headers")
347 | expect(response.headers)
348 | .to.have.property("Content-Type")
349 | .that.equals("font/woff2")
350 | expect(response).to.have.property("isBase64Encoded").that.equals(true)
351 | return response
352 | })
353 |
354 | it(".bin", async function () {
355 | const event = mockEvent({ path: "blah.bin" })
356 | let h = new StaticFileHandler(STATIC_FILES_PATH)
357 | const response = await h.get(event, null)
358 | expect(response).to.haveOwnProperty("statusCode").that.equals(200)
359 | expect(response).to.have.property("isBase64Encoded").that.equals(true)
360 | return response
361 | })
362 | })
363 | })
364 | })
365 |
366 | describe("get (httpApi v2)", function () {
367 | it("should return index.html", function () {
368 | const event = mockEventV2("index.html")
369 | let h = new StaticFileHandler(STATIC_FILES_PATH)
370 | return h.get(event, null).then((response) => {
371 | expect(response).to.have.property("statusCode", 200)
372 | expect(response)
373 | .to.have.property("body")
374 | .to.match(/^/)
375 | return response
376 | })
377 | })
378 | it("should support path parameters", function () {
379 | const event = mockEventV2("/binary/vendor/output.css.map", {
380 | pathParameters: { pathvar: "vendor/output.css.map" },
381 | })
382 | let h = new StaticFileHandler(STATIC_FILES_PATH)
383 | const response = h.get(event, null)
384 | expect(response)
385 | .to.eventually.have.ownProperty("statusCode")
386 | .that.equals(200)
387 | return expect(response)
388 | .to.eventually.haveOwnProperty("body")
389 | .that.is.a("string")
390 | .and.has.length(107)
391 | })
392 | })
393 | })
394 |
--------------------------------------------------------------------------------
/src/test/data/testfiles/README.md:
--------------------------------------------------------------------------------
1 | This directory is not empty. Is it?
2 |
--------------------------------------------------------------------------------
/src/test/data/testfiles/blah.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/src/test/data/testfiles/blah.bin
--------------------------------------------------------------------------------
/src/test/data/testfiles/custom-error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CUSTOM
6 |
7 |
8 |
9 | CUSTOM {{errorText}}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/test/data/testfiles/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/src/test/data/testfiles/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/src/test/data/testfiles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/test/data/testfiles/jpg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/src/test/data/testfiles/jpg.jpg
--------------------------------------------------------------------------------
/src/test/data/testfiles/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/src/test/data/testfiles/png.png
--------------------------------------------------------------------------------
/src/test/data/testfiles/unknown-mime-type.unknowntype:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/src/test/data/testfiles/unknown-mime-type.unknowntype
--------------------------------------------------------------------------------
/src/test/data/testfiles/vendor/output.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["input.less"],"names":[],"mappings":"AAGA;EACE,WAAA;EACA,YAAA","file":"output.css"}
--------------------------------------------------------------------------------
/src/test/data/testfiles/vendor/output.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"hello.js","sourceRoot":"","sources":["hello.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA"}
--------------------------------------------------------------------------------
/src/test/e2e.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | const chai = require("chai")
4 | const chaiAsPromised = require("chai-as-promised")
5 | chai.use(chaiAsPromised)
6 | const expect = chai.expect
7 | const path = require("path")
8 | const { spawnSync } = require("child_process")
9 | const { versions } = require("process")
10 |
11 | function nodeVersionMajor() /* : Number */ {
12 | return Number(versions.node.split(".")[0])
13 | }
14 |
15 | describe("e2e", function () {
16 | // npm install & serverless loading a project is pretty slow (apparently damn slow on node8: https://travis-ci.org/activescott/serverless-aws-static-file-handler/jobs/632405805?utm_medium=notification&utm_source=github_status)
17 | this.timeout(60000)
18 |
19 | it("should load plugin", function () {
20 | // Serverless Framework v3 does not support Node.js v10. Please upgrade Node.js to the latest LTS version (v12 is a minimum supported version):
21 | if (nodeVersionMajor() < 20) {
22 | console.warn(
23 | "Serverless Framework v3 does not support Node.js v10, skipping test"
24 | )
25 | this.skip()
26 | return
27 | }
28 | // does a simple load of a plugin per https://github.com/activescott/serverless-aws-static-file-handler/issues/32
29 | const proc = loadServerless("../../test-files/basic-project")
30 | expect(proc).to.haveOwnProperty("status", 0)
31 | })
32 | })
33 |
34 | function loadServerless(projectDir) {
35 | // serverless print will load all the plugins and fail if they fail to load
36 | const cwd = path.join(__dirname, projectDir)
37 | // first run npm install
38 | console.log("Running npm install for project at '" + projectDir + "'...")
39 | const npmProc = spawnSync("npm", ["install"], {
40 | cwd: cwd,
41 | })
42 | if (npmProc.status !== 0) {
43 | console.error(
44 | "npm install failed for project '" + projectDir + "'.\nsdtout:",
45 | npmProc.stdout.toString(),
46 | "\n stderr:",
47 | npmProc.stderr.toString()
48 | )
49 | return npmProc
50 | }
51 | console.log(
52 | "Running npm install for project at '" + projectDir + "' complete."
53 | )
54 | const slsProc = spawnSync("./node_modules/.bin/serverless", ["print"], {
55 | cwd: cwd,
56 | })
57 | if (slsProc.status !== 0) {
58 | console.error("proc.stdout:", slsProc.stdout.toString())
59 | console.error("proc.stderr:", slsProc.stderr.toString())
60 | }
61 | return slsProc
62 | }
63 |
--------------------------------------------------------------------------------
/test-files/basic-project/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 |
--------------------------------------------------------------------------------
/test-files/basic-project/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/test-files/basic-project/README.md:
--------------------------------------------------------------------------------
1 | This is a test project that is only used for the internal e2e tests.
2 |
--------------------------------------------------------------------------------
/test-files/basic-project/data-files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | These should all load and either be a valid image or a font:
9 |
10 |
11 |
67 |
68 |
--------------------------------------------------------------------------------
/test-files/basic-project/data-files/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/test-files/basic-project/data-files/png.png
--------------------------------------------------------------------------------
/test-files/basic-project/handler.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const path = require("path")
3 | const StaticFileHandler = require("serverless-aws-static-file-handler")
4 | const clientFilesPath = path.join(__dirname, "./data-files/")
5 | const fileHandler = new StaticFileHandler(clientFilesPath)
6 |
7 | module.exports.html = async (event, context) => {
8 | event.path = "index.html" // forcing a specific page for this handler; ignore requested path
9 | return fileHandler.get(event, context)
10 | }
11 |
12 | module.exports.png = async (event, context) => {
13 | event.path = "png.png"
14 | return fileHandler.get(event, context)
15 | }
16 |
--------------------------------------------------------------------------------
/test-files/basic-project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "static-file-handler-demo-proxy",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "deploy": "serverless deploy",
8 | "destroy": "serverless remove"
9 | },
10 | "devDependencies": {
11 | "serverless": "^3.1.1"
12 | },
13 | "dependencies": {
14 | "serverless-aws-static-file-handler": "file:../../serverless-aws-static-file-handler-0.0.0.tgz"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test-files/basic-project/serverless.yml:
--------------------------------------------------------------------------------
1 | service: static-file-handler-test-basic
2 |
3 | plugins:
4 | - serverless-aws-static-file-handler/plugins/BinaryMediaTypes
5 |
6 | custom:
7 | apiGateway:
8 | binaryMediaTypes:
9 | # You can use the wildcard character (*) to cover multiple media types per https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
10 | # NOTE: Using */* has a side effect as noted at https://github.com/activescott/serverless-aws-static-file-handler
11 | # IANA descrete type wildcards from: https://www.iana.org/assignments/media-types/media-types.xhtml
12 | - application/*
13 | - audio/*
14 | - font/*
15 | - image/*
16 | - video/*
17 |
18 | provider:
19 | name: aws
20 | runtime: nodejs20.x
21 |
22 | functions:
23 | html:
24 | handler: handler.html
25 | events:
26 | - http:
27 | path: /
28 | method: get
29 | png:
30 | handler: handler.png
31 | events:
32 | - http:
33 | path: png
34 | method: get
35 |
--------------------------------------------------------------------------------
/test-files/scripts/test-http.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | ## allows curl to some known HTTP endpoint and confirm an expected status code
3 | URL_PATH=$1
4 | if [ -z "$URL_PATH" ]; then
5 | echo "path not specified"
6 | return 1
7 | fi
8 | EXPECT_CODE=$2
9 | if [ -z "$EXPECT_CODE" ]; then
10 | echo "EXPECT_CODE not specified; defaulting to 200"
11 | EXPECT_CODE=200
12 | fi
13 |
14 | echo "Testing $URL_PATH for code $EXPECT_CODE..."
15 | HTTP_CODE=$(curl -s -w '%{http_code}' --compressed --output /dev/null "$URL_PATH")
16 | if [ "$HTTP_CODE" = "$EXPECT_CODE" ]; then
17 | echo "Testing $URL_PATH succeeded (returned expected code $HTTP_CODE)"
18 | exit 0
19 | else
20 | echo "\n˅˅˅˅˅ FAILURE ˅˅˅˅˅"
21 | echo "*** $URL_PATH *FAILED*. . Expected to have code $EXPECT_CODE but was $HTTP_CODE ***"
22 | echo "^^^^^ FAILURE ^^^^^\n"
23 | exit 1
24 | fi
25 |
--------------------------------------------------------------------------------
/test-files/scripts/test-local-e2e.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | THISDIR=$(cd $(dirname "$0"); pwd) #this script's directory
3 |
4 | ##################################################
5 | # Runs an end-to-end test locally using serverless-offline
6 | # TLDR; this test curls to some known good endpoints looking for an expected HTTP response code:
7 | ##################################################
8 |
9 | die () {
10 | echo >&2 "$@"
11 | stop_serverless_offline
12 | help
13 | exit 1
14 | }
15 |
16 | help () {
17 | echo
18 | cat << END_DOC
19 | The script $THISSCRIPT failed. This likely means the e2e tests failed. See output above to confirm.
20 | END_DOC
21 |
22 | }
23 |
24 | REPO_ROOT_DIR=$(cd "$THISDIR/../.." ; pwd)
25 | echo "Using REPO_ROOT_DIR $REPO_ROOT_DIR"
26 |
27 | cd "$REPO_ROOT_DIR/examples/serverless-offline"
28 |
29 | echo "Running npm install..."
30 | npm install -s
31 | echo "Running npm install completed."
32 |
33 | # run severless-offline in background:
34 | start_serverless_offline() {
35 | echo "starting serverless-offline in background..."
36 | ./node_modules/.bin/serverless offline &
37 |
38 | # wait on serverless-offline to start
39 | SECS=10
40 | while [ $SECS -gt 0 ]; do
41 | printf "waiting for $SECS seconds for serverless-offline to start serving...\n"
42 | sleep 1
43 | SECS=`expr $SECS - 1`
44 | done
45 | printf "waiting for seconds for serverless-offline complete. Continuing with test\n"
46 | }
47 |
48 | start_serverless_offline
49 |
50 | stop_serverless_offline() {
51 | # shut down serverles offline now
52 | echo "Killing serverless offline process..."
53 | pkill -f -n "serverless offline"
54 | # sleep for a sec just to get clean output due to the background process
55 | sleep 1
56 | echo "Killing serverless offline process complete."
57 | }
58 |
59 | ROOT_URL=http://localhost:3000/dev
60 |
61 | test_url() {
62 | TEST_URL=$1
63 | TEST_HTTP_RESP_CODE=$2
64 | echo "testing URL '$TEST_URL' with HTTP response code '$TEST_HTTP_RESP_CODE'"
65 | $REPO_ROOT_DIR/test-files/scripts/test-http.sh $TEST_URL $TEST_HTTP_RESP_CODE || die "\n*FAILURE* testing URL '$TEST_URL' with HTTP response code '$TEST_HTTP_RESP_CODE'!"
66 | }
67 |
68 | test_url $ROOT_URL/binary/jpg.jpg
69 |
70 | # 200; these all should succeed
71 | test_url $ROOT_URL/binary/png.png
72 | test_url $ROOT_URL/binary/jpg.jpg
73 | test_url $ROOT_URL/binary/glyphicons-halflings-regular.woff2
74 | test_url $ROOT_URL/binary/subdir/png.png
75 |
76 | # 403 for APIG, but 404 for serverless-offline
77 | test_url "$ROOT_URL/ff404.png" 404
78 | test_url "$ROOT_URL/jpeg404.jpg" 404
79 | test_url "$ROOT_URL/subdir404/ff.png" 404
80 | test_url "$ROOT_URL/subdir/ff404.png" 404
81 |
82 | # 404
83 | test_url "$ROOT_URL/binary/404-glyphicons-halflings-regular.woff2" 404
84 | test_url "$ROOT_URL/binary/subdir/404-png.png" 404
85 |
86 | stop_serverless_offline
87 |
--------------------------------------------------------------------------------
/test-files/webpack-project/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "comments": false,
3 | "presets": [
4 | [
5 | "@babel/preset-env",
6 | {
7 | "targets": {
8 | "node": "12"
9 | }
10 | }
11 | ]
12 | ],
13 | "plugins": [
14 | "source-map-support"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/test-files/webpack-project/.gitignore:
--------------------------------------------------------------------------------
1 | .webpack/
--------------------------------------------------------------------------------
/test-files/webpack-project/README.md:
--------------------------------------------------------------------------------
1 | # webpack example for serverless-aws-static-file-handler
2 |
3 | Things can be a bit different when packed such as paths to files as noted in [issue #109](https://github.com/activescott/serverless-aws-static-file-handler/issues/109). This example/test is a repro of essentially that issue.
4 | Shamelessly stolen from https://github.com/serverless-heaven/serverless-webpack/tree/master/examples/babel-webpack-4 and ever-so-slightly modified to include serverless-aws-static-file-handler.
5 |
--------------------------------------------------------------------------------
/test-files/webpack-project/data-files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | These should all load and either be a valid image or a font:
9 |
10 |
11 |
67 |
68 |
--------------------------------------------------------------------------------
/test-files/webpack-project/data-files/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/serverless-aws-static-file-handler/e1435cebaa3d3348caeb93edde14154c39a92a21/test-files/webpack-project/data-files/png.png
--------------------------------------------------------------------------------
/test-files/webpack-project/event.json:
--------------------------------------------------------------------------------
1 | {
2 | "resource": null,
3 | "httpMethod": "GET",
4 | "headers": {},
5 | "multiValueHeaders": {},
6 | "queryStringParameters": null,
7 | "multiValueQueryStringParameters": null,
8 | "pathParameters": null,
9 | "stageVariables": null,
10 | "requestContext": {},
11 | "body": null,
12 | "isBase64Encoded": false
13 | }
14 |
--------------------------------------------------------------------------------
/test-files/webpack-project/handler.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 | const path = require("path")
3 | const StaticFileHandler = require("serverless-aws-static-file-handler")
4 | const clientFilesPath = path.join(__dirname, "./data-files/")
5 | const fileHandler = new StaticFileHandler(clientFilesPath)
6 |
7 | // require.context triggers file-loader to copy the data-files directory. see https://webpack.js.org/guides/dependency-management/#requirecontext
8 | require.context("./data-files")
9 |
10 | export const html = async (event, context) => {
11 | event.path = "index.html" // forcing a specific page for this handler; ignore requested path
12 | return fileHandler.get(event, context)
13 | }
14 |
15 | export const png = async (event, context) => {
16 | event.path = "png.png"
17 | return fileHandler.get(event, context)
18 | }
19 |
20 | export const notfound = async (event, context) => {
21 | event.path = "notfound.html"
22 | return fileHandler.get(event, context)
23 | }
24 |
--------------------------------------------------------------------------------
/test-files/webpack-project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serverless-aws-static-file-handler-serverless-webpack-example",
3 | "version": "1.0.0",
4 | "description": "serverless-aws-static-file-handler and serverless-webpack example",
5 | "scripts": {
6 | "html": "serverless invoke local --function=html --path=./event.json",
7 | "png": "serverless invoke local --function=png --path=./event.json",
8 | "notfound": "serverless invoke local --function=notfound --path=./event.json"
9 | },
10 | "license": "MIT",
11 | "dependencies": {
12 | "serverless-aws-static-file-handler": "file:../..",
13 | "source-map-support": "^0.5.21"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.26.10",
17 | "@babel/plugin-transform-runtime": "^7.26.10",
18 | "@babel/preset-env": "^7.26.9",
19 | "babel-loader": "^10.0.0",
20 | "babel-plugin-source-map-support": "^2.2.0",
21 | "file-loader": "^6.2.0",
22 | "serverless": "^3.35.2",
23 | "serverless-offline": "^13.3.3",
24 | "serverless-webpack": "^5.15",
25 | "webpack": "^5.98.0",
26 | "webpack-node-externals": "^3.0.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test-files/webpack-project/serverless.yml:
--------------------------------------------------------------------------------
1 | service: babel-webpack-4-example
2 |
3 | # Add the serverless-webpack plugin
4 | plugins:
5 | - serverless-webpack
6 | - serverless-offline
7 |
8 | provider:
9 | name: aws
10 | runtime: nodejs12.x
11 |
12 | custom:
13 | webpack:
14 | webpackConfig: ./webpack.config.js
15 | includeModules: true
16 | # If you use Yarn instead of NPM in your environment, uncomment the following line.
17 | # packager: yarn
18 |
19 | package:
20 | individually: true
21 |
22 | functions:
23 | html:
24 | handler: handler.html
25 | events:
26 | - http:
27 | path: /
28 | method: get
29 | png:
30 | handler: handler.png
31 | events:
32 | - http:
33 | path: png
34 | method: get
35 | notfound:
36 | handler: handler.notfound
37 | events:
38 | - http:
39 | path: notfound
40 | method: get
41 |
--------------------------------------------------------------------------------
/test-files/webpack-project/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path")
2 | const slsw = require("serverless-webpack")
3 | const nodeExternals = require("webpack-node-externals")
4 |
5 | module.exports = {
6 | entry: slsw.lib.entries,
7 | target: "node",
8 | mode: slsw.lib.webpack.isLocal ? "development" : "production",
9 | optimization: {
10 | // We no not want to minimize our code.
11 | minimize: false,
12 | },
13 | performance: {
14 | // Turn off size warnings for entry points
15 | hints: false,
16 | },
17 | devtool: "nosources-source-map",
18 | externals: [nodeExternals()],
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js$/,
23 | exclude: /node_modules/,
24 | use: [
25 | {
26 | loader: "babel-loader",
27 | },
28 | ],
29 | },
30 | {
31 | test: /\.(png|jpe?g|gif|html)$/i,
32 | use: [
33 | {
34 | loader: "file-loader",
35 | options: {
36 | name: "[path][name].[ext]",
37 | },
38 | },
39 | ],
40 | },
41 | ],
42 | },
43 | output: {
44 | libraryTarget: "commonjs2",
45 | path: path.join(__dirname, ".webpack"),
46 | filename: "[name].js",
47 | sourceMapFilename: "[file].map",
48 | },
49 | }
50 |
--------------------------------------------------------------------------------