From 2e8c285aed8e5dbaa300db81cb3d5a2f8b016802 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Thu, 16 May 2024 13:40:52 +0100 Subject: [PATCH 01/32] chore(feat/slsa)[SEC-1085]: add publish pipeline [INS-3792] (#347) * chore: add publish pipeline [INS-3792] * refactor build and publish pipeline to add provenance and sbom scan --------- Co-authored-by: saisatishkarra --- .github/workflows/build.yml | 52 ++++++++++++++++++++++++++++++++++- .github/workflows/release.yml | 43 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d9704ca..41da6ace 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,11 @@ -name: Build +name: Build and Publish Httpsnippet on: push: branches: - master + tags: + - '*' # Restrict any specific tag formats pull_request: types: - opened @@ -11,6 +13,26 @@ on: workflow_dispatch: jobs: + scan: + permissions: + packages: write + contents: write # publish sbom to GH releases/tag assets + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@v3 + with: + path: ${{ github.repository }} + + # Perform SCA analysis for the code repository + # Produces SBOM and CVE report + # Helps understand vulnerabilities / license compliance across third party dependencies + - id: sca-project + uses: Kong/public-shared-actions/security-actions/sca@2f02738ecb1670f01391162e43fe3f5d4e7942a1 # v2.2.2 + with: + dir: ${{ github.repository }} + upload-sbom-release-assets: true + build: runs-on: ubuntu-latest strategy: @@ -37,3 +59,31 @@ jobs: - name: Build run: npm run build + + publish: + runs-on: ubuntu-latest + if: ${{ github.ref_type == 'tag' && github.repository_owner == 'Kong' }} + steps: + # checkout tag + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.9.0 + registry-url: 'https://registry.npmjs.org' + + - name: Install + run: npm ci + + - name: Build + run: npm run build + + - name: Publish to NPM + run: npm publish --no-git-checks --provenance --tag ${{ github.sha }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..45e30afd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: Release httpsnippet + +on: + workflow_dispatch: + inputs: + version: + description: 'Tag version to release' + required: true + +env: + # Release Tag to build and publish + TAG: ${{ github.event.inputs.version }} + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git user + uses: Homebrew/actions/git-user-config@master + with: + username: ${{ (github.event_name == 'workflow_dispatch' && github.actor) || 'insomnia-infra' }} + + - name: Tag the Repository + run: | + git tag ${{ env.TAG }} + git push origin ${{ env.TAG }} + + - name: Create Tag and Release + uses: ncipollo/release-action@v1 + id: core_tag_and_release + with: + tag: ${{ env.TAG }} + name: "httpsnippet ${{ env.TAG }} 📦" + generateReleaseNotes: true + prerelease: false + draft: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2aaed3475c806ccc4356054da349833f3e25562c Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Thu, 16 May 2024 16:23:35 +0100 Subject: [PATCH 02/32] fix(CI): add pat for triggering releases [no-ticket] (#349) --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 45e30afd..04d6a4d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: run: | git tag ${{ env.TAG }} git push origin ${{ env.TAG }} - + - name: Create Tag and Release uses: ncipollo/release-action@v1 id: core_tag_and_release @@ -40,4 +40,4 @@ jobs: prerelease: false draft: false env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PAT_INSOMNIA_INFRA }} From 36d0de12b5ce0115b986bc0dc7060f18c3f2d2e5 Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Thu, 16 May 2024 08:38:12 -0700 Subject: [PATCH 03/32] fix(CI): Persist PAT to trigger downstream workflow on tag push (#350) --- .github/workflows/build.yml | 1 + .github/workflows/release.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41da6ace..0d45ca5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,7 @@ jobs: upload-sbom-release-assets: true build: + needs: [scan] runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 04d6a4d0..f5464af1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.PAT_INSOMNIA_INFRA }} + - name: Configure Git user uses: Homebrew/actions/git-user-config@master From cfb34a752442d8a984f255dd6a1dd2bf6a6374a0 Mon Sep 17 00:00:00 2001 From: saisatishkarra Date: Thu, 16 May 2024 09:02:31 -0700 Subject: [PATCH 04/32] fix(ci): permissions to publish npm provenance (#351) --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d45ca5e..7e9a7ff6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,6 +63,11 @@ jobs: publish: runs-on: ubuntu-latest + permissions: + contents: write + id-token: write # For using token to sign images + actions: read # For getting workflow run info to build provenance + packages: write # Required for publishing provenance. Issue: https://github.com/slsa-framework/slsa-github-generator/tree/main/internal/builders/container#known-issues if: ${{ github.ref_type == 'tag' && github.repository_owner == 'Kong' }} steps: # checkout tag From c4a69065fd070557d1854fcd3b5a779f7fc42d3d Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 14:58:17 +0100 Subject: [PATCH 05/32] feat: add npm version to release pipeline [INS-3853] (#352) * feat: add npm version to release pipeline [INS-3853] * fix * add npm ci --- .github/workflows/release.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5464af1..9c656ae7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,16 +21,32 @@ jobs: fetch-depth: 0 token: ${{ secrets.PAT_INSOMNIA_INFRA }} - - name: Configure Git user uses: Homebrew/actions/git-user-config@master with: username: ${{ (github.event_name == 'workflow_dispatch' && github.actor) || 'insomnia-infra' }} + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install + run: npm ci + + - name: Create new package version + run: npm version "${{ env.TAG }}" + + - name: Git Commit + run: git commit -am "Bump version to ${{ env.TAG }}" + - name: Tag the Repository run: | git tag ${{ env.TAG }} - git push origin ${{ env.TAG }} + remote_repo="https://${GITHUB_ACTOR}:${RELEASE_GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git push "${remote_repo}" --follow-tags + env: + RELEASE_GH_TOKEN: ${{ secrets.PAT_INSOMNIA_INFRA }} - name: Create Tag and Release uses: ncipollo/release-action@v1 From 91be017439eca955b51f8f60b1beea5d51dee796 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 15:19:53 +0100 Subject: [PATCH 06/32] fix: rm extra commit on release.yml [INS-3853] (#353) --- .github/workflows/release.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c656ae7..297c1373 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,9 +37,6 @@ jobs: - name: Create new package version run: npm version "${{ env.TAG }}" - - name: Git Commit - run: git commit -am "Bump version to ${{ env.TAG }}" - - name: Tag the Repository run: | git tag ${{ env.TAG }} From aa711ef70bfb7f21959b18959583fa1308303760 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 15:33:02 +0100 Subject: [PATCH 07/32] fix: release push [INS-3853] (#354) --- .github/workflows/release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 297c1373..c56dcd0e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,11 +37,10 @@ jobs: - name: Create new package version run: npm version "${{ env.TAG }}" - - name: Tag the Repository + - name: Merge version commit into master run: | - git tag ${{ env.TAG }} remote_repo="https://${GITHUB_ACTOR}:${RELEASE_GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push "${remote_repo}" --follow-tags + git push "${remote_repo}" env: RELEASE_GH_TOKEN: ${{ secrets.PAT_INSOMNIA_INFRA }} From b81e7074933d97761585f7dca884381ef8ffc2a3 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 14:53:38 +0000 Subject: [PATCH 08/32] 3.0.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdcf2cdb..eab90243 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.1", + "version": "3.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.1", + "version": "3.0.2", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 1dcad8df..fa9a9021 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.1", + "version": "3.0.2", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From a3ddd6cd9640e3ee7dca029710cb9bf74d8649ac Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 16:12:42 +0100 Subject: [PATCH 09/32] fix: tag weirdness (#355) * fix: tag weirdness * fix * fix * fix --- .github/workflows/release.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c56dcd0e..5a813b66 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,12 +37,13 @@ jobs: - name: Create new package version run: npm version "${{ env.TAG }}" - - name: Merge version commit into master + - name: DEBUG see tags run: | - remote_repo="https://${GITHUB_ACTOR}:${RELEASE_GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push "${remote_repo}" - env: - RELEASE_GH_TOKEN: ${{ secrets.PAT_INSOMNIA_INFRA }} + git tag --list + git remote -v + + - name: Merge version commit into master + run: git push origin ${{ env.TAG }} - name: Create Tag and Release uses: ncipollo/release-action@v1 @@ -52,6 +53,4 @@ jobs: name: "httpsnippet ${{ env.TAG }} 📦" generateReleaseNotes: true prerelease: false - draft: false - env: - GITHUB_TOKEN: ${{ secrets.PAT_INSOMNIA_INFRA }} + draft: false \ No newline at end of file From 140d4b7d0e77ec84e8c71900ab0138fbc5728510 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 16:17:46 +0100 Subject: [PATCH 10/32] fix: release tags --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a813b66..fc7dffc4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,14 +43,14 @@ jobs: git remote -v - name: Merge version commit into master - run: git push origin ${{ env.TAG }} + run: git push origin v${{ env.TAG }} - name: Create Tag and Release uses: ncipollo/release-action@v1 id: core_tag_and_release with: - tag: ${{ env.TAG }} - name: "httpsnippet ${{ env.TAG }} 📦" + tag: v${{ env.TAG }} + name: "httpsnippet v${{ env.TAG }} 📦" generateReleaseNotes: true prerelease: false draft: false \ No newline at end of file From 771796620002b53e4f58f39ce5d1953baa869922 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 16:24:57 +0100 Subject: [PATCH 11/32] fix: push also to origin --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc7dffc4..4b98f66e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,9 @@ jobs: git remote -v - name: Merge version commit into master - run: git push origin v${{ env.TAG }} + run: | + git push origin v${{ env.TAG }} + git push origin master - name: Create Tag and Release uses: ncipollo/release-action@v1 From 63650f3f6a921e4a617316ba12f14904d9af4b76 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 17 May 2024 15:25:55 +0000 Subject: [PATCH 12/32] 3.0.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index eab90243..2dd07b59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.2", + "version": "3.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.2", + "version": "3.0.4", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index fa9a9021..ea245662 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.2", + "version": "3.0.4", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From 143bb6d3c9c7acd8fa6e0b9d412cbacd25ae5b39 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Tue, 2 Jul 2024 01:14:23 +0100 Subject: [PATCH 13/32] Create SECURITY.md [SEC-1138] --- SECURITY.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..b423b5d6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security Policy + +## Reporting a Vulnerability + +At HTTPSnippet, we take security issues very seriously. If you believe you have found a security vulnerability in our project, we encourage you to disclose it responsibly. Please report any potential security vulnerabilities to us by sending an email to [vulnerability@konghq.com](mailto:vulnerability@konghq.com). + +## How to Report + +1. **Do not publicly disclose the vulnerability**: Please do not create a GitHub issue or post the vulnerability on public forums. Instead, contact us directly at [vulnerability@konghq.com](mailto:vulnerability@konghq.com). +2. **Provide detailed information**: When reporting a vulnerability, please include as much information as possible to help us understand and reproduce the issue. This may include: + - Description of the vulnerability + - Steps to reproduce the issue + - Potential impact + - Any relevant logs or screenshots + +## What to Expect + +- **Acknowledgment**: We will acknowledge receipt of your vulnerability report within 48 hours. +- **Investigation**: Our security team will investigate the report and will keep you informed of the progress. We aim to resolve critical vulnerabilities within 30 days of confirmation. +- **Disclosure**: We prefer coordinated disclosure and will work with you to schedule the disclosure of the vulnerability in a way that minimizes the risk to users. + +## Bug Bounty Program + +We encourage security researchers to participate in our bug bounty program as outlined on the [Kong Vulnerability Disclosure](https://konghq.com/compliance/bug-bounty) page. This program provides rewards for discovering and reporting security vulnerabilities in accordance with our disclosure guidelines. + +Thank you for helping to keep HTTPSnippet secure. + +For more information on our security policies and guidelines, please visit the [Kong Vulnerability Disclosure](https://konghq.com/compliance/bug-bounty) page. + +## Contact + +For any questions or further assistance, please contact us at [vulnerability@konghq.com](mailto:vulnerability@konghq.com). From e901d614579904928555b223f9d864938fa968c7 Mon Sep 17 00:00:00 2001 From: Ben Schofield <47790940+Benjscho@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:28:10 -0700 Subject: [PATCH 14/32] Add rust target with reqwest (#328) * Create initial rust files Create initial rust files to support the target. Create all of the demo fixture files for the different request types. These also require specific `Cargo.toml` dependencies with some features, but I've tried to keep them fully qualified where possible. I'm not sure yet how best to show adding dependencies to a project to enable these features. * Start work on reqwest client Start working on the reqwest conversion client after adding targets. * Complete Adding Rust as target Completed adding Rust as a target. All of the fixtures have been tested in a separate Rust project to verify that they build and successfully run against the Har test endpoint. All tests are running and passing, except for the snapshot that verifies all available targets, not sure where to update that. * Run linter Ran the linter defined in `package.json` All tests except snapshot for targets passing. * Update snapshot to fix available targets test Update the available targets test to fix the snapshot. * lint --------- Co-authored-by: Filipe Freire --- .github/workflows/build.yml | 7 +- .github/workflows/release.yml | 4 +- .github/workflows/sast.yml | 3 +- SECURITY.md | 2 +- package-lock.json | 2 +- package.json | 3 +- src/helpers/__snapshots__/utils.test.ts.snap | 14 + src/helpers/code-builder.ts | 12 + src/helpers/escape.test.ts | 20 +- src/helpers/escape.ts | 69 +++-- src/helpers/headers.ts | 2 - src/targets/c/libcurl/client.ts | 6 +- src/targets/ocaml/cohttp/client.ts | 4 +- src/targets/php/guzzle/client.ts | 8 +- src/targets/php/helpers.ts | 2 +- src/targets/powershell/common.ts | 9 +- src/targets/r/httr/client.ts | 25 +- src/targets/rust/helpers.ts | 84 ++++++ src/targets/rust/reqwest/client.ts | 239 ++++++++++++++++++ .../fixtures/application-form-encoded.rs | 29 +++ .../rust/reqwest/fixtures/application-json.rs | 33 +++ src/targets/rust/reqwest/fixtures/cookies.rs | 22 ++ .../rust/reqwest/fixtures/custom-method.rs | 19 ++ src/targets/rust/reqwest/fixtures/full.rs | 35 +++ src/targets/rust/reqwest/fixtures/headers.rs | 24 ++ src/targets/rust/reqwest/fixtures/https.rs | 18 ++ .../reqwest/fixtures/jsonObj-multiline.rs | 26 ++ .../reqwest/fixtures/jsonObj-null-value.rs | 26 ++ .../rust/reqwest/fixtures/multipart-data.rs | 34 +++ .../rust/reqwest/fixtures/multipart-file.rs | 33 +++ .../fixtures/multipart-form-data-no-params.rs | 24 ++ .../reqwest/fixtures/multipart-form-data.rs | 24 ++ src/targets/rust/reqwest/fixtures/nested.rs | 25 ++ src/targets/rust/reqwest/fixtures/query.rs | 25 ++ src/targets/rust/reqwest/fixtures/short.rs | 18 ++ .../rust/reqwest/fixtures/text-plain.rs | 26 ++ src/targets/rust/target.ts | 14 + src/targets/targets.test.ts | 12 +- src/targets/targets.ts | 2 + tsconfig.build.json | 2 +- 40 files changed, 897 insertions(+), 89 deletions(-) create mode 100644 src/targets/rust/helpers.ts create mode 100644 src/targets/rust/reqwest/client.ts create mode 100644 src/targets/rust/reqwest/fixtures/application-form-encoded.rs create mode 100644 src/targets/rust/reqwest/fixtures/application-json.rs create mode 100644 src/targets/rust/reqwest/fixtures/cookies.rs create mode 100644 src/targets/rust/reqwest/fixtures/custom-method.rs create mode 100644 src/targets/rust/reqwest/fixtures/full.rs create mode 100644 src/targets/rust/reqwest/fixtures/headers.rs create mode 100644 src/targets/rust/reqwest/fixtures/https.rs create mode 100644 src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs create mode 100644 src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-data.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-file.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-form-data.rs create mode 100644 src/targets/rust/reqwest/fixtures/nested.rs create mode 100644 src/targets/rust/reqwest/fixtures/query.rs create mode 100644 src/targets/rust/reqwest/fixtures/short.rs create mode 100644 src/targets/rust/reqwest/fixtures/text-plain.rs create mode 100644 src/targets/rust/target.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e9a7ff6..ab177cee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ on: jobs: scan: permissions: - packages: write + packages: write contents: write # publish sbom to GH releases/tag assets runs-on: ubuntu-latest steps: @@ -74,14 +74,14 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20.9.0 registry-url: 'https://registry.npmjs.org' - + - name: Install run: npm ci @@ -92,4 +92,3 @@ jobs: run: npm publish --no-git-checks --provenance --tag ${{ github.sha }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b98f66e..20d87aed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,7 @@ jobs: id: core_tag_and_release with: tag: v${{ env.TAG }} - name: "httpsnippet v${{ env.TAG }} 📦" + name: 'httpsnippet v${{ env.TAG }} 📦' generateReleaseNotes: true prerelease: false - draft: false \ No newline at end of file + draft: false diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml index 86672638..92cfb544 100644 --- a/.github/workflows/sast.yml +++ b/.github/workflows/sast.yml @@ -4,10 +4,9 @@ on: pull_request: {} push: branches: - - master + - master workflow_dispatch: {} - jobs: semgrep: name: Semgrep SAST diff --git a/SECURITY.md b/SECURITY.md index b423b5d6..17989d2a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ At HTTPSnippet, we take security issues very seriously. If you believe you have ## How to Report 1. **Do not publicly disclose the vulnerability**: Please do not create a GitHub issue or post the vulnerability on public forums. Instead, contact us directly at [vulnerability@konghq.com](mailto:vulnerability@konghq.com). -2. **Provide detailed information**: When reporting a vulnerability, please include as much information as possible to help us understand and reproduce the issue. This may include: +1. **Provide detailed information**: When reporting a vulnerability, please include as much information as possible to help us understand and reproduce the issue. This may include: - Description of the vulnerability - Steps to reproduce the issue - Potential impact diff --git a/package-lock.json b/package-lock.json index 2dd07b59..289dab03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "typescript": "^4.6.3" }, "engines": { - "node": "^14.19.1 || ^16.14.2 || ^18.0.0" + "node": "^14.19.1 || ^16.14.2 || ^18.0.0 || ^20.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index ea245662..2613b2b2 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "request", "requests", "ruby", + "rust", "shell", "snippet", "swift", @@ -72,8 +73,8 @@ "eslint-plugin-jest": "^26.1.3", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-simple-import-sort": "^7.0.0", - "markdownlint-cli2": "^0.5.1", "jest": "^27.5.1", + "markdownlint-cli2": "^0.5.1", "prettier": "^2.6.2", "ts-jest": "^27.1.4", "type-fest": "^2.12.2", diff --git a/src/helpers/__snapshots__/utils.test.ts.snap b/src/helpers/__snapshots__/utils.test.ts.snap index 7abba140..d8af8e02 100644 --- a/src/helpers/__snapshots__/utils.test.ts.snap +++ b/src/helpers/__snapshots__/utils.test.ts.snap @@ -322,6 +322,20 @@ Array [ "key": "ruby", "title": "Ruby", }, + Object { + "clients": Array [ + Object { + "description": "reqwest HTTP library", + "key": "reqwest", + "link": "https://docs.rs/reqwest/latest/reqwest/", + "title": "reqwest", + }, + ], + "default": "reqwest", + "extname": ".rs", + "key": "rust", + "title": "Rust", + }, Object { "clients": Array [ Object { diff --git a/src/helpers/code-builder.ts b/src/helpers/code-builder.ts index 8ad78d65..b0d4b26d 100644 --- a/src/helpers/code-builder.ts +++ b/src/helpers/code-builder.ts @@ -56,6 +56,18 @@ export class CodeBuilder { this.code.push(newLine); }; + /** + * Add the line to the end of the last line. Creates a new line + * if no lines exist yet. + */ + pushToLast = (line: string) => { + if (!this.code) { + this.push(line); + } + const updatedLine = `${this.code[this.code.length - 1]}${line}`; + this.code[this.code.length - 1] = updatedLine; + }; + /** * Add an empty line at the end of current lines */ diff --git a/src/helpers/escape.test.ts b/src/helpers/escape.test.ts index 4750ec38..c9a2e1e2 100644 --- a/src/helpers/escape.test.ts +++ b/src/helpers/escape.test.ts @@ -1,34 +1,26 @@ import { escapeString } from './escape'; -describe('Escape methods', () => { +describe('escape methods', () => { describe('escapeString', () => { it('does nothing to a safe string', () => { - expect( - escapeString("hello world") - ).toBe("hello world"); + expect(escapeString('hello world')).toBe('hello world'); }); it('escapes double quotes by default', () => { - expect( - escapeString('"hello world"') - ).toBe('\\"hello world\\"'); + expect(escapeString('"hello world"')).toBe('\\"hello world\\"'); }); it('escapes newlines by default', () => { - expect( - escapeString('hello\r\nworld') - ).toBe('hello\\r\\nworld'); + expect(escapeString('hello\r\nworld')).toBe('hello\\r\\nworld'); }); it('escapes backslashes', () => { - expect( - escapeString('hello\\world') - ).toBe('hello\\\\world'); + expect(escapeString('hello\\world')).toBe('hello\\\\world'); }); it('escapes unrepresentable characters', () => { expect( - escapeString('hello \u0000') // 0 = ASCII 'null' character + escapeString('hello \u0000'), // 0 = ASCII 'null' character ).toBe('hello \\u0000'); }); }); diff --git a/src/helpers/escape.ts b/src/helpers/escape.ts index cbedfda9..fabe3290 100644 --- a/src/helpers/escape.ts +++ b/src/helpers/escape.ts @@ -31,47 +31,42 @@ export interface EscapeOptions { * for the complete original algorithm. */ export function escapeString(rawValue: any, options: EscapeOptions = {}) { - const { - delimiter = '"', - escapeChar = '\\', - escapeNewlines = true - } = options; + const { delimiter = '"', escapeChar = '\\', escapeNewlines = true } = options; const stringValue = rawValue.toString(); - return [...stringValue].map((c) => { - if (c === '\b') { - return escapeChar + 'b'; - } else if (c === '\t') { - return escapeChar + 't'; - } else if (c === '\n') { - if (escapeNewlines) { - return escapeChar + 'n'; - } else { + return [...stringValue] + .map(c => { + if (c === '\b') { + return `${escapeChar}b`; + } else if (c === '\t') { + return `${escapeChar}t`; + } else if (c === '\n') { + if (escapeNewlines) { + return `${escapeChar}n`; + } return c; // Don't just continue, or this is caught by < \u0020 - } - } else if (c === '\f') { - return escapeChar + 'f'; - } else if (c === '\r') { - if (escapeNewlines) { - return escapeChar + 'r'; - } else { + } else if (c === '\f') { + return `${escapeChar}f`; + } else if (c === '\r') { + if (escapeNewlines) { + return `${escapeChar}r`; + } return c; // Don't just continue, or this is caught by < \u0020 + } else if (c === escapeChar) { + return escapeChar + escapeChar; + } else if (c === delimiter) { + return escapeChar + delimiter; + } else if (c < '\u0020' || c > '\u007E') { + // Delegate the trickier non-ASCII cases to the normal algorithm. Some of these + // are escaped as \uXXXX, whilst others are represented literally. Since we're + // using this primarily for header values that are generally (though not 100% + // strictly?) ASCII-only, this should almost never happen. + return JSON.stringify(c).slice(1, -1); } - } else if (c === escapeChar) { - return escapeChar + escapeChar; - } else if (c === delimiter) { - return escapeChar + delimiter; - } else if (c < '\u0020' || c > '\u007E') { - // Delegate the trickier non-ASCII cases to the normal algorithm. Some of these - // are escaped as \uXXXX, whilst others are represented literally. Since we're - // using this primarily for header values that are generally (though not 100% - // strictly?) ASCII-only, this should almost never happen. - return JSON.stringify(c).slice(1, -1); - } else { return c; - } - }).join(''); + }) + .join(''); } /** @@ -81,8 +76,7 @@ export function escapeString(rawValue: any, options: EscapeOptions = {}) { * * If value is not a string, it will be stringified with .toString() first. */ -export const escapeForSingleQuotes = (value: any) => - escapeString(value, { delimiter: "'" }); +export const escapeForSingleQuotes = (value: any) => escapeString(value, { delimiter: "'" }); /** * Make a string value safe to insert literally into a snippet within double quotes, @@ -91,5 +85,4 @@ export const escapeForSingleQuotes = (value: any) => * * If value is not a string, it will be stringified with .toString() first. */ -export const escapeForDoubleQuotes = (value: any) => - escapeString(value, { delimiter: '"' }); +export const escapeForDoubleQuotes = (value: any) => escapeString(value, { delimiter: '"' }); diff --git a/src/helpers/headers.ts b/src/helpers/headers.ts index b8353863..dfae16c7 100644 --- a/src/helpers/headers.ts +++ b/src/helpers/headers.ts @@ -1,5 +1,3 @@ -import { ValueOf } from 'type-fest'; - type Headers = Record; /** diff --git a/src/targets/c/libcurl/client.ts b/src/targets/c/libcurl/client.ts index 7f6e9b42..7eb778fc 100644 --- a/src/targets/c/libcurl/client.ts +++ b/src/targets/c/libcurl/client.ts @@ -26,7 +26,11 @@ export const libcurl: Client = { push('struct curl_slist *headers = NULL;'); headers.forEach(header => { - push(`headers = curl_slist_append(headers, "${header}: ${escapeForDoubleQuotes(headersObj[header])}");`); + push( + `headers = curl_slist_append(headers, "${header}: ${escapeForDoubleQuotes( + headersObj[header], + )}");`, + ); }); push('curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);'); diff --git a/src/targets/ocaml/cohttp/client.ts b/src/targets/ocaml/cohttp/client.ts index 0528ead7..07fad606 100644 --- a/src/targets/ocaml/cohttp/client.ts +++ b/src/targets/ocaml/cohttp/client.ts @@ -39,7 +39,9 @@ export const cohttp: Client = { if (headers.length === 1) { push( - `let headers = Header.add (Header.init ()) "${headers[0]}" "${escapeForDoubleQuotes(allHeaders[headers[0]])}" in`, + `let headers = Header.add (Header.init ()) "${headers[0]}" "${escapeForDoubleQuotes( + allHeaders[headers[0]], + )}" in`, ); } else if (headers.length > 1) { push('let headers = Header.add_list (Header.init ()) ['); diff --git a/src/targets/php/guzzle/client.ts b/src/targets/php/guzzle/client.ts index 6daf3eff..a9a15fde 100644 --- a/src/targets/php/guzzle/client.ts +++ b/src/targets/php/guzzle/client.ts @@ -123,7 +123,9 @@ export const guzzle: Client = { const headers = Object.keys(headersObj) .sort() .map(function (key) { - return `${opts.indent}${opts.indent}'${key}' => '${escapeForSingleQuotes(headersObj[key])}',`; + return `${ + opts.indent + }${opts.indent}'${key}' => '${escapeForSingleQuotes(headersObj[key])}',`; }); // construct cookies @@ -131,7 +133,9 @@ export const guzzle: Client = { .map(cookie => `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`) .join('; '); if (cookieString.length) { - headers.push(`${opts.indent}${opts.indent}'cookie' => '${escapeForSingleQuotes(cookieString)}',`); + headers.push( + `${opts.indent}${opts.indent}'cookie' => '${escapeForSingleQuotes(cookieString)}',`, + ); } if (headers.length) { diff --git a/src/targets/php/helpers.ts b/src/targets/php/helpers.ts index 5d6670e9..b64a932b 100644 --- a/src/targets/php/helpers.ts +++ b/src/targets/php/helpers.ts @@ -1,4 +1,4 @@ -import { escapeString } from "../../helpers/escape"; +import { escapeString } from '../../helpers/escape'; export const convertType = (obj: any[] | any, indent?: string, lastIndent?: string) => { lastIndent = lastIndent || ''; diff --git a/src/targets/powershell/common.ts b/src/targets/powershell/common.ts index bba0789b..9cf395de 100644 --- a/src/targets/powershell/common.ts +++ b/src/targets/powershell/common.ts @@ -56,9 +56,12 @@ export const generatePowershellConvert = (command: PowershellCommand) => { } if (postData.text) { - commandOptions.push(`-ContentType '${ - escapeString(getHeader(allHeaders, 'content-type'), { delimiter: "'", escapeChar: '`' }) - }'`); + commandOptions.push( + `-ContentType '${escapeString(getHeader(allHeaders, 'content-type'), { + delimiter: "'", + escapeChar: '`', + })}'`, + ); commandOptions.push(`-Body '${postData.text}'`); } diff --git a/src/targets/r/httr/client.ts b/src/targets/r/httr/client.ts index 0d1e92ea..c8c2f206 100644 --- a/src/targets/r/httr/client.ts +++ b/src/targets/r/httr/client.ts @@ -98,31 +98,26 @@ export const httr: Client = { // Construct headers const cookieHeader = getHeader(allHeaders, 'cookie'); - let acceptHeader = getHeader(allHeaders, 'accept'); + const acceptHeader = getHeader(allHeaders, 'accept'); const setCookies = cookieHeader ? `set_cookies(\`${String(cookieHeader) .replace(/;/g, '", `') .replace(/` /g, '`') - .replace(/[=]/g, '` = "') - }")` - : undefined + .replace(/[=]/g, '` = "')}")` + : undefined; - const setAccept = acceptHeader - ? `accept("${escapeForDoubleQuotes(acceptHeader)}")` - : undefined + const setAccept = acceptHeader ? `accept("${escapeForDoubleQuotes(acceptHeader)}")` : undefined; - const setContentType = `content_type("${escapeForDoubleQuotes(postData.mimeType)}")` + const setContentType = `content_type("${escapeForDoubleQuotes(postData.mimeType)}")`; const otherHeaders = Object.entries(allHeaders) // These headers are all handled separately: .filter(([key]) => !['cookie', 'accept', 'content-type'].includes(key.toLowerCase())) .map(([key, value]) => `'${key}' = '${escapeForSingleQuotes(value)}'`) - .join(', ') + .join(', '); - const setHeaders = otherHeaders - ? `add_headers(${otherHeaders})` - : undefined + const setHeaders = otherHeaders ? `add_headers(${otherHeaders})` : undefined; // Construct request let request = `response <- VERB("${method}", url`; @@ -135,10 +130,12 @@ export const httr: Client = { request += ', query = queryString'; } - const headerAdditions = [setHeaders, setContentType, setAccept, setCookies].filter(x => !!x).join(', '); + const headerAdditions = [setHeaders, setContentType, setAccept, setCookies] + .filter(x => !!x) + .join(', '); if (headerAdditions) { - request += ', ' + headerAdditions + request += `, ${headerAdditions}`; } if (postData.text || postData.jsonObj || postData.params) { diff --git a/src/targets/rust/helpers.ts b/src/targets/rust/helpers.ts new file mode 100644 index 00000000..39844ed8 --- /dev/null +++ b/src/targets/rust/helpers.ts @@ -0,0 +1,84 @@ +function concatValues( + concatType: 'array' | 'object', + values: any, + pretty: boolean, + indentation: string, + indentLevel: number, +): string { + const currentIndent = indentation.repeat(indentLevel); + const closingBraceIndent = indentation.repeat(indentLevel - 1); + const join = pretty ? `,\n${currentIndent}` : ', '; + const openingBrace = concatType === 'object' ? 'json!({' : '('; + const closingBrace = concatType === 'object' ? '})' : ')'; + + if (pretty) { + return `${openingBrace}\n${currentIndent}${values.join( + join, + )}\n${closingBraceIndent}${closingBrace}`; + } + + return `${openingBrace}${values.join(join)}${closingBrace}`; +} + +/** + * Create a valid Rust string of a literal value using serde_json according to its type. + * + * @param {*} value Any Javascript literal + * @param {Object} opts Target options + * @return {string} + */ +export const literalRepresentation = ( + value: any, + opts: Record, + indentLevel?: number, +): any => { + /* + * Note: this version is almost entirely borrowed from the Python client helper. The + * only real modification involves the braces and the types. The helper + * could potentially be parameterised for reuse. + */ + indentLevel = indentLevel === undefined ? 1 : indentLevel + 1; + + switch (Object.prototype.toString.call(value)) { + case '[object Number]': + return value; + + case '[object Array]': { + let pretty = false; + const valuesRep: any = (value as any[]).map(v => { + // Switch to prettify if the value is a dict with more than one key. + if (Object.prototype.toString.call(v) === '[object Object]') { + pretty = Object.keys(v).length > 1; + } + return literalRepresentation(v, opts, indentLevel); + }); + return concatValues('array', valuesRep, pretty, opts.indent, indentLevel); + } + + case '[object Object]': { + const keyValuePairs = []; + for (const key in value) { + keyValuePairs.push(`"${key}": ${literalRepresentation(value[key], opts, indentLevel)}`); + } + return concatValues( + 'object', + keyValuePairs, + opts.pretty && keyValuePairs.length > 1, + opts.indent, + indentLevel, + ); + } + + case '[object Null]': + return 'json!(null)'; + + case '[object Boolean]': + return value ? 'true' : 'false'; + + default: + if (value === null || value === undefined) { + return ''; + } + return `"${value.toString().replace(/"/g, '\\"')}"`; + } +}; diff --git a/src/targets/rust/reqwest/client.ts b/src/targets/rust/reqwest/client.ts new file mode 100644 index 00000000..b06f413f --- /dev/null +++ b/src/targets/rust/reqwest/client.ts @@ -0,0 +1,239 @@ +/** + * @description + * HTTP code snippet generator for Rust using reqwest + * + * @author + * @Benjscho + * + * for any questions or issues regarding the generated code snippet, please open an issue mentioning the author. + */ + +import { CodeBuilder } from '../../../helpers/code-builder'; +import { Client } from '../../targets'; +import { literalRepresentation } from '../helpers'; + +export const reqwest: Client = { + info: { + key: 'reqwest', + title: 'reqwest', + link: 'https://docs.rs/reqwest/latest/reqwest/', + description: 'reqwest HTTP library', + }, + convert: ({ queryObj, url, postData, allHeaders, method }, options) => { + const opts = { + indent: ' ', + pretty: true, + ...options, + }; + + let indentLevel = 0; + + // start snippet + const { push, blank, join, pushToLast, unshift } = new CodeBuilder({ indent: opts.indent }); + + // import reqwest + push('use reqwest;', indentLevel); + blank(); + + // start async main for tokio + push('#[tokio::main]', indentLevel); + push('pub async fn main() {', indentLevel); + indentLevel += 1; + + // add url + push(`let url = "${url}";`, indentLevel); + blank(); + + let hasQuery = false; + // construct query string + if (Object.keys(queryObj).length) { + hasQuery = true; + push('let querystring = [', indentLevel); + indentLevel += 1; + for (const [key, value] of Object.entries(queryObj)) { + push(`("${key}", "${value}"),`, indentLevel); + } + indentLevel -= 1; + push('];', indentLevel); + blank(); + } + + // construct payload + let payload: Record = {}; + const files: Record = {}; + + let hasFiles = false; + let hasForm = false; + let hasBody = false; + let jsonPayload = false; + let isMultipart = false; + switch (postData.mimeType) { + case 'application/json': + if (postData.jsonObj) { + push( + `let payload = ${literalRepresentation(postData.jsonObj, opts, indentLevel)};`, + indentLevel, + ); + } + jsonPayload = true; + break; + + case 'multipart/form-data': + isMultipart = true; + + if (!postData.params) { + push(`let form = reqwest::multipart::Form::new()`, indentLevel); + push(`.text("", "");`, indentLevel + 1); + break; + } + + payload = {}; + postData.params.forEach(p => { + if (p.fileName) { + files[p.name] = p.fileName; + hasFiles = true; + } else { + payload[p.name] = p.value; + } + }); + + if (hasFiles) { + for (const line of fileToPartString) { + push(line, indentLevel); + } + blank(); + } + push(`let form = reqwest::multipart::Form::new()`, indentLevel); + + for (const [name, fileName] of Object.entries(files)) { + push(`.part("${name}", file_to_part("${fileName}").await)`, indentLevel + 1); + } + for (const [name, value] of Object.entries(payload)) { + push(`.text("${name}", "${value}")`, indentLevel + 1); + } + pushToLast(';'); + + break; + + default: { + if (postData.mimeType === 'application/x-www-form-urlencoded' && postData.paramsObj) { + push( + `let payload = ${literalRepresentation(postData.paramsObj, opts, indentLevel)};`, + indentLevel, + ); + hasForm = true; + break; + } + + if (postData.text) { + push( + `let payload = ${literalRepresentation(postData.text, opts, indentLevel)};`, + indentLevel, + ); + hasBody = true; + break; + } + } + } + + if (hasForm || jsonPayload || hasBody) { + unshift(`use serde_json::json;`); + blank(); + } + + let hasHeaders = false; + // construct headers + if (Object.keys(allHeaders).length) { + hasHeaders = true; + push('let mut headers = reqwest::header::HeaderMap::new();', indentLevel); + for (const [key, value] of Object.entries(allHeaders)) { + // Skip setting content-type if there is a file, as this header will + // cause the request to hang, and reqwest will set it for us. + if (key.toLowerCase() === 'content-type' && isMultipart) { + continue; + } + push( + `headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, + indentLevel, + ); + } + blank(); + } + + // construct client + push('let client = reqwest::Client::new();', indentLevel); + + // construct query + switch (method) { + case 'POST': + push(`let response = client.post(url)`, indentLevel); + break; + + case 'GET': + push(`let response = client.get(url)`, indentLevel); + break; + + default: { + push( + `let response = client.request(reqwest::Method::from_str("${method}").unwrap(), url)`, + indentLevel, + ); + unshift(`use std::str::FromStr;`); + break; + } + } + + if (hasQuery) { + push(`.query(&querystring)`, indentLevel + 1); + } + + if (isMultipart) { + push(`.multipart(form)`, indentLevel + 1); + } + + if (hasHeaders) { + push(`.headers(headers)`, indentLevel + 1); + } + + if (jsonPayload) { + push(`.json(&payload)`, indentLevel + 1); + } + + if (hasForm) { + push(`.form(&payload)`, indentLevel + 1); + } + + if (hasBody) { + push(`.body(payload)`, indentLevel + 1); + } + + // send query + push('.send()', indentLevel + 1); + push('.await;', indentLevel + 1); + blank(); + + // Print response + push('let results = response.unwrap()', indentLevel); + push('.json::()', indentLevel + 1); + push('.await', indentLevel + 1); + push('.unwrap();', indentLevel + 1); + blank(); + + push('dbg!(results);', indentLevel); + + push('}\n'); + + return join(); + }, +}; + +const fileToPartString = [ + `async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part {`, + ` let file = tokio::fs::File::open(file_name).await.unwrap();`, + ` let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new());`, + ` let body = reqwest::Body::wrap_stream(stream);`, + ` reqwest::multipart::Part::stream(body)`, + ` .file_name(file_name)`, + ` .mime_str("text/plain").unwrap()`, + `}`, +]; diff --git a/src/targets/rust/reqwest/fixtures/application-form-encoded.rs b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs new file mode 100644 index 00000000..cfd4e705 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs @@ -0,0 +1,29 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({ + "foo": "bar", + "hello": "world" + }); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/x-www-form-urlencoded".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .form(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/application-json.rs b/src/targets/rust/reqwest/fixtures/application-json.rs new file mode 100644 index 00000000..9c2f155d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/application-json.rs @@ -0,0 +1,33 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({ + "number": 1, + "string": "f\"oo", + "arr": (1, 2, 3), + "nested": json!({"a": "b"}), + "arr_mix": (1, "a", json!({"arr_mix_nested": json!({})})), + "boolean": false + }); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/cookies.rs b/src/targets/rust/reqwest/fixtures/cookies.rs new file mode 100644 index 00000000..fd84c788 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/cookies.rs @@ -0,0 +1,22 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("cookie", "foo=bar; bar=baz".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/custom-method.rs b/src/targets/rust/reqwest/fixtures/custom-method.rs new file mode 100644 index 00000000..be726a01 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/custom-method.rs @@ -0,0 +1,19 @@ +use std::str::FromStr; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.request(reqwest::Method::from_str("PROPFIND").unwrap(), url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/full.rs b/src/targets/rust/reqwest/fixtures/full.rs new file mode 100644 index 00000000..6f5d0eb9 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/full.rs @@ -0,0 +1,35 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo", "bar,baz"), + ("baz", "abc"), + ("key", "value"), + ]; + + let payload = json!({"foo": "bar"}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("cookie", "foo=bar; bar=baz".parse().unwrap()); + headers.insert("accept", "application/json".parse().unwrap()); + headers.insert("content-type", "application/x-www-form-urlencoded".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .query(&querystring) + .headers(headers) + .form(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/headers.rs b/src/targets/rust/reqwest/fixtures/headers.rs new file mode 100644 index 00000000..9552aac2 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/headers.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("accept", "application/json".parse().unwrap()); + headers.insert("x-foo", "Bar".parse().unwrap()); + headers.insert("quoted-value", "\"quoted\" 'string'".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.get(url) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/https.rs b/src/targets/rust/reqwest/fixtures/https.rs new file mode 100644 index 00000000..ce06b998 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/https.rs @@ -0,0 +1,18 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "https://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.get(url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs new file mode 100644 index 00000000..2d43904d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({"foo": "bar"}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs new file mode 100644 index 00000000..4c82a856 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({"foo": json!(null)}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-data.rs b/src/targets/rust/reqwest/fixtures/multipart-data.rs new file mode 100644 index 00000000..72644efc --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-data.rs @@ -0,0 +1,34 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { + let file = tokio::fs::File::open(file_name).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let body = reqwest::Body::wrap_stream(stream); + reqwest::multipart::Part::stream(body) + .file_name(file_name) + .mime_str("text/plain").unwrap() + } + + let form = reqwest::multipart::Form::new() + .part("foo", file_to_part("hello.txt").await) + .text("bar", "Bonjour le monde"); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-file.rs b/src/targets/rust/reqwest/fixtures/multipart-file.rs new file mode 100644 index 00000000..fa1260c6 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-file.rs @@ -0,0 +1,33 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { + let file = tokio::fs::File::open(file_name).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let body = reqwest::Body::wrap_stream(stream); + reqwest::multipart::Part::stream(body) + .file_name(file_name) + .mime_str("text/plain").unwrap() + } + + let form = reqwest::multipart::Form::new() + .part("foo", file_to_part("test/fixtures/files/hello.txt").await); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs new file mode 100644 index 00000000..8cc2a5f0 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let form = reqwest::multipart::Form::new() + .text("", ""); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs new file mode 100644 index 00000000..f17bcb00 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let form = reqwest::multipart::Form::new() + .text("foo", "bar"); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/nested.rs b/src/targets/rust/reqwest/fixtures/nested.rs new file mode 100644 index 00000000..d0eec9b8 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/nested.rs @@ -0,0 +1,25 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo[bar]", "baz,zap"), + ("fiz", "buz"), + ("key", "value"), + ]; + + let client = reqwest::Client::new(); + let response = client.get(url) + .query(&querystring) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/query.rs b/src/targets/rust/reqwest/fixtures/query.rs new file mode 100644 index 00000000..3b715402 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/query.rs @@ -0,0 +1,25 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo", "bar,baz"), + ("baz", "abc"), + ("key", "value"), + ]; + + let client = reqwest::Client::new(); + let response = client.get(url) + .query(&querystring) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/short.rs b/src/targets/rust/reqwest/fixtures/short.rs new file mode 100644 index 00000000..886d61d4 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/short.rs @@ -0,0 +1,18 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.get(url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/text-plain.rs b/src/targets/rust/reqwest/fixtures/text-plain.rs new file mode 100644 index 00000000..d23f1487 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/text-plain.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = "Hello World"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "text/plain".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .body(payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/target.ts b/src/targets/rust/target.ts new file mode 100644 index 00000000..2b0ff498 --- /dev/null +++ b/src/targets/rust/target.ts @@ -0,0 +1,14 @@ +import { Target } from '../targets'; +import { reqwest } from './reqwest/client'; + +export const rust: Target = { + info: { + key: 'rust', + title: 'Rust', + extname: '.rs', + default: 'reqwest', + }, + clientsById: { + reqwest, + }, +}; diff --git a/src/targets/targets.test.ts b/src/targets/targets.test.ts index bbd10748..befb3c68 100644 --- a/src/targets/targets.test.ts +++ b/src/targets/targets.test.ts @@ -4,7 +4,17 @@ import path from 'path'; import short from '../fixtures/requests/short.json'; import { availableTargets, extname } from '../helpers/utils'; import { HTTPSnippet, Request } from '../httpsnippet'; -import { addTarget, addTargetClient, Client, ClientId, isClient, isTarget, Target, TargetId, targets } from './targets'; +import { + addTarget, + addTargetClient, + Client, + ClientId, + isClient, + isTarget, + Target, + TargetId, + targets, +} from './targets'; const expectedBasePath = ['src', 'fixtures', 'requests']; diff --git a/src/targets/targets.ts b/src/targets/targets.ts index c2215d5e..bba9eccd 100644 --- a/src/targets/targets.ts +++ b/src/targets/targets.ts @@ -18,6 +18,7 @@ import { powershell } from './powershell/target'; import { python } from './python/target'; import { r } from './r/target'; import { ruby } from './ruby/target'; +import { rust } from './rust/target'; import { shell } from './shell/target'; import { swift } from './swift/target'; @@ -73,6 +74,7 @@ export const targets = { python, r, ruby, + rust, shell, swift, }; diff --git a/tsconfig.build.json b/tsconfig.build.json index c87a0e04..05274469 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -9,7 +9,7 @@ "downlevelIteration": true, "lib": ["ESNext"], "declaration": true, - "declarationMap": true, + "declarationMap": true }, "include": ["src"], "exclude": ["dist", "**/*.test.ts"] From bb81dca186934625ffe211167c5b4c7049448d7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:29:11 +0100 Subject: [PATCH 15/32] chore(deps-dev): bump ws from 7.5.7 to 7.5.10 (#358) Bumps [ws](https://github.com/websockets/ws) from 7.5.7 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.7...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 289dab03..90b85528 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5207,9 +5207,10 @@ } }, "node_modules/ws": { - "version": "7.5.7", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -8728,7 +8729,9 @@ } }, "ws": { - "version": "7.5.7", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} }, From b6b20213e7f313e903213f37bba44bcf4d9be677 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:29:47 +0100 Subject: [PATCH 16/32] chore(deps-dev): bump braces from 3.0.2 to 3.0.3 (#357) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 90b85528..bdef6f37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1709,11 +1709,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2536,9 +2537,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2931,8 +2933,9 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -4854,8 +4857,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -6475,10 +6479,12 @@ } }, "braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-process-hrtime": { @@ -6987,7 +6993,9 @@ } }, "fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -7227,6 +7235,8 @@ }, "is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-obj": { @@ -8519,6 +8529,8 @@ }, "to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" From 893da8bde10ecc790ac3826b9bf60a92b1ea9ade Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:30:43 +0100 Subject: [PATCH 17/32] chore(deps-dev): bump @babel/traverse from 7.22.6 to 7.24.5 (#348) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.6 to 7.24.5. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.5/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 306 ++++++++++++++++++++++------------------------ 1 file changed, 148 insertions(+), 158 deletions(-) diff --git a/package-lock.json b/package-lock.json index bdef6f37..6f47a2a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,12 +69,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -120,14 +121,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -169,22 +170,22 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -254,30 +255,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -307,14 +308,15 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -383,9 +385,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", - "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -544,34 +546,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz", - "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.6", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -587,13 +589,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -961,14 +963,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -984,9 +986,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -999,21 +1001,15 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, "node_modules/@nicolo-ribaudo/semver-v6": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", @@ -5312,12 +5308,13 @@ } }, "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, "@babel/compat-data": { @@ -5350,14 +5347,14 @@ } }, "@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dev": true, "requires": { - "@babel/types": "^7.22.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" } }, @@ -5392,19 +5389,19 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -5455,24 +5452,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true }, "@babel/helper-validator-option": { @@ -5493,14 +5490,15 @@ } }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "dependencies": { "ansi-styles": { @@ -5556,9 +5554,9 @@ } }, "@babel/parser": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", - "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -5653,31 +5651,31 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/traverse": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz", - "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.6", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", "globals": "^11.1.0" }, "dependencies": { @@ -5688,13 +5686,13 @@ } }, "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" } }, @@ -5979,14 +5977,14 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { @@ -5996,9 +5994,9 @@ "dev": true }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true }, "@jridgewell/sourcemap-codec": { @@ -6008,21 +6006,13 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nicolo-ribaudo/semver-v6": { From b9e1d7be8be1b2c79207b51e71c94aaf9a3c5908 Mon Sep 17 00:00:00 2001 From: Sunrise Date: Sat, 13 Jul 2024 01:21:46 +0800 Subject: [PATCH 18/32] Generate Crystal language code (#343) * Generate Crystal language code * Generate Crystal language code * Remove a blank line * fix crystal tests --------- Co-authored-by: Filipe Freire --- README.md | 2 +- package.json | 1 + src/helpers/__snapshots__/utils.test.ts.snap | 14 ++++ src/targets/crystal/native/client.test.ts | 18 +++++ src/targets/crystal/native/client.ts | 72 +++++++++++++++++++ .../fixtures/application-form-encoded.cr | 10 +++ .../native/fixtures/application-json.cr | 10 +++ .../crystal/native/fixtures/cookies.cr | 9 +++ .../crystal/native/fixtures/custom-method.cr | 6 ++ src/targets/crystal/native/fixtures/full.cr | 12 ++++ .../crystal/native/fixtures/headers.cr | 11 +++ src/targets/crystal/native/fixtures/https.cr | 6 ++ .../native/fixtures/insecure-skip-verify.cr | 6 ++ .../native/fixtures/jsonObj-multiline.cr | 10 +++ .../native/fixtures/jsonObj-null-value.cr | 10 +++ .../crystal/native/fixtures/multipart-data.cr | 10 +++ .../crystal/native/fixtures/multipart-file.cr | 10 +++ .../fixtures/multipart-form-data-no-params.cr | 9 +++ .../native/fixtures/multipart-form-data.cr | 10 +++ src/targets/crystal/native/fixtures/nested.cr | 6 ++ src/targets/crystal/native/fixtures/query.cr | 6 ++ src/targets/crystal/native/fixtures/short.cr | 6 ++ .../crystal/native/fixtures/text-plain.cr | 10 +++ src/targets/crystal/target.ts | 14 ++++ src/targets/targets.test.ts | 1 + src/targets/targets.ts | 2 + 26 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 src/targets/crystal/native/client.test.ts create mode 100644 src/targets/crystal/native/client.ts create mode 100644 src/targets/crystal/native/fixtures/application-form-encoded.cr create mode 100644 src/targets/crystal/native/fixtures/application-json.cr create mode 100644 src/targets/crystal/native/fixtures/cookies.cr create mode 100644 src/targets/crystal/native/fixtures/custom-method.cr create mode 100644 src/targets/crystal/native/fixtures/full.cr create mode 100644 src/targets/crystal/native/fixtures/headers.cr create mode 100644 src/targets/crystal/native/fixtures/https.cr create mode 100644 src/targets/crystal/native/fixtures/insecure-skip-verify.cr create mode 100644 src/targets/crystal/native/fixtures/jsonObj-multiline.cr create mode 100644 src/targets/crystal/native/fixtures/jsonObj-null-value.cr create mode 100644 src/targets/crystal/native/fixtures/multipart-data.cr create mode 100644 src/targets/crystal/native/fixtures/multipart-file.cr create mode 100644 src/targets/crystal/native/fixtures/multipart-form-data-no-params.cr create mode 100644 src/targets/crystal/native/fixtures/multipart-form-data.cr create mode 100644 src/targets/crystal/native/fixtures/nested.cr create mode 100644 src/targets/crystal/native/fixtures/query.cr create mode 100644 src/targets/crystal/native/fixtures/short.cr create mode 100644 src/targets/crystal/native/fixtures/text-plain.cr create mode 100644 src/targets/crystal/target.ts diff --git a/README.md b/README.md index d0c551b8..b3afc23e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![version][npm-version]][npm-url] [![License][npm-license]][license-url] -> HTTP Request snippet generator for _many_ languages & tools including: `cURL`, `HTTPie`, `JavaScript`, `Node`, `C`, `Java`, `PHP`, `Objective-C`, `Swift`, `Python`, `Ruby`, `C#`, `Go`, `OCaml` and [more](https://github.com/Kong/httpsnippet/wiki/Targets)! +> HTTP Request snippet generator for _many_ languages & tools including: `cURL`, `HTTPie`, `JavaScript`, `Node`, `C`, `Java`, `PHP`, `Objective-C`, `Swift`, `Python`, `Ruby`, `C#`, `Go`, `OCaml`, `Crystal` and [more](https://github.com/Kong/httpsnippet/wiki/Targets)! Relies on the popular [HAR](http://www.softwareishard.com/blog/har-12-spec/#request) format to import data and describe HTTP calls. diff --git a/package.json b/package.json index 2613b2b2..37fcc158 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "keywords": [ "api", "clojure", + "crystal", "csharp", "curl", "go", diff --git a/src/helpers/__snapshots__/utils.test.ts.snap b/src/helpers/__snapshots__/utils.test.ts.snap index d8af8e02..8464d7bd 100644 --- a/src/helpers/__snapshots__/utils.test.ts.snap +++ b/src/helpers/__snapshots__/utils.test.ts.snap @@ -30,6 +30,20 @@ Array [ "key": "clojure", "title": "Clojure", }, + Object { + "clients": Array [ + Object { + "description": "Crystal HTTP client", + "key": "native", + "link": "https://crystal-lang.org/api/master/HTTP/Client.html", + "title": "http::client", + }, + ], + "default": "native", + "extname": ".cr", + "key": "crystal", + "title": "Crystal", + }, Object { "clients": Array [ Object { diff --git a/src/targets/crystal/native/client.test.ts b/src/targets/crystal/native/client.test.ts new file mode 100644 index 00000000..f61a181f --- /dev/null +++ b/src/targets/crystal/native/client.test.ts @@ -0,0 +1,18 @@ +import https from '../../../fixtures/requests/https.json'; +import { runCustomFixtures } from '../../../fixtures/runCustomFixtures'; +import { Request } from '../../../httpsnippet'; + +runCustomFixtures({ + targetId: 'crystal', + clientId: 'native', + tests: [ + { + it: 'should support the insecureSkipVerify option', + input: https as Request, + options: { + insecureSkipVerify: true, + }, + expected: 'insecure-skip-verify.cr', + }, + ], +}); diff --git a/src/targets/crystal/native/client.ts b/src/targets/crystal/native/client.ts new file mode 100644 index 00000000..faba72c0 --- /dev/null +++ b/src/targets/crystal/native/client.ts @@ -0,0 +1,72 @@ +/** + * @description + * HTTP code snippet generator for native Crystal + * + * @author + * @18183883296 + * + * for any questions or issues regarding the generated code snippet, please open an issue mentioning the author. + */ +import { CodeBuilder } from '../../../helpers/code-builder'; +import { escapeForDoubleQuotes } from '../../../helpers/escape'; +import { Client } from '../../targets'; + +export interface CrystalNativeOptions { + insecureSkipVerify?: boolean; +} + +export const native: Client = { + info: { + key: 'native', + title: 'http::client', + link: 'https://crystal-lang.org/api/master/HTTP/Client.html', + description: 'Crystal HTTP client', + }, + convert: ({ method: rawMethod, fullUrl, postData, allHeaders }, options = {}) => { + const { insecureSkipVerify = false } = options; + + const { push, blank, join } = new CodeBuilder(); + + push('require "http/client"'); + + blank(); + + push(`url = "${fullUrl}"`); + + const headers = Object.keys(allHeaders); + if (headers.length) { + push('headers = HTTP::Headers{'); + headers.forEach(key => { + push(` "${key}" => "${escapeForDoubleQuotes(allHeaders[key])}"`); + }); + push('}'); + } + + if (postData.text) { + push(`reqBody = ${JSON.stringify(postData.text)}`); + } + + blank(); + + const method = rawMethod.toUpperCase(); + const methods = ['GET', 'POST', 'HEAD', 'DELETE', 'PATCH', 'PUT', 'OPTIONS']; + + const headersContext = headers.length ? ', headers: headers' : ''; + const bodyContext = postData.text ? ', body: reqBody' : ''; + const sslContext = insecureSkipVerify ? ', tls: OpenSSL::SSL::Context::Client.insecure' : ''; + + if (methods.includes(method)) { + push( + `response = HTTP::Client.${method.toLowerCase()} url${headersContext}${bodyContext}${sslContext}`, + ); + } else { + push( + `response = HTTP::Client.exec "${method}", url${headersContext}${bodyContext}${sslContext}`, + ); + } + + push('puts response.body'); + + return join(); + }, +}; diff --git a/src/targets/crystal/native/fixtures/application-form-encoded.cr b/src/targets/crystal/native/fixtures/application-form-encoded.cr new file mode 100644 index 00000000..1a88422b --- /dev/null +++ b/src/targets/crystal/native/fixtures/application-form-encoded.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "application/x-www-form-urlencoded" +} +reqBody = "foo=bar&hello=world" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/application-json.cr b/src/targets/crystal/native/fixtures/application-json.cr new file mode 100644 index 00000000..1389c2f9 --- /dev/null +++ b/src/targets/crystal/native/fixtures/application-json.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "application/json" +} +reqBody = "{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/cookies.cr b/src/targets/crystal/native/fixtures/cookies.cr new file mode 100644 index 00000000..70d45c12 --- /dev/null +++ b/src/targets/crystal/native/fixtures/cookies.cr @@ -0,0 +1,9 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "cookie" => "foo=bar; bar=baz" +} + +response = HTTP::Client.post url, headers: headers +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/custom-method.cr b/src/targets/crystal/native/fixtures/custom-method.cr new file mode 100644 index 00000000..ee1852bf --- /dev/null +++ b/src/targets/crystal/native/fixtures/custom-method.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "http://mockbin.com/har" + +response = HTTP::Client.exec "PROPFIND", url +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/full.cr b/src/targets/crystal/native/fixtures/full.cr new file mode 100644 index 00000000..edd8f4ce --- /dev/null +++ b/src/targets/crystal/native/fixtures/full.cr @@ -0,0 +1,12 @@ +require "http/client" + +url = "http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value" +headers = HTTP::Headers{ + "cookie" => "foo=bar; bar=baz" + "accept" => "application/json" + "content-type" => "application/x-www-form-urlencoded" +} +reqBody = "foo=bar" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/headers.cr b/src/targets/crystal/native/fixtures/headers.cr new file mode 100644 index 00000000..1061d33b --- /dev/null +++ b/src/targets/crystal/native/fixtures/headers.cr @@ -0,0 +1,11 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "accept" => "application/json" + "x-foo" => "Bar" + "quoted-value" => "\"quoted\" 'string'" +} + +response = HTTP::Client.get url, headers: headers +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/https.cr b/src/targets/crystal/native/fixtures/https.cr new file mode 100644 index 00000000..c9fa4fd6 --- /dev/null +++ b/src/targets/crystal/native/fixtures/https.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "https://mockbin.com/har" + +response = HTTP::Client.get url +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/insecure-skip-verify.cr b/src/targets/crystal/native/fixtures/insecure-skip-verify.cr new file mode 100644 index 00000000..3349fc84 --- /dev/null +++ b/src/targets/crystal/native/fixtures/insecure-skip-verify.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "https://mockbin.com/har" + +response = HTTP::Client.get url, tls: OpenSSL::SSL::Context::Client.insecure +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/jsonObj-multiline.cr b/src/targets/crystal/native/fixtures/jsonObj-multiline.cr new file mode 100644 index 00000000..12ce7198 --- /dev/null +++ b/src/targets/crystal/native/fixtures/jsonObj-multiline.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "application/json" +} +reqBody = "{\n \"foo\": \"bar\"\n}" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/jsonObj-null-value.cr b/src/targets/crystal/native/fixtures/jsonObj-null-value.cr new file mode 100644 index 00000000..053961c1 --- /dev/null +++ b/src/targets/crystal/native/fixtures/jsonObj-null-value.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "application/json" +} +reqBody = "{\"foo\":null}" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/multipart-data.cr b/src/targets/crystal/native/fixtures/multipart-data.cr new file mode 100644 index 00000000..72ace104 --- /dev/null +++ b/src/targets/crystal/native/fixtures/multipart-data.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "multipart/form-data; boundary=---011000010111000001101001" +} +reqBody = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"bar\"\r\n\r\nBonjour le monde\r\n-----011000010111000001101001--\r\n" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/multipart-file.cr b/src/targets/crystal/native/fixtures/multipart-file.cr new file mode 100644 index 00000000..1555da83 --- /dev/null +++ b/src/targets/crystal/native/fixtures/multipart-file.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "multipart/form-data; boundary=---011000010111000001101001" +} +reqBody = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n-----011000010111000001101001--\r\n" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/multipart-form-data-no-params.cr b/src/targets/crystal/native/fixtures/multipart-form-data-no-params.cr new file mode 100644 index 00000000..3cbcc9c5 --- /dev/null +++ b/src/targets/crystal/native/fixtures/multipart-form-data-no-params.cr @@ -0,0 +1,9 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "Content-Type" => "multipart/form-data" +} + +response = HTTP::Client.post url, headers: headers +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/multipart-form-data.cr b/src/targets/crystal/native/fixtures/multipart-form-data.cr new file mode 100644 index 00000000..d5216d7d --- /dev/null +++ b/src/targets/crystal/native/fixtures/multipart-form-data.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "Content-Type" => "multipart/form-data; boundary=---011000010111000001101001" +} +reqBody = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/nested.cr b/src/targets/crystal/native/fixtures/nested.cr new file mode 100644 index 00000000..f6fbf4b3 --- /dev/null +++ b/src/targets/crystal/native/fixtures/nested.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "http://mockbin.com/har?foo%5Bbar%5D=baz%2Czap&fiz=buz&key=value" + +response = HTTP::Client.get url +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/query.cr b/src/targets/crystal/native/fixtures/query.cr new file mode 100644 index 00000000..cacc2b3d --- /dev/null +++ b/src/targets/crystal/native/fixtures/query.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value" + +response = HTTP::Client.get url +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/short.cr b/src/targets/crystal/native/fixtures/short.cr new file mode 100644 index 00000000..0715616d --- /dev/null +++ b/src/targets/crystal/native/fixtures/short.cr @@ -0,0 +1,6 @@ +require "http/client" + +url = "http://mockbin.com/har" + +response = HTTP::Client.get url +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/native/fixtures/text-plain.cr b/src/targets/crystal/native/fixtures/text-plain.cr new file mode 100644 index 00000000..8890116b --- /dev/null +++ b/src/targets/crystal/native/fixtures/text-plain.cr @@ -0,0 +1,10 @@ +require "http/client" + +url = "http://mockbin.com/har" +headers = HTTP::Headers{ + "content-type" => "text/plain" +} +reqBody = "Hello World" + +response = HTTP::Client.post url, headers: headers, body: reqBody +puts response.body \ No newline at end of file diff --git a/src/targets/crystal/target.ts b/src/targets/crystal/target.ts new file mode 100644 index 00000000..f3714af5 --- /dev/null +++ b/src/targets/crystal/target.ts @@ -0,0 +1,14 @@ +import { Target } from '../targets'; +import { native } from './native/client'; + +export const crystal: Target = { + info: { + key: 'crystal', + title: 'Crystal', + extname: '.cr', + default: 'native', + }, + clientsById: { + native, + }, +}; diff --git a/src/targets/targets.test.ts b/src/targets/targets.test.ts index befb3c68..48a969de 100644 --- a/src/targets/targets.test.ts +++ b/src/targets/targets.test.ts @@ -84,6 +84,7 @@ availableTargets() expect(result).toStrictEqual(expected); }); } catch (error) { + console.error(error); throw new Error( `Missing a test file for ${targetId}:${clientId} for the ${fixture} fixture.\nExpected to find the output fixture: \`/src/targets/${targetId}/${clientId}/fixtures/${fixture}${fixtureExtension}\``, ); diff --git a/src/targets/targets.ts b/src/targets/targets.ts index bba9eccd..7f9362d4 100644 --- a/src/targets/targets.ts +++ b/src/targets/targets.ts @@ -4,6 +4,7 @@ import { CodeBuilderOptions } from '../helpers/code-builder'; import { Request } from '../httpsnippet'; import { c } from './c/target'; import { clojure } from './clojure/target'; +import { crystal } from './crystal/target'; import { csharp } from './csharp/target'; import { go } from './go/target'; import { http } from './http/target'; @@ -60,6 +61,7 @@ export interface Target { export const targets = { c, clojure, + crystal, csharp, go, http, From e99422a58ea2d46024197c95cc61530d8e84fc2b Mon Sep 17 00:00:00 2001 From: Navdeep Singh Rathore <57226514+nsrCodes@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:14:06 +0530 Subject: [PATCH 19/32] Fix setting default value of request.PostData of HarEntry (#323) * fix: prevent override of default value of request.postData when postData might be undefined * chore: optional chaining safety check for when request.PostData can be undefined --- src/httpsnippet.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/httpsnippet.ts b/src/httpsnippet.ts index 8d315a5c..53171f13 100644 --- a/src/httpsnippet.ts +++ b/src/httpsnippet.ts @@ -100,10 +100,10 @@ export class HTTPSnippet { cookies: [], httpVersion: 'HTTP/1.1', queryString: [], - postData: { + ...request, + postData: request?.postData || { mimeType: request.postData?.mimeType || 'application/octet-stream', }, - ...request, }; if (validateHarRequest(req)) { @@ -164,7 +164,7 @@ export class HTTPSnippet { request.allHeaders.cookie = cookies.join('; '); } - switch (request.postData.mimeType) { + switch (request?.postData.mimeType) { case 'multipart/mixed': case 'multipart/related': case 'multipart/form-data': From 29947cc13b6f700949e18046ce31ccaf95a0dcc9 Mon Sep 17 00:00:00 2001 From: Boris Verkhovskiy Date: Fri, 12 Jul 2024 11:46:57 -0600 Subject: [PATCH 20/32] Support any method for PowerShell (#320) * Support any method for PowerShell * fix test --------- Co-authored-by: Filipe Freire --- src/targets/powershell/common.ts | 20 +++++++++++++------ .../restmethod/fixtures/custom-method.ps1 | 2 +- .../webrequest/fixtures/custom-method.ps1 | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/targets/powershell/common.ts b/src/targets/powershell/common.ts index 9cf395de..cca01e36 100644 --- a/src/targets/powershell/common.ts +++ b/src/targets/powershell/common.ts @@ -16,11 +16,19 @@ export const generatePowershellConvert = (command: PowershellCommand) => { allHeaders, }) => { const { push, join } = new CodeBuilder(); - const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']; - - if (!methods.includes(method.toUpperCase())) { - return 'Method not supported'; - } + const methods = [ + 'DEFAULT', + 'DELETE', + 'GET', + 'HEAD', + 'MERGE', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', + 'TRACE' + ]; + const methodArg = methods.includes(method.toUpperCase()) ? '-Method' : '-CustomMethod'; const commandOptions = []; @@ -65,7 +73,7 @@ export const generatePowershellConvert = (command: PowershellCommand) => { commandOptions.push(`-Body '${postData.text}'`); } - push(`$response = ${command} -Uri '${fullUrl}' -Method ${method} ${commandOptions.join(' ')}`); + push(`$response = ${command} -Uri '${fullUrl}' ${methodArg} ${method} ${commandOptions.join(' ')}`); return join(); }; return convert; diff --git a/src/targets/powershell/restmethod/fixtures/custom-method.ps1 b/src/targets/powershell/restmethod/fixtures/custom-method.ps1 index 8eb41a68..114e893a 100644 --- a/src/targets/powershell/restmethod/fixtures/custom-method.ps1 +++ b/src/targets/powershell/restmethod/fixtures/custom-method.ps1 @@ -1 +1 @@ -Method not supported \ No newline at end of file +$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -CustomMethod PROPFIND \ No newline at end of file diff --git a/src/targets/powershell/webrequest/fixtures/custom-method.ps1 b/src/targets/powershell/webrequest/fixtures/custom-method.ps1 index 8eb41a68..5f587e40 100644 --- a/src/targets/powershell/webrequest/fixtures/custom-method.ps1 +++ b/src/targets/powershell/webrequest/fixtures/custom-method.ps1 @@ -1 +1 @@ -Method not supported \ No newline at end of file +$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -CustomMethod PROPFIND \ No newline at end of file From 147eb9815b951686f6190b637458c217c843ad8b Mon Sep 17 00:00:00 2001 From: Brian Goad Date: Fri, 12 Jul 2024 13:49:37 -0400 Subject: [PATCH 21/32] chore: add nvmrc and set it to 20 (#262) * Add nvmrc and set it to 14.9 as per https://github.com/Kong/httpsnippet/pull/248 * Use node 18 * bump to node 20 on nvmrc file --------- Co-authored-by: Filipe Freire --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..209e3ef4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 From 8739d73b3c3f8112aebfa54c55caa8355b1d1abd Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 12 Jul 2024 18:50:15 +0100 Subject: [PATCH 22/32] Add generator for Ruby's Faraday client (#362) Co-authored-by: Lukas_Skywalker --- src/helpers/__snapshots__/utils.test.ts.snap | 6 + src/targets/ruby/faraday/client.ts | 107 ++++++++++++++++++ .../fixtures/application-form-encoded.rb | 18 +++ .../ruby/faraday/fixtures/application-json.rb | 13 +++ src/targets/ruby/faraday/fixtures/cookies.rb | 12 ++ .../ruby/faraday/fixtures/custom-method.rb | 1 + src/targets/ruby/faraday/fixtures/full.rb | 22 ++++ src/targets/ruby/faraday/fixtures/headers.rb | 14 +++ src/targets/ruby/faraday/fixtures/https.rb | 11 ++ .../faraday/fixtures/jsonObj-multiline.rb | 13 +++ .../faraday/fixtures/jsonObj-null-value.rb | 13 +++ .../ruby/faraday/fixtures/multipart-data.rb | 13 +++ .../ruby/faraday/fixtures/multipart-file.rb | 13 +++ .../fixtures/multipart-form-data-no-params.rb | 12 ++ .../faraday/fixtures/multipart-form-data.rb | 13 +++ src/targets/ruby/faraday/fixtures/nested.rb | 14 +++ src/targets/ruby/faraday/fixtures/query.rb | 14 +++ src/targets/ruby/faraday/fixtures/short.rb | 11 ++ .../ruby/faraday/fixtures/text-plain.rb | 13 +++ src/targets/ruby/target.ts | 2 + 20 files changed, 335 insertions(+) create mode 100644 src/targets/ruby/faraday/client.ts create mode 100644 src/targets/ruby/faraday/fixtures/application-form-encoded.rb create mode 100644 src/targets/ruby/faraday/fixtures/application-json.rb create mode 100644 src/targets/ruby/faraday/fixtures/cookies.rb create mode 100644 src/targets/ruby/faraday/fixtures/custom-method.rb create mode 100644 src/targets/ruby/faraday/fixtures/full.rb create mode 100644 src/targets/ruby/faraday/fixtures/headers.rb create mode 100644 src/targets/ruby/faraday/fixtures/https.rb create mode 100644 src/targets/ruby/faraday/fixtures/jsonObj-multiline.rb create mode 100644 src/targets/ruby/faraday/fixtures/jsonObj-null-value.rb create mode 100644 src/targets/ruby/faraday/fixtures/multipart-data.rb create mode 100644 src/targets/ruby/faraday/fixtures/multipart-file.rb create mode 100644 src/targets/ruby/faraday/fixtures/multipart-form-data-no-params.rb create mode 100644 src/targets/ruby/faraday/fixtures/multipart-form-data.rb create mode 100644 src/targets/ruby/faraday/fixtures/nested.rb create mode 100644 src/targets/ruby/faraday/fixtures/query.rb create mode 100644 src/targets/ruby/faraday/fixtures/short.rb create mode 100644 src/targets/ruby/faraday/fixtures/text-plain.rb diff --git a/src/helpers/__snapshots__/utils.test.ts.snap b/src/helpers/__snapshots__/utils.test.ts.snap index 8464d7bd..6ba3a2d2 100644 --- a/src/helpers/__snapshots__/utils.test.ts.snap +++ b/src/helpers/__snapshots__/utils.test.ts.snap @@ -330,6 +330,12 @@ Array [ "link": "http://ruby-doc.org/stdlib-2.2.1/libdoc/net/http/rdoc/Net/HTTP.html", "title": "net::http", }, + Object { + "description": "Faraday HTTP client", + "key": "faraday", + "link": "https://github.com/lostisland/faraday", + "title": "faraday", + }, ], "default": "native", "extname": ".rb", diff --git a/src/targets/ruby/faraday/client.ts b/src/targets/ruby/faraday/client.ts new file mode 100644 index 00000000..e8e259a4 --- /dev/null +++ b/src/targets/ruby/faraday/client.ts @@ -0,0 +1,107 @@ +import { CodeBuilder } from '../../../helpers/code-builder'; +import { escapeForSingleQuotes } from '../../../helpers/escape'; +import { Client } from '../../targets'; + +export const faraday: Client = { + info: { + key: 'faraday', + title: 'faraday', + link: 'https://github.com/lostisland/faraday', + description: 'Faraday HTTP client', + }, + convert: ({ uriObj, queryObj, method: rawMethod, fullUrl, postData, allHeaders }, options = {}) => { + const { push, blank, join } = new CodeBuilder(); + + // To support custom methods we check for the supported methods + // and if doesn't exist then we build a custom class for it + const method = rawMethod.toUpperCase(); + const methods = [ + 'GET', + 'POST', + 'HEAD', + 'DELETE', + 'PATCH', + 'PUT', + 'OPTIONS', + 'COPY', + 'LOCK', + 'UNLOCK', + 'MOVE', + 'TRACE', + ]; + + if(!methods.includes(method)) { + push(`# Faraday cannot currently run ${method} requests. Please use another client.`) + return join(); + } + + push("require 'faraday'"); + blank(); + + // Write body to beginning of script + if(postData.mimeType === 'application/x-www-form-urlencoded') { + if (postData.params) { + push(`data = {`); + postData.params.forEach(param => { + push(` :${param.name} => ${JSON.stringify(param.value)},`); + }); + push(`}`); + blank(); + } + } + + push(`conn = Faraday.new(`); + push(` url: '${uriObj.protocol}//${uriObj.host}',`); + if(allHeaders['content-type'] || allHeaders['Content-Type']) { + push(` headers: {'Content-Type' => '${allHeaders['content-type'] || allHeaders['Content-Type']}'}`); + } + push(`)`); + + blank(); + push(`response = conn.${method.toLowerCase()}('${uriObj.pathname}') do |req|`); + + const headers = Object.keys(allHeaders); + if (headers.length) { + headers.forEach(key => { + if(key.toLowerCase() !== 'content-type') { + push(` req.headers['${key}'] = '${escapeForSingleQuotes(allHeaders[key])}'`); + } + }); + } + + Object.keys(queryObj).forEach(name => { + const value = queryObj[name]; + if (Array.isArray(value)) { + push(` req.params['${name}'] = ${JSON.stringify(value)}`) + } else { + push(` req.params['${name}'] = '${value}'`) + } + }); + + switch (postData.mimeType) { + case 'application/x-www-form-urlencoded': + if (postData.params) { + push(` req.body = URI.encode_www_form(data)`); + } + break; + + case 'application/json': + if (postData.jsonObj) { + push(` req.body = ${JSON.stringify(postData.text)}`); + } + break; + + default: + if (postData.text) { + push(` req.body = ${JSON.stringify(postData.text)}`); + } + } + + push('end'); + blank() + push('puts response.status'); + push('puts response.body'); + + return join(); + }, +}; diff --git a/src/targets/ruby/faraday/fixtures/application-form-encoded.rb b/src/targets/ruby/faraday/fixtures/application-form-encoded.rb new file mode 100644 index 00000000..9f15c8af --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/application-form-encoded.rb @@ -0,0 +1,18 @@ +require 'faraday' + +data = { + :foo => "bar", + :hello => "world", +} + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'application/x-www-form-urlencoded'} +) + +response = conn.post('/har') do |req| + req.body = URI.encode_www_form(data) +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/application-json.rb b/src/targets/ruby/faraday/fixtures/application-json.rb new file mode 100644 index 00000000..2c22a0eb --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/application-json.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'application/json'} +) + +response = conn.post('/har') do |req| + req.body = "{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/cookies.rb b/src/targets/ruby/faraday/fixtures/cookies.rb new file mode 100644 index 00000000..5c884d73 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/cookies.rb @@ -0,0 +1,12 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', +) + +response = conn.post('/har') do |req| + req.headers['cookie'] = 'foo=bar; bar=baz' +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/custom-method.rb b/src/targets/ruby/faraday/fixtures/custom-method.rb new file mode 100644 index 00000000..213ada53 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/custom-method.rb @@ -0,0 +1 @@ +# Faraday cannot currently run PROPFIND requests. Please use another client. \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/full.rb b/src/targets/ruby/faraday/fixtures/full.rb new file mode 100644 index 00000000..00cf3c6a --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/full.rb @@ -0,0 +1,22 @@ +require 'faraday' + +data = { + :foo => "bar", +} + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'application/x-www-form-urlencoded'} +) + +response = conn.post('/har') do |req| + req.headers['cookie'] = 'foo=bar; bar=baz' + req.headers['accept'] = 'application/json' + req.params['foo'] = ["bar","baz"] + req.params['baz'] = 'abc' + req.params['key'] = 'value' + req.body = URI.encode_www_form(data) +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/headers.rb b/src/targets/ruby/faraday/fixtures/headers.rb new file mode 100644 index 00000000..efaaa37a --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/headers.rb @@ -0,0 +1,14 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', +) + +response = conn.get('/har') do |req| + req.headers['accept'] = 'application/json' + req.headers['x-foo'] = 'Bar' + req.headers['quoted-value'] = '"quoted" \'string\'' +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/https.rb b/src/targets/ruby/faraday/fixtures/https.rb new file mode 100644 index 00000000..84161367 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/https.rb @@ -0,0 +1,11 @@ +require 'faraday' + +conn = Faraday.new( + url: 'https://mockbin.com', +) + +response = conn.get('/har') do |req| +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/jsonObj-multiline.rb b/src/targets/ruby/faraday/fixtures/jsonObj-multiline.rb new file mode 100644 index 00000000..e6e66b0d --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/jsonObj-multiline.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'application/json'} +) + +response = conn.post('/har') do |req| + req.body = "{\n \"foo\": \"bar\"\n}" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/jsonObj-null-value.rb b/src/targets/ruby/faraday/fixtures/jsonObj-null-value.rb new file mode 100644 index 00000000..d2ecc57a --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/jsonObj-null-value.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'application/json'} +) + +response = conn.post('/har') do |req| + req.body = "{\"foo\":null}" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/multipart-data.rb b/src/targets/ruby/faraday/fixtures/multipart-data.rb new file mode 100644 index 00000000..5dd46a56 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/multipart-data.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'multipart/form-data; boundary=---011000010111000001101001'} +) + +response = conn.post('/har') do |req| + req.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"bar\"\r\n\r\nBonjour le monde\r\n-----011000010111000001101001--\r\n" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/multipart-file.rb b/src/targets/ruby/faraday/fixtures/multipart-file.rb new file mode 100644 index 00000000..4d716896 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/multipart-file.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'multipart/form-data; boundary=---011000010111000001101001'} +) + +response = conn.post('/har') do |req| + req.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n-----011000010111000001101001--\r\n" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/multipart-form-data-no-params.rb b/src/targets/ruby/faraday/fixtures/multipart-form-data-no-params.rb new file mode 100644 index 00000000..fc40bcef --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/multipart-form-data-no-params.rb @@ -0,0 +1,12 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'multipart/form-data'} +) + +response = conn.post('/har') do |req| +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/multipart-form-data.rb b/src/targets/ruby/faraday/fixtures/multipart-form-data.rb new file mode 100644 index 00000000..fe3e1446 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/multipart-form-data.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'multipart/form-data; boundary=---011000010111000001101001'} +) + +response = conn.post('/har') do |req| + req.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/nested.rb b/src/targets/ruby/faraday/fixtures/nested.rb new file mode 100644 index 00000000..1a6966d2 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/nested.rb @@ -0,0 +1,14 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', +) + +response = conn.get('/har') do |req| + req.params['foo[bar]'] = 'baz,zap' + req.params['fiz'] = 'buz' + req.params['key'] = 'value' +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/query.rb b/src/targets/ruby/faraday/fixtures/query.rb new file mode 100644 index 00000000..c418c554 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/query.rb @@ -0,0 +1,14 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', +) + +response = conn.get('/har') do |req| + req.params['foo'] = ["bar","baz"] + req.params['baz'] = 'abc' + req.params['key'] = 'value' +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/short.rb b/src/targets/ruby/faraday/fixtures/short.rb new file mode 100644 index 00000000..8ce34f78 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/short.rb @@ -0,0 +1,11 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', +) + +response = conn.get('/har') do |req| +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/faraday/fixtures/text-plain.rb b/src/targets/ruby/faraday/fixtures/text-plain.rb new file mode 100644 index 00000000..b253cc37 --- /dev/null +++ b/src/targets/ruby/faraday/fixtures/text-plain.rb @@ -0,0 +1,13 @@ +require 'faraday' + +conn = Faraday.new( + url: 'http://mockbin.com', + headers: {'Content-Type' => 'text/plain'} +) + +response = conn.post('/har') do |req| + req.body = "Hello World" +end + +puts response.status +puts response.body \ No newline at end of file diff --git a/src/targets/ruby/target.ts b/src/targets/ruby/target.ts index 7d2e5141..7550bc65 100644 --- a/src/targets/ruby/target.ts +++ b/src/targets/ruby/target.ts @@ -1,5 +1,6 @@ import { Target } from '../targets'; import { native } from './native/client'; +import { faraday } from './faraday/client'; export const ruby: Target = { info: { @@ -10,5 +11,6 @@ export const ruby: Target = { }, clientsById: { native, + faraday }, }; From fbd23b56d9065d88476c9585dc4a7fd77ab59edd Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 12 Jul 2024 18:03:15 +0000 Subject: [PATCH 23/32] 3.0.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f47a2a4..b9192be2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.4", + "version": "3.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.4", + "version": "3.0.5", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 37fcc158..1d728fee 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.4", + "version": "3.0.5", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From 6a758d64cb3e4b9c7d2e4382817ccffc5d3f099a Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Fri, 12 Jul 2024 14:10:02 -0400 Subject: [PATCH 24/32] Prep for Release: source maps, types, prettier (#301) * updates library exports * fixes lint configuration so that it actually fails now, as intended * tidy up --------- Co-authored-by: Filipe Freire --- package.json | 12 ++++++++---- src/httpsnippet.ts | 4 ++-- src/index.ts | 28 ++++++++++++++++++++++++++++ src/targets/powershell/common.ts | 6 ++++-- src/targets/ruby/faraday/client.ts | 24 ++++++++++++++---------- src/targets/ruby/target.ts | 4 ++-- tsconfig.build.json | 15 ++++++++------- 7 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 src/index.ts diff --git a/package.json b/package.json index 1d728fee..e395e6f2 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "author": "Kong ", "homepage": "https://github.com/Kong/httpsnippet", "license": "MIT", - "main": "dist/httpsnippet.js", - "types": "dist/httpsnippet.d.ts", + "main": "dist/index.js", + "types": "dist/index.d.ts", "bin": "bin/httpsnippet", "keywords": [ "api", @@ -51,9 +51,13 @@ "clean": "tsc --build tsconfig.build.json --clean", "prebuild": "npm run clean", "lint": "npm run lint:prettify && npm run lint:code && npm run lint:markdown", - "lint:prettify": "prettier --write .", - "lint:code": "eslint . --ext ts,d.ts,test.ts --fix", + "lint:prettify": "prettier --check .", + "lint:code": "eslint . --ext ts,d.ts,test.ts", "lint:markdown": "markdownlint-cli2 \"**/*.md\" \"#**/node_modules\"", + "lint:fix": "npm run lint:prettify:fix && npm run lint:code:fix && npm run lint:markdown:fix", + "lint:prettify:fix": "prettier --write .", + "lint:code:fix": "eslint . --ext ts,d.ts,test.ts --fix", + "lint:markdown:fix": "markdownlint-cli2-fix \"**/*.md\" \"#**/node_modules\"", "build": "tsc --build tsconfig.build.json", "build:types": "tsc -d --declarationDir dist/lib --declarationMap --emitDeclarationOnly", "test": "jest" diff --git a/src/httpsnippet.ts b/src/httpsnippet.ts index 53171f13..8cb5ccd7 100644 --- a/src/httpsnippet.ts +++ b/src/httpsnippet.ts @@ -53,7 +53,7 @@ interface Entry { request: Partial; } -interface HarEntry { +export interface HarEntry { log: { version: string; creator: { @@ -64,7 +64,7 @@ interface HarEntry { }; } -const isHarEntry = (value: any): value is HarEntry => +export const isHarEntry = (value: any): value is HarEntry => typeof value === 'object' && 'log' in value && typeof value.log === 'object' && diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..3a865a77 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,28 @@ +export { CodeBuilder, CodeBuilderOptions, PostProcessor } from './helpers/code-builder'; +export { EscapeOptions, escapeString } from './helpers/escape'; +export { HARError, validateHarRequest } from './helpers/har-validator'; +export { getHeader, getHeaderName } from './helpers/headers'; +export { AvailableTarget, availableTargets, extname } from './helpers/utils'; +export { + HarEntry, + HarRequest, + HTTPSnippet, + isHarEntry, + Request, + RequestExtras, +} from './httpsnippet'; +export { + addTarget, + addTargetClient, + Client, + ClientId, + ClientInfo, + Converter, + Extension, + isClient, + isTarget, + Target, + TargetId, + TargetInfo, + targets, +} from './targets/targets'; diff --git a/src/targets/powershell/common.ts b/src/targets/powershell/common.ts index cca01e36..4c3b923a 100644 --- a/src/targets/powershell/common.ts +++ b/src/targets/powershell/common.ts @@ -26,7 +26,7 @@ export const generatePowershellConvert = (command: PowershellCommand) => { 'PATCH', 'POST', 'PUT', - 'TRACE' + 'TRACE', ]; const methodArg = methods.includes(method.toUpperCase()) ? '-Method' : '-CustomMethod'; @@ -73,7 +73,9 @@ export const generatePowershellConvert = (command: PowershellCommand) => { commandOptions.push(`-Body '${postData.text}'`); } - push(`$response = ${command} -Uri '${fullUrl}' ${methodArg} ${method} ${commandOptions.join(' ')}`); + push( + `$response = ${command} -Uri '${fullUrl}' ${methodArg} ${method} ${commandOptions.join(' ')}`, + ); return join(); }; return convert; diff --git a/src/targets/ruby/faraday/client.ts b/src/targets/ruby/faraday/client.ts index e8e259a4..493a2d3f 100644 --- a/src/targets/ruby/faraday/client.ts +++ b/src/targets/ruby/faraday/client.ts @@ -9,7 +9,7 @@ export const faraday: Client = { link: 'https://github.com/lostisland/faraday', description: 'Faraday HTTP client', }, - convert: ({ uriObj, queryObj, method: rawMethod, fullUrl, postData, allHeaders }, options = {}) => { + convert: ({ uriObj, queryObj, method: rawMethod, postData, allHeaders }) => { const { push, blank, join } = new CodeBuilder(); // To support custom methods we check for the supported methods @@ -30,8 +30,8 @@ export const faraday: Client = { 'TRACE', ]; - if(!methods.includes(method)) { - push(`# Faraday cannot currently run ${method} requests. Please use another client.`) + if (!methods.includes(method)) { + push(`# Faraday cannot currently run ${method} requests. Please use another client.`); return join(); } @@ -39,7 +39,7 @@ export const faraday: Client = { blank(); // Write body to beginning of script - if(postData.mimeType === 'application/x-www-form-urlencoded') { + if (postData.mimeType === 'application/x-www-form-urlencoded') { if (postData.params) { push(`data = {`); postData.params.forEach(param => { @@ -52,8 +52,12 @@ export const faraday: Client = { push(`conn = Faraday.new(`); push(` url: '${uriObj.protocol}//${uriObj.host}',`); - if(allHeaders['content-type'] || allHeaders['Content-Type']) { - push(` headers: {'Content-Type' => '${allHeaders['content-type'] || allHeaders['Content-Type']}'}`); + if (allHeaders['content-type'] || allHeaders['Content-Type']) { + push( + ` headers: {'Content-Type' => '${ + allHeaders['content-type'] || allHeaders['Content-Type'] + }'}`, + ); } push(`)`); @@ -63,7 +67,7 @@ export const faraday: Client = { const headers = Object.keys(allHeaders); if (headers.length) { headers.forEach(key => { - if(key.toLowerCase() !== 'content-type') { + if (key.toLowerCase() !== 'content-type') { push(` req.headers['${key}'] = '${escapeForSingleQuotes(allHeaders[key])}'`); } }); @@ -72,9 +76,9 @@ export const faraday: Client = { Object.keys(queryObj).forEach(name => { const value = queryObj[name]; if (Array.isArray(value)) { - push(` req.params['${name}'] = ${JSON.stringify(value)}`) + push(` req.params['${name}'] = ${JSON.stringify(value)}`); } else { - push(` req.params['${name}'] = '${value}'`) + push(` req.params['${name}'] = '${value}'`); } }); @@ -98,7 +102,7 @@ export const faraday: Client = { } push('end'); - blank() + blank(); push('puts response.status'); push('puts response.body'); diff --git a/src/targets/ruby/target.ts b/src/targets/ruby/target.ts index 7550bc65..247c7572 100644 --- a/src/targets/ruby/target.ts +++ b/src/targets/ruby/target.ts @@ -1,6 +1,6 @@ import { Target } from '../targets'; -import { native } from './native/client'; import { faraday } from './faraday/client'; +import { native } from './native/client'; export const ruby: Target = { info: { @@ -11,6 +11,6 @@ export const ruby: Target = { }, clientsById: { native, - faraday + faraday, }, }; diff --git a/tsconfig.build.json b/tsconfig.build.json index 05274469..a25f17e4 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,15 +1,16 @@ { "compilerOptions": { - "outDir": "dist", - "rootDir": "src", "allowJs": true, - "resolveJsonModule": true, - "strict": true, - "esModuleInterop": true, + "declaration": true, + "declarationMap": true, "downlevelIteration": true, "lib": ["ESNext"], - "declaration": true, - "declarationMap": true + "esModuleInterop": true, + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "src", + "sourceMap": true, + "strict": true }, "include": ["src"], "exclude": ["dist", "**/*.test.ts"] From 723581e1daeb94c5d2f59044ee6d26ab2fbb8435 Mon Sep 17 00:00:00 2001 From: Filipe Freire Date: Fri, 12 Jul 2024 18:11:19 +0000 Subject: [PATCH 25/32] 3.0.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9192be2..63aab739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.5", + "version": "3.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.5", + "version": "3.0.6", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index e395e6f2..f433d822 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.5", + "version": "3.0.6", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From 08aec7bbd10f64a469f31b0d0b7ad121cedd8d04 Mon Sep 17 00:00:00 2001 From: Vaibhav Raj Singh Date: Tue, 1 Oct 2024 00:27:53 +0530 Subject: [PATCH 26/32] feat: replace ajv usage [INS-145] (#371) * Feat: replace custom har-validator with hard-validator-compiled package * test(http-snippet): update casing * fix: linting --- package-lock.json | 19 ++++++++++--------- package.json | 2 +- src/helpers/har-validator.ts | 31 ------------------------------- src/httpsnippet.test.ts | 2 +- src/httpsnippet.ts | 4 ++-- src/index.ts | 1 - src/targets/har-schema.d.ts | 4 ---- 7 files changed, 14 insertions(+), 49 deletions(-) delete mode 100644 src/helpers/har-validator.ts delete mode 100644 src/targets/har-schema.d.ts diff --git a/package-lock.json b/package-lock.json index 63aab739..a0ac4be5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "chalk": "^4.1.2", "event-stream": "4.0.1", "form-data": "4.0.0", - "har-schema": "^2.0.0", + "har-validator-compiled": "^1.0.0", "stringify-object": "3.3.0", "yargs": "^17.4.0" }, @@ -2721,12 +2721,11 @@ "dev": true, "license": "ISC" }, - "node_modules/har-schema": { - "version": "2.0.0", - "license": "ISC", - "engines": { - "node": ">=4" - } + "node_modules/har-validator-compiled": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/har-validator-compiled/-/har-validator-compiled-1.0.0.tgz", + "integrity": "sha512-dher7nFSx+Ef6OoqVveLClh8itAR3vd8Qx70Lh/hEgP1iGeARAolbci7Y8JBrHIYgFCT6xRdvvL16AR9Zh07Dw==", + "license": "MIT" }, "node_modules/has": { "version": "1.0.3", @@ -7100,8 +7099,10 @@ "version": "4.2.9", "dev": true }, - "har-schema": { - "version": "2.0.0" + "har-validator-compiled": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/har-validator-compiled/-/har-validator-compiled-1.0.0.tgz", + "integrity": "sha512-dher7nFSx+Ef6OoqVveLClh8itAR3vd8Qx70Lh/hEgP1iGeARAolbci7Y8JBrHIYgFCT6xRdvvL16AR9Zh07Dw==" }, "has": { "version": "1.0.3", diff --git a/package.json b/package.json index f433d822..6bbf6a75 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "chalk": "^4.1.2", "event-stream": "4.0.1", "form-data": "4.0.0", - "har-schema": "^2.0.0", + "har-validator-compiled": "^1.0.0", "stringify-object": "3.3.0", "yargs": "^17.4.0" } diff --git a/src/helpers/har-validator.ts b/src/helpers/har-validator.ts deleted file mode 100644 index 45d80271..00000000 --- a/src/helpers/har-validator.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Ajv, { ErrorObject } from 'ajv'; -import { Request } from 'har-format'; -import * as schema from 'har-schema'; - -export class HARError extends Error { - name = 'HARError'; - message = 'validation failed'; - errors: ErrorObject[] = []; - constructor(errors: ErrorObject[]) { - super(); - this.errors = errors; - Error.captureStackTrace(this, this.constructor); - } -} - -const ajv = new Ajv({ - allErrors: true, -}); -ajv.addSchema(schema); - -export const validateHarRequest = (request: any): request is Request => { - const validate = ajv.getSchema('request.json'); - if (!validate) { - throw new Error('failed to find HAR request schema'); - } - const valid = validate(request); - if (!valid && validate.errors) { - throw new HARError(validate.errors); - } - return true; -}; diff --git a/src/httpsnippet.test.ts b/src/httpsnippet.test.ts index c945d6cd..5eab435f 100644 --- a/src/httpsnippet.test.ts +++ b/src/httpsnippet.test.ts @@ -19,7 +19,7 @@ describe('hTTPSnippet', () => { // @ts-expect-error intentionally incorrect const attempt = () => new HTTPSnippet({ ziltoid: 'the omniscient' }); - expect(attempt).toThrow('validation failed'); + expect(attempt).toThrow('Validation Failed'); }); it('should parse HAR file with multiple entries', () => { diff --git a/src/httpsnippet.ts b/src/httpsnippet.ts index 8cb5ccd7..0722c884 100644 --- a/src/httpsnippet.ts +++ b/src/httpsnippet.ts @@ -1,11 +1,11 @@ import { map as eventStreamMap } from 'event-stream'; import FormData from 'form-data'; import { Param, PostDataCommon, Request as NpmHarRequest } from 'har-format'; +import { validateRequest } from 'har-validator-compiled'; import { stringify as queryStringify } from 'querystring'; import { format as urlFormat, parse as urlParse, UrlWithParsedQuery } from 'url'; import { formDataIterator, isBlob } from './helpers/form-data'; -import { validateHarRequest } from './helpers/har-validator'; import { getHeaderName } from './helpers/headers'; import { ReducedHelperObject, reducer } from './helpers/reducer'; import { ClientId, TargetId, targets } from './targets/targets'; @@ -106,7 +106,7 @@ export class HTTPSnippet { }, }; - if (validateHarRequest(req)) { + if (validateRequest(req)) { this.requests.push(this.prepare(req)); } }); diff --git a/src/index.ts b/src/index.ts index 3a865a77..9b3bf454 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ export { CodeBuilder, CodeBuilderOptions, PostProcessor } from './helpers/code-builder'; export { EscapeOptions, escapeString } from './helpers/escape'; -export { HARError, validateHarRequest } from './helpers/har-validator'; export { getHeader, getHeaderName } from './helpers/headers'; export { AvailableTarget, availableTargets, extname } from './helpers/utils'; export { diff --git a/src/targets/har-schema.d.ts b/src/targets/har-schema.d.ts deleted file mode 100644 index 1df95659..00000000 --- a/src/targets/har-schema.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'har-schema' { - const schema: object; - export default schema; -} From 889ef62970046ff22597607bf05f5b919cce302c Mon Sep 17 00:00:00 2001 From: Filipe Freire <11976836+filfreire@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:19:57 +0000 Subject: [PATCH 27/32] 3.0.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0ac4be5..bba29bdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.6", + "version": "3.0.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.6", + "version": "3.0.7", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 6bbf6a75..647b2195 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.6", + "version": "3.0.7", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From d10747920576122da0adcd27251d856a437b67ed Mon Sep 17 00:00:00 2001 From: Michael Welch Date: Tue, 1 Oct 2024 09:31:55 +0000 Subject: [PATCH 28/32] correct the casing of Method enums in RestSharp snippets (#366) Ensure that code snippets use enum members that are pascal cased and not upper cased. For example use `Method.Get` instead of `Method.GET` to ensure the snippets can compile. fixes #365 use `var` for return "type" of RestSharp request response The type `IRestResponse` doesn't exist in latest client (version 112) fixes #367 --- src/targets/csharp/restsharp/client.ts | 11 +++++++++-- .../restsharp/fixtures/application-form-encoded.cs | 4 ++-- .../csharp/restsharp/fixtures/application-json.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/cookies.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/full.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/headers.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/https.cs | 4 ++-- .../csharp/restsharp/fixtures/jsonObj-multiline.cs | 4 ++-- .../csharp/restsharp/fixtures/jsonObj-null-value.cs | 4 ++-- .../csharp/restsharp/fixtures/multipart-data.cs | 4 ++-- .../csharp/restsharp/fixtures/multipart-file.cs | 4 ++-- .../fixtures/multipart-form-data-no-params.cs | 4 ++-- .../csharp/restsharp/fixtures/multipart-form-data.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/nested.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/query.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/short.cs | 4 ++-- src/targets/csharp/restsharp/fixtures/text-plain.cs | 4 ++-- 17 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/targets/csharp/restsharp/client.ts b/src/targets/csharp/restsharp/client.ts index 15f2a6e8..c37df19c 100644 --- a/src/targets/csharp/restsharp/client.ts +++ b/src/targets/csharp/restsharp/client.ts @@ -20,8 +20,15 @@ export const restsharp: Client = { return 'Method not supported'; } + function toPascalCase(str: string): string { + return str.replace( + /\w+/g, + word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), + ); + } + push(`var client = new RestClient("${fullUrl}");`); - push(`var request = new RestRequest(Method.${method.toUpperCase()});`); + push(`var request = new RestRequest("", Method.${toPascalCase(method)});`); // Add headers, including the cookies @@ -39,7 +46,7 @@ export const restsharp: Client = { push(`request.AddParameter("${header}", ${text}, ParameterType.RequestBody);`); } - push('IRestResponse response = client.Execute(request);'); + push('var response = client.Execute(request);'); return join(); }, }; diff --git a/src/targets/csharp/restsharp/fixtures/application-form-encoded.cs b/src/targets/csharp/restsharp/fixtures/application-form-encoded.cs index 4e4744d0..50f004de 100644 --- a/src/targets/csharp/restsharp/fixtures/application-form-encoded.cs +++ b/src/targets/csharp/restsharp/fixtures/application-form-encoded.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "application/x-www-form-urlencoded"); request.AddParameter("application/x-www-form-urlencoded", "foo=bar&hello=world", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/application-json.cs b/src/targets/csharp/restsharp/fixtures/application-json.cs index 2ee8c46f..c766a25b 100644 --- a/src/targets/csharp/restsharp/fixtures/application-json.cs +++ b/src/targets/csharp/restsharp/fixtures/application-json.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "application/json"); request.AddParameter("application/json", "{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/cookies.cs b/src/targets/csharp/restsharp/fixtures/cookies.cs index f0489b65..c4de96e3 100644 --- a/src/targets/csharp/restsharp/fixtures/cookies.cs +++ b/src/targets/csharp/restsharp/fixtures/cookies.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddCookie("foo", "bar"); request.AddCookie("bar", "baz"); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/full.cs b/src/targets/csharp/restsharp/fixtures/full.cs index 14b52d97..d4f61890 100644 --- a/src/targets/csharp/restsharp/fixtures/full.cs +++ b/src/targets/csharp/restsharp/fixtures/full.cs @@ -1,8 +1,8 @@ var client = new RestClient("http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("accept", "application/json"); request.AddHeader("content-type", "application/x-www-form-urlencoded"); request.AddCookie("foo", "bar"); request.AddCookie("bar", "baz"); request.AddParameter("application/x-www-form-urlencoded", "foo=bar", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/headers.cs b/src/targets/csharp/restsharp/fixtures/headers.cs index 9a02c6db..3368e954 100644 --- a/src/targets/csharp/restsharp/fixtures/headers.cs +++ b/src/targets/csharp/restsharp/fixtures/headers.cs @@ -1,6 +1,6 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.GET); +var request = new RestRequest("", Method.Get); request.AddHeader("accept", "application/json"); request.AddHeader("x-foo", "Bar"); request.AddHeader("quoted-value", "\"quoted\" 'string'"); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/https.cs b/src/targets/csharp/restsharp/fixtures/https.cs index 05a36907..1c284c6e 100644 --- a/src/targets/csharp/restsharp/fixtures/https.cs +++ b/src/targets/csharp/restsharp/fixtures/https.cs @@ -1,3 +1,3 @@ var client = new RestClient("https://mockbin.com/har"); -var request = new RestRequest(Method.GET); -IRestResponse response = client.Execute(request); \ No newline at end of file +var request = new RestRequest("", Method.Get); +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/jsonObj-multiline.cs b/src/targets/csharp/restsharp/fixtures/jsonObj-multiline.cs index 14fbe777..aefed778 100644 --- a/src/targets/csharp/restsharp/fixtures/jsonObj-multiline.cs +++ b/src/targets/csharp/restsharp/fixtures/jsonObj-multiline.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "application/json"); request.AddParameter("application/json", "{\n \"foo\": \"bar\"\n}", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/jsonObj-null-value.cs b/src/targets/csharp/restsharp/fixtures/jsonObj-null-value.cs index 36b092c9..8ec7f936 100644 --- a/src/targets/csharp/restsharp/fixtures/jsonObj-null-value.cs +++ b/src/targets/csharp/restsharp/fixtures/jsonObj-null-value.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "application/json"); request.AddParameter("application/json", "{\"foo\":null}", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/multipart-data.cs b/src/targets/csharp/restsharp/fixtures/multipart-data.cs index 7c95fe63..f769b710 100644 --- a/src/targets/csharp/restsharp/fixtures/multipart-data.cs +++ b/src/targets/csharp/restsharp/fixtures/multipart-data.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001"); request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"bar\"\r\n\r\nBonjour le monde\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/multipart-file.cs b/src/targets/csharp/restsharp/fixtures/multipart-file.cs index 47758542..ad5ee10f 100644 --- a/src/targets/csharp/restsharp/fixtures/multipart-file.cs +++ b/src/targets/csharp/restsharp/fixtures/multipart-file.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001"); request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/multipart-form-data-no-params.cs b/src/targets/csharp/restsharp/fixtures/multipart-form-data-no-params.cs index be2fcac1..2dda3e2a 100644 --- a/src/targets/csharp/restsharp/fixtures/multipart-form-data-no-params.cs +++ b/src/targets/csharp/restsharp/fixtures/multipart-form-data-no-params.cs @@ -1,4 +1,4 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("Content-Type", "multipart/form-data"); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/multipart-form-data.cs b/src/targets/csharp/restsharp/fixtures/multipart-form-data.cs index 9eb6893b..5fa2dabf 100644 --- a/src/targets/csharp/restsharp/fixtures/multipart-form-data.cs +++ b/src/targets/csharp/restsharp/fixtures/multipart-form-data.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("Content-Type", "multipart/form-data; boundary=---011000010111000001101001"); request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/nested.cs b/src/targets/csharp/restsharp/fixtures/nested.cs index e28bf319..33af293f 100644 --- a/src/targets/csharp/restsharp/fixtures/nested.cs +++ b/src/targets/csharp/restsharp/fixtures/nested.cs @@ -1,3 +1,3 @@ var client = new RestClient("http://mockbin.com/har?foo%5Bbar%5D=baz%2Czap&fiz=buz&key=value"); -var request = new RestRequest(Method.GET); -IRestResponse response = client.Execute(request); \ No newline at end of file +var request = new RestRequest("", Method.Get); +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/query.cs b/src/targets/csharp/restsharp/fixtures/query.cs index 3e3112c0..82d87b1f 100644 --- a/src/targets/csharp/restsharp/fixtures/query.cs +++ b/src/targets/csharp/restsharp/fixtures/query.cs @@ -1,3 +1,3 @@ var client = new RestClient("http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value"); -var request = new RestRequest(Method.GET); -IRestResponse response = client.Execute(request); \ No newline at end of file +var request = new RestRequest("", Method.Get); +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/short.cs b/src/targets/csharp/restsharp/fixtures/short.cs index b644539c..b62d4344 100644 --- a/src/targets/csharp/restsharp/fixtures/short.cs +++ b/src/targets/csharp/restsharp/fixtures/short.cs @@ -1,3 +1,3 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.GET); -IRestResponse response = client.Execute(request); \ No newline at end of file +var request = new RestRequest("", Method.Get); +var response = client.Execute(request); \ No newline at end of file diff --git a/src/targets/csharp/restsharp/fixtures/text-plain.cs b/src/targets/csharp/restsharp/fixtures/text-plain.cs index a0a672de..cd180a8f 100644 --- a/src/targets/csharp/restsharp/fixtures/text-plain.cs +++ b/src/targets/csharp/restsharp/fixtures/text-plain.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); -var request = new RestRequest(Method.POST); +var request = new RestRequest("", Method.Post); request.AddHeader("content-type", "text/plain"); request.AddParameter("text/plain", "Hello World", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); \ No newline at end of file +var response = client.Execute(request); \ No newline at end of file From 8a1bc90fdcacf9a2ba9e0dde3f75df7765b218c1 Mon Sep 17 00:00:00 2001 From: Filipe Freire <11976836+filfreire@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:00:46 +0000 Subject: [PATCH 29/32] 3.0.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bba29bdc..b32f9f2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.7", + "version": "3.0.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.7", + "version": "3.0.8", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 647b2195..03c880fb 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.7", + "version": "3.0.8", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From 66b587b815f64a6a51a880381df321ef48b25e9d Mon Sep 17 00:00:00 2001 From: Vaibhav Raj Singh Date: Wed, 16 Oct 2024 14:11:36 +0530 Subject: [PATCH 30/32] fix(form-data): fix ssr error due to window object access (#373) * fix(form-data): override import from package root * fix(httpsnippet): linting errors --- src/httpsnippet.ts | 16 +++++++++------- src/types/form-data.d.ts | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 src/types/form-data.d.ts diff --git a/src/httpsnippet.ts b/src/httpsnippet.ts index 0722c884..c46157b3 100644 --- a/src/httpsnippet.ts +++ b/src/httpsnippet.ts @@ -1,5 +1,5 @@ import { map as eventStreamMap } from 'event-stream'; -import FormData from 'form-data'; +import FormData from 'form-data/lib/form_data'; import { Param, PostDataCommon, Request as NpmHarRequest } from 'har-format'; import { validateRequest } from 'har-validator-compiled'; import { stringify as queryStringify } from 'querystring'; @@ -13,6 +13,13 @@ import { ClientId, TargetId, targets } from './targets/targets'; export { availableTargets, extname } from './helpers/utils'; export { addTarget, addTargetClient } from './targets/targets'; +// We're implementing the logic for which FormData object to use, ourselves. +// This allows us to use the native FormData object in the browser and the `form-data` module in Node, +// instead of relying on the package entrypoint to handle that. +const resolveFormData = + // @ts-expect-error — we're only using window.FormData if it exists + typeof window !== 'undefined' && window.FormData ? window.FormData : FormData; + const DEBUG_MODE = false; const debug = { @@ -174,7 +181,7 @@ export class HTTPSnippet { request.postData.mimeType = 'multipart/form-data'; if (request.postData?.params) { - const form = new FormData(); + const form = new resolveFormData(); // The `form-data` module returns one of two things: a native FormData object, or its own polyfill // Since the polyfill does not support the full API of the native FormData object, when this library is running in a browser environment it'll fail on two things: @@ -186,7 +193,6 @@ export class HTTPSnippet { // Since the native FormData object is iterable, we easily detect what version of `form-data` we're working with here to allow `multipart/form-data` requests to be compiled under both browser and Node environments. // // This hack is pretty awful but it's the only way we can use this library in the browser as if we code this against just the native FormData object, we can't polyfill that back into Node because Blob and File objects, which something like `formdata-polyfill` requires, don't exist there. - // @ts-expect-error TODO const isNativeFormData = typeof form[Symbol.iterator] === 'function'; // TODO: THIS ABSOLUTELY MUST BE REMOVED. @@ -194,7 +200,6 @@ export class HTTPSnippet { // easter egg const boundary = '---011000010111000001101001'; // this is binary for "api". yep. if (!isNativeFormData) { - // @ts-expect-error THIS IS WRONG. VERY WRONG. form._boundary = boundary; } @@ -205,16 +210,13 @@ export class HTTPSnippet { if (isNativeFormData) { if (isBlob(value)) { - // @ts-expect-error TODO form.append(name, value, filename); } else { form.append(name, value); } } else { form.append(name, value, { - // @ts-expect-error TODO filename, - // @ts-expect-error TODO contentType: param.contentType || null, }); } diff --git a/src/types/form-data.d.ts b/src/types/form-data.d.ts new file mode 100644 index 00000000..0f43ed74 --- /dev/null +++ b/src/types/form-data.d.ts @@ -0,0 +1,4 @@ +declare module 'form-data/lib/form_data' { + import FormData from 'form-data'; + export default FormData; +} From 5b4474df44564aae889423ab26e80d41f99034df Mon Sep 17 00:00:00 2001 From: Filipe Freire <11976836+filfreire@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:43:16 +0000 Subject: [PATCH 31/32] 3.0.9 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b32f9f2c..5d4e0bf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "httpsnippet", - "version": "3.0.8", + "version": "3.0.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "httpsnippet", - "version": "3.0.8", + "version": "3.0.9", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 03c880fb..633a4414 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.0.8", + "version": "3.0.9", "name": "httpsnippet", "description": "HTTP Request snippet generator for *most* languages", "author": "Kong ", From 73c327c49716c66441a97be67a99c8ff2959e2ad Mon Sep 17 00:00:00 2001 From: Balu George Date: Mon, 24 Mar 2025 15:20:56 +0530 Subject: [PATCH 32/32] fix: Pin all external github actions to their corresponding commit SHAs (#378) * Pin all external github actions to their corresponding commit SHAs * Update security actions to use the latest commit --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sast.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab177cee..cc46c1c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: # Produces SBOM and CVE report # Helps understand vulnerabilities / license compliance across third party dependencies - id: sca-project - uses: Kong/public-shared-actions/security-actions/sca@2f02738ecb1670f01391162e43fe3f5d4e7942a1 # v2.2.2 + uses: Kong/public-shared-actions/security-actions/sca@a18abf762d6e2444bcbfd20de70451ea1e3bc1b1 with: dir: ${{ github.repository }} upload-sbom-release-assets: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20d87aed..8e357624 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: token: ${{ secrets.PAT_INSOMNIA_INFRA }} - name: Configure Git user - uses: Homebrew/actions/git-user-config@master + uses: Homebrew/actions/git-user-config@266845213695c3047d210b2e8fbc42ecdaf45802 # master with: username: ${{ (github.event_name == 'workflow_dispatch' && github.actor) || 'insomnia-infra' }} @@ -48,7 +48,7 @@ jobs: git push origin master - name: Create Tag and Release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 id: core_tag_and_release with: tag: v${{ env.TAG }} diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml index 92cfb544..0236f517 100644 --- a/.github/workflows/sast.yml +++ b/.github/workflows/sast.yml @@ -22,4 +22,4 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: Kong/public-shared-actions/security-actions/semgrep@bd3d75259607dd015bea3b3313123f53b80e9d7f + - uses: Kong/public-shared-actions/security-actions/semgrep@a18abf762d6e2444bcbfd20de70451ea1e3bc1b1