From 22c6eabd133195d22874250a5ae40cb26b851b01 Mon Sep 17 00:00:00 2001 From: James George Date: Fri, 19 Apr 2024 08:38:46 -0700 Subject: [PATCH] chore: migrate `Node.js` implementation for `js-sandbox` to `isolated-vm` (#3973) Co-authored-by: Andrew Bastin --- .github/workflows/tests.yml | 15 +- packages/hoppscotch-cli/bin/hopp.js | 27 ++- packages/hoppscotch-cli/package.json | 3 +- .../src/__tests__/commands/test.spec.ts | 2 +- .../samples/collections/coll-v1-req-v0.json | 78 +++++-- .../samples/collections/coll-v1-req-v1.json | 55 ++++- .../samples/collections/coll-v2-req-v2.json | 61 ++++- .../samples/collections/coll-v2-req-v3.json | 61 ++++- .../samples/collections/secret-envs-coll.json | 72 ++++-- .../secret-envs-persistence-coll.json | 26 +-- ...ecret-envs-persistence-scripting-coll.json | 4 +- .../samples/environments/secret-envs.json | 7 +- .../secret-supplied-values-envs.json | 7 +- .../hoppscotch-cli/src/__tests__/utils.ts | 27 ++- packages/hoppscotch-cli/src/index.ts | 3 +- packages/hoppscotch-cli/src/utils/mutators.ts | 54 +++-- packages/hoppscotch-js-sandbox/jest.config.js | 10 - packages/hoppscotch-js-sandbox/jest.setup.ts | 1 - packages/hoppscotch-js-sandbox/node.d.ts | 4 +- packages/hoppscotch-js-sandbox/package.json | 14 +- packages/hoppscotch-js-sandbox/setupFiles.ts | 15 ++ .../base64-helper-functions.spec.ts | 5 +- .../{testing/envs => env}/get.spec.ts | 5 +- .../{testing/envs => env}/getResolve.spec.ts | 5 +- .../{testing/envs => env}/resolve.spec.ts | 4 +- .../{testing/envs => env}/set.spec.ts | 4 +- .../{testing/envs => env}/unset.spec.ts | 4 +- .../{testing => }/expect/toBe.spec.ts | 7 +- .../{testing => }/expect/toBeLevelxxx.spec.ts | 5 +- .../{testing => }/expect/toBeType.spec.ts | 4 +- .../{testing => }/expect/toHaveLength.spec.ts | 4 +- .../{testing => }/expect/toInclude.spec.ts | 4 +- ...preRequest.spec.ts => pre-request.spec.ts} | 4 +- .../{utils.spec.ts => shared-utils.spec.ts} | 4 +- .../{testing => }/test-runner.spec.ts | 4 +- packages/hoppscotch-js-sandbox/src/node.ts | 2 - .../hoppscotch-js-sandbox/src/node/index.ts | 2 + .../src/node/pre-request.ts | 91 ++++++++ .../src/node/test-runner.ts | 217 ++++++++++++++++++ .../hoppscotch-js-sandbox/src/node/utils.ts | 23 ++ .../src/pre-request/node-vm/index.ts | 38 --- .../src/{utils.ts => shared-utils.ts} | 65 +++++- .../src/test-runner/node-vm/index.ts | 57 ----- packages/hoppscotch-js-sandbox/src/web.ts | 2 - .../hoppscotch-js-sandbox/src/web/index.ts | 2 + .../web-worker => web/pre-request}/index.ts | 0 .../web-worker => web/pre-request}/worker.ts | 2 +- .../web-worker => web/test-runner}/index.ts | 0 .../web-worker => web/test-runner}/worker.ts | 5 +- packages/hoppscotch-js-sandbox/vite.config.ts | 10 +- packages/hoppscotch-js-sandbox/web.d.ts | 4 +- pnpm-lock.yaml | 184 +++++++++++++-- 52 files changed, 1028 insertions(+), 285 deletions(-) delete mode 100644 packages/hoppscotch-js-sandbox/jest.config.js delete mode 100644 packages/hoppscotch-js-sandbox/jest.setup.ts create mode 100644 packages/hoppscotch-js-sandbox/setupFiles.ts rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/base64-helper-functions.spec.ts (95%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing/envs => env}/get.spec.ts (97%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing/envs => env}/getResolve.spec.ts (98%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing/envs => env}/resolve.spec.ts (97%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing/envs => env}/set.spec.ts (97%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing/envs => env}/unset.spec.ts (98%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/expect/toBe.spec.ts (95%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/expect/toBeLevelxxx.spec.ts (99%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/expect/toBeType.spec.ts (98%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/expect/toHaveLength.spec.ts (98%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/expect/toInclude.spec.ts (98%) rename packages/hoppscotch-js-sandbox/src/__tests__/{preRequest.spec.ts => pre-request.spec.ts} (95%) rename packages/hoppscotch-js-sandbox/src/__tests__/{utils.spec.ts => shared-utils.spec.ts} (78%) rename packages/hoppscotch-js-sandbox/src/__tests__/{testing => }/test-runner.spec.ts (95%) delete mode 100644 packages/hoppscotch-js-sandbox/src/node.ts create mode 100644 packages/hoppscotch-js-sandbox/src/node/index.ts create mode 100644 packages/hoppscotch-js-sandbox/src/node/pre-request.ts create mode 100644 packages/hoppscotch-js-sandbox/src/node/test-runner.ts create mode 100644 packages/hoppscotch-js-sandbox/src/node/utils.ts delete mode 100644 packages/hoppscotch-js-sandbox/src/pre-request/node-vm/index.ts rename packages/hoppscotch-js-sandbox/src/{utils.ts => shared-utils.ts} (85%) delete mode 100644 packages/hoppscotch-js-sandbox/src/test-runner/node-vm/index.ts delete mode 100644 packages/hoppscotch-js-sandbox/src/web.ts create mode 100644 packages/hoppscotch-js-sandbox/src/web/index.ts rename packages/hoppscotch-js-sandbox/src/{pre-request/web-worker => web/pre-request}/index.ts (100%) rename packages/hoppscotch-js-sandbox/src/{pre-request/web-worker => web/pre-request}/worker.ts (93%) rename packages/hoppscotch-js-sandbox/src/{test-runner/web-worker => web/test-runner}/index.ts (100%) rename packages/hoppscotch-js-sandbox/src/{test-runner/web-worker => web/test-runner}/worker.ts (93%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9a2af6430..00cb46d6f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,22 +17,21 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup environment run: mv .env.example .env + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - name: Setup pnpm - uses: pnpm/action-setup@v2.2.4 + uses: pnpm/action-setup@v3 with: version: 8 run_install: true - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - cache: pnpm - - name: Run tests run: pnpm test diff --git a/packages/hoppscotch-cli/bin/hopp.js b/packages/hoppscotch-cli/bin/hopp.js index 06750bef9..2a5e91a60 100755 --- a/packages/hoppscotch-cli/bin/hopp.js +++ b/packages/hoppscotch-cli/bin/hopp.js @@ -1,6 +1,31 @@ #!/usr/bin/env node // * The entry point of the CLI +// @ts-check import { cli } from "../dist/index.js"; -cli(process.argv); +import { spawnSync } from "child_process"; +import { cloneDeep } from "lodash-es"; + +const nodeVersion = parseInt(process.versions.node.split(".")[0]); + +// As per isolated-vm documentation, we need to supply `--no-node-snapshot` for node >= 20 +// src: https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements +if (nodeVersion >= 20 && !process.execArgv.includes("--no-node-snapshot")) { + const argCopy = cloneDeep(process.argv); + + // Replace first argument with --no-node-snapshot + // We can get argv[0] from process.argv0 + argCopy[0] = "--no-node-snapshot"; + + const result = spawnSync( + process.argv0, + argCopy, + { stdio: "inherit" } + ); + + // Exit with the same status code as the spawned process + process.exit(result.status ?? 0); +} else { + cli(process.argv); +} diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 33892bcd5..2a6ea7d30 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/cli", - "version": "0.7.0", + "version": "0.7.1", "description": "A CLI to run Hoppscotch test scripts in CI environments.", "homepage": "https://hoppscotch.io", "type": "module", @@ -44,6 +44,7 @@ "axios": "1.6.7", "chalk": "5.3.0", "commander": "11.1.0", + "isolated-vm": "4.7.2", "lodash-es": "4.17.21", "qs": "6.11.2", "verzod": "0.2.2", diff --git a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts index 25b3325d2..e65fac753 100644 --- a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts @@ -224,7 +224,7 @@ describe("Test `hopp test --env ` command:", () => { }); describe("Secret environment variables", () => { - jest.setTimeout(10000); + jest.setTimeout(100000); // Reads secret environment values from system environment test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json index 6393ba6ad..bf63d2ae1 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json @@ -1,27 +1,55 @@ { - "v": 1, - "name": "coll-v1", - "folders": [], - "requests": [ + "v": 1, + "name": "coll-v1", + "folders": [ + { + "v": 1, + "name": "coll-v1-child", + "folders": [], + "requests": [ { - "url": "https://httpbin.org", - "path": "/get", - "headers": [ - { "key": "Inactive-Header", "value": "Inactive Header", "active": false }, - { "key": "Authorization", "value": "Bearer token123", "active": true } - ], - "params": [ - { "key": "key", "value": "value", "active": true }, - { "key": "inactive-key", "value": "inactive-param", "active": false } - ], - "name": "req-v0", - "method": "GET", - "preRequestScript": "", - "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", - "contentType": "application/json", - "body": "", - "auth": "Bearer Token", - "bearerToken": "token123" - } - ] -} \ No newline at end of file + "url": "https://echo.hoppscotch.io", + "path": "/get", + "headers": [ + { "key": "Inactive-Header", "value": "Inactive Header", "active": false }, + { "key": "Authorization", "value": "Bearer token123", "active": true } + ], + "params": [ + { "key": "key", "value": "value", "active": true }, + { "key": "inactive-key", "value": "inactive-param", "active": false } + ], + "name": "req-v0-II", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", + "contentType": "application/json", + "body": "", + "auth": "Bearer Token", + "bearerToken": "token123" + } + ] + } + ], + "requests": [ + { + "url": "https://echo.hoppscotch.io", + "path": "/get", + "headers": [ + { "key": "Inactive-Header", "value": "Inactive Header", "active": false }, + { "key": "Authorization", "value": "Bearer token123", "active": true } + ], + "params": [ + { "key": "key", "value": "value", "active": true }, + { "key": "inactive-key", "value": "inactive-param", "active": false } + ], + "name": "req-v0", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", + "contentType": "application/json", + "body": "", + "auth": "Bearer Token", + "bearerToken": "token123" + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json index 61f9adb52..9bfc979e8 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json @@ -1,11 +1,60 @@ { "v": 1, "name": "coll-v1", - "folders": [], + "folders": [ + { + "v": 1, + "name": "coll-v1-child", + "folders": [], + "requests": [ + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v1-II", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + } + } + ] + } + ], "requests": [ { "v": "1", - "endpoint": "https://httpbin.org/get", + "endpoint": "https://echo.hoppscotch.io", "headers": [ { "key": "Inactive-Header", @@ -33,7 +82,7 @@ "name": "req-v1", "method": "GET", "preRequestScript": "", - "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", "body": { "contentType": null, "body": null diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json index 780373f82..0bcbd1321 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json @@ -1,11 +1,66 @@ { "v": 2, "name": "coll-v2", - "folders": [], + "folders": [ + { + "v": 2, + "name": "coll-v2-child", + "folders": [], + "requests": [ + { + "v": "2", + "endpoint": "https://echo.hoppscotch.io", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v2-II", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] + } + ], "requests": [ { "v": "2", - "endpoint": "https://httpbin.org/get", + "endpoint": "https://echo.hoppscotch.io", "headers": [ { "key": "Inactive-Header", @@ -33,7 +88,7 @@ "name": "req-v2", "method": "GET", "preRequestScript": "", - "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", "body": { "contentType": null, "body": null diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json index fcce134a8..916e809db 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json @@ -1,11 +1,66 @@ { "v": 2, "name": "coll-v2", - "folders": [], + "folders": [ + { + "v": 2, + "name": "coll-v2-child", + "folders": [], + "requests": [ + { + "v": "3", + "endpoint": "https://echo.hoppscotch.io", + "headers": [ + { + "key": "Inactive-Header", + "value": "Inactive Header", + "active": false + }, + { + "key": "Authorization", + "value": "Bearer token123", + "active": true + } + ], + "params": [ + { + "key": "key", + "value": "value", + "active": true + }, + { + "key": "inactive-key", + "value": "inactive-param", + "active": false + } + ], + "name": "req-v3-II", + "method": "GET", + "preRequestScript": "", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", + "body": { + "contentType": null, + "body": null + }, + "auth": { + "authType": "bearer", + "authActive": true, + "token": "token123" + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] + } + ], "requests": [ { "v": "3", - "endpoint": "https://httpbin.org/get", + "endpoint": "https://echo.hoppscotch.io", "headers": [ { "key": "Inactive-Header", @@ -33,7 +88,7 @@ "name": "req-v3", "method": "GET", "preRequestScript": "", - "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", + "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})", "body": { "contentType": null, "body": null diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json index 2cea26e85..a79d5a23e 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json @@ -5,8 +5,14 @@ "requests": [ { "v": "3", - "auth": { "authType": "none", "authActive": true }, - "body": { "body": null, "contentType": null }, + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, "name": "test-secret-headers", "method": "GET", "params": [], @@ -18,13 +24,16 @@ } ], "requestVariables": [], - "endpoint": "<>/headers", - "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", + "endpoint": "<>/headers", + "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", "preRequestScript": "const secretHeaderValueFromPreReqScript = pw.env.get(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" }, { "v": "3", - "auth": { "authType": "none", "authActive": true }, + "auth": { + "authType": "none", + "authActive": true + }, "body": { "body": "{\n \"secretBodyKey\": \"<>\"\n}", "contentType": "application/json" @@ -34,14 +43,20 @@ "params": [], "headers": [], "requestVariables": [], - "endpoint": "<>/post", - "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", + "endpoint": "<>/post", + "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(JSON.parse(pw.response.body.data).secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", "preRequestScript": "const secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)" }, { "v": "3", - "auth": { "authType": "none", "authActive": true }, - "body": { "body": null, "contentType": null }, + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, "name": "test-secret-query-params", "method": "GET", "params": [ @@ -53,7 +68,7 @@ ], "headers": [], "requestVariables": [], - "endpoint": "<>/get", + "endpoint": "<>", "testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})", "preRequestScript": "const secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)" }, @@ -65,14 +80,17 @@ "username": "<>", "authActive": true }, - "body": { "body": null, "contentType": null }, + "body": { + "body": null, + "contentType": null + }, "name": "test-secret-basic-auth", "method": "GET", "params": [], "headers": [], "requestVariables": [], - "endpoint": "<>/basic-auth/<>/<>", - "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", + "endpoint": "<>/basic-auth/<>/<>", + "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n // The endpoint at times results in a `502` bad gateway\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", "preRequestScript": "" }, { @@ -84,30 +102,42 @@ "username": "testuser", "authActive": true }, - "body": { "body": null, "contentType": null }, + "body": { + "body": null, + "contentType": null + }, "name": "test-secret-bearer-auth", "method": "GET", "params": [], "headers": [], "requestVariables": [], - "endpoint": "<>/bearer", - "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", + "endpoint": "<>/bearer", + "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n // Safeguard to prevent test failures due to the endpoint\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", "preRequestScript": "const secretBearerToken = pw.env.get(\"secretBearerToken\")\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)" }, { "v": "3", - "auth": { "authType": "none", "authActive": true }, - "body": { "body": null, "contentType": null }, + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, "name": "test-secret-fallback", "method": "GET", "params": [], "headers": [], "requestVariables": [], - "endpoint": "<>", + "endpoint": "<>", "testScript": "pw.test(\"Returns an empty string if the value for a secret environment variable is not found in the system environment\", () => {\n pw.expect(pw.env.get(\"nonExistentValueInSystemEnv\")).toBe(\"\")\n})", "preRequestScript": "" } ], - "auth": { "authType": "inherit", "authActive": false }, + "auth": { + "authType": "inherit", + "authActive": false + }, "headers": [] -} +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json index 823eec801..1de038c5f 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json @@ -1,6 +1,6 @@ { "v": 2, - "name": "secret-envs-setters-coll", + "name": "secret-envs-persistence-coll", "folders": [], "requests": [ { @@ -24,8 +24,8 @@ "active": true } ], - "endpoint": "<>/headers", - "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", + "endpoint": "<>", + "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", "preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" }, { @@ -49,8 +49,8 @@ "active": true } ], - "endpoint": "<>/headers", - "testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})", + "endpoint": "<>", + "testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})", "preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value-overriden\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" }, { @@ -68,8 +68,8 @@ "params": [], "requestVariables": [], "headers": [], - "endpoint": "<>/post", - "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", + "endpoint": "<>/post", + "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(JSON.parse(pw.response.body.data).secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", "preRequestScript": "const secretBodyValue = pw.env.get(\"secretBodyValue\")\n\nif (!secretBodyValue) { \n pw.env.set(\"secretBodyValue\", \"secret-body-value\")\n}\n\nconst secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)" }, { @@ -93,7 +93,7 @@ ], "requestVariables": [], "headers": [], - "endpoint": "<>/get", + "endpoint": "<>", "testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})", "preRequestScript": "const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n\nif (!secretQueryParamValue) {\n pw.env.set(\"secretQueryParamValue\", \"secret-query-param-value\")\n}\n\nconst secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)" }, @@ -114,8 +114,8 @@ "params": [], "requestVariables": [], "headers": [], - "endpoint": "<>/basic-auth/<>/<>", - "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", + "endpoint": "<>/basic-auth/<>/<>", + "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n // The endpoint at times results in a `502` bad gateway\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", "preRequestScript": "let secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n\nlet secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\nif (!secretBasicAuthUsername) {\n pw.env.set(\"secretBasicAuthUsername\", \"test-user\")\n}\n\nif (!secretBasicAuthPassword) {\n pw.env.set(\"secretBasicAuthPassword\", \"test-pass\")\n}" }, { @@ -136,8 +136,8 @@ "params": [], "requestVariables": [], "headers": [], - "endpoint": "<>/bearer", - "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", + "endpoint": "<>/bearer", + "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n // Safeguard to prevent test failures due to the endpoint\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", "preRequestScript": "let secretBearerToken = pw.env.resolve(\"<>\")\n\nif (!secretBearerToken) {\n pw.env.set(\"secretBearerToken\", \"test-token\")\n secretBearerToken = pw.env.resolve(\"<>\")\n}\n\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)" } ], @@ -146,4 +146,4 @@ "authActive": false }, "headers": [] -} +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json index 61dc17cd2..6a103ed44 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json @@ -5,7 +5,7 @@ "requests": [ { "v": "3", - "endpoint": "https://httpbin.org/post", + "endpoint": "https://echo.hoppscotch.io/post", "name": "req", "params": [], "headers": [ @@ -18,7 +18,7 @@ "method": "POST", "auth": { "authType": "none", "authActive": true }, "preRequestScript": "pw.env.set(\"preReqVarOne\", \"pre-req-value-one\")\n\npw.env.set(\"preReqVarTwo\", \"pre-req-value-two\")\n\npw.env.set(\"customHeaderValueFromSecretVar\", \"custom-header-secret-value\")\n\npw.env.set(\"customBodyValue\", \"custom-body-value\")", - "testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.json.key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})", + "testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request script\", () => {\n pw.expect(pw.response.body.headers[\"custom-header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request script\", () => {\n pw.expect(JSON.parse(pw.response.body.data).key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})", "body": { "contentType": "application/json", "body": "{\n \"key\": \"<>\"\n}" diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json b/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json index 53d33bb35..266f821c8 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json @@ -32,7 +32,12 @@ "secret": true }, { - "key": "baseURL", + "key": "echoHoppBaseURL", + "value": "https://echo.hoppscotch.io", + "secret": false + }, + { + "key": "httpbinBaseURL", "value": "https://httpbin.org", "secret": false } diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json b/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json index fec73a293..88155f84e 100644 --- a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json +++ b/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json @@ -38,7 +38,12 @@ "secret": true }, { - "key": "baseURL", + "key": "echoHoppBaseURL", + "value": "https://echo.hoppscotch.io", + "secret": false + }, + { + "key": "httpbinBaseURL", "value": "https://httpbin.org", "secret": false } diff --git a/packages/hoppscotch-cli/src/__tests__/utils.ts b/packages/hoppscotch-cli/src/__tests__/utils.ts index f6068b2d6..40c4d70f0 100644 --- a/packages/hoppscotch-cli/src/__tests__/utils.ts +++ b/packages/hoppscotch-cli/src/__tests__/utils.ts @@ -3,15 +3,16 @@ import { resolve } from "path"; import { ExecResponse } from "./types"; -export const runCLI = (args: string, options = {}): Promise => - { - const CLI_PATH = resolve(__dirname, "../../bin/hopp"); - const command = `node ${CLI_PATH} ${args}` +export const runCLI = (args: string, options = {}): Promise => { + const CLI_PATH = resolve(__dirname, "../../bin/hopp.js"); + const command = `node ${CLI_PATH} ${args}`; - return new Promise((resolve) => - exec(command, options, (error, stdout, stderr) => resolve({ error, stdout, stderr })) - ); - } + return new Promise((resolve) => + exec(command, options, (error, stdout, stderr) => + resolve({ error, stdout, stderr }) + ) + ); +}; export const trimAnsi = (target: string) => { const ansiRegex = @@ -25,12 +26,18 @@ export const getErrorCode = (out: string) => { return ansiTrimmedStr.split(" ")[0]; }; -export const getTestJsonFilePath = (file: string, kind: "collection" | "environment") => { +export const getTestJsonFilePath = ( + file: string, + kind: "collection" | "environment" +) => { const kindDir = { collection: "collections", environment: "environments", }[kind]; - const filePath = resolve(__dirname, `../../src/__tests__/samples/${kindDir}/${file}`); + const filePath = resolve( + __dirname, + `../../src/__tests__/samples/${kindDir}/${file}` + ); return filePath; }; diff --git a/packages/hoppscotch-cli/src/index.ts b/packages/hoppscotch-cli/src/index.ts index bf81a7ab4..daa71995a 100644 --- a/packages/hoppscotch-cli/src/index.ts +++ b/packages/hoppscotch-cli/src/index.ts @@ -1,6 +1,7 @@ import chalk from "chalk"; import { Command } from "commander"; import * as E from "fp-ts/Either"; + import { version } from "../package.json"; import { test } from "./commands/test"; import { handleError } from "./handlers/error"; @@ -20,7 +21,7 @@ const CLI_AFTER_ALL_TXT = `\nFor more help, head on to ${accent( "https://docs.hoppscotch.io/documentation/clients/cli" )}`; -const program = new Command() +const program = new Command(); program .name("hopp") diff --git a/packages/hoppscotch-cli/src/utils/mutators.ts b/packages/hoppscotch-cli/src/utils/mutators.ts index 91b1383f4..516f720e2 100644 --- a/packages/hoppscotch-cli/src/utils/mutators.ts +++ b/packages/hoppscotch-cli/src/utils/mutators.ts @@ -7,6 +7,41 @@ import { error } from "../types/errors"; import { FormDataEntry } from "../types/request"; import { isHoppErrnoException } from "./checks"; +const getValidRequests = ( + collections: HoppCollection[], + collectionFilePath: string +) => { + return collections.map((collection) => { + // Validate requests using zod schema + const requestSchemaParsedResult = z + .array(entityReference(HoppRESTRequest)) + .safeParse(collection.requests); + + // Handle validation errors + if (!requestSchemaParsedResult.success) { + throw error({ + code: "MALFORMED_COLLECTION", + path: collectionFilePath, + data: "Please check the collection data.", + }); + } + + // Recursively validate requests in nested folders + if (collection.folders.length > 0) { + collection.folders = getValidRequests( + collection.folders, + collectionFilePath + ); + } + + // Return validated collection + return { + ...collection, + requests: requestSchemaParsedResult.data, + }; + }); +}; + /** * Parses array of FormDataEntry to FormData. * @param values Array of FormDataEntry. @@ -82,22 +117,5 @@ export async function parseCollectionData( }); } - return collectionSchemaParsedResult.data.map((collection) => { - const requestSchemaParsedResult = z - .array(entityReference(HoppRESTRequest)) - .safeParse(collection.requests); - - if (!requestSchemaParsedResult.success) { - throw error({ - code: "MALFORMED_COLLECTION", - path, - data: "Please check the collection data.", - }); - } - - return { - ...collection, - requests: requestSchemaParsedResult.data, - }; - }); + return getValidRequests(collectionSchemaParsedResult.data, path); } diff --git a/packages/hoppscotch-js-sandbox/jest.config.js b/packages/hoppscotch-js-sandbox/jest.config.js deleted file mode 100644 index 078d001b1..000000000 --- a/packages/hoppscotch-js-sandbox/jest.config.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - preset: "ts-jest", - testEnvironment: "jsdom", - collectCoverage: true, - setupFilesAfterEnv: ["./jest.setup.ts"], - moduleNameMapper: { - "~/(.*)": "/src/$1", - "^lodash-es$": "lodash", - }, -} diff --git a/packages/hoppscotch-js-sandbox/jest.setup.ts b/packages/hoppscotch-js-sandbox/jest.setup.ts deleted file mode 100644 index 9960425a6..000000000 --- a/packages/hoppscotch-js-sandbox/jest.setup.ts +++ /dev/null @@ -1 +0,0 @@ -require("@relmify/jest-fp-ts") diff --git a/packages/hoppscotch-js-sandbox/node.d.ts b/packages/hoppscotch-js-sandbox/node.d.ts index 70ca9c627..32567df82 100644 --- a/packages/hoppscotch-js-sandbox/node.d.ts +++ b/packages/hoppscotch-js-sandbox/node.d.ts @@ -1,2 +1,2 @@ -export { default } from "./dist/node.d.ts" -export * from "./dist/node.d.ts" +export { default } from "./dist/node/index.d.ts" +export * from "./dist/node/index.d.ts" diff --git a/packages/hoppscotch-js-sandbox/package.json b/packages/hoppscotch-js-sandbox/package.json index 3026e0efc..d38b67825 100644 --- a/packages/hoppscotch-js-sandbox/package.json +++ b/packages/hoppscotch-js-sandbox/package.json @@ -30,7 +30,7 @@ "scripts": { "lint": "eslint --ext .ts,.js --ignore-path .gitignore .", "lintfix": "eslint --fix --ext .ts,.js --ignore-path .gitignore .", - "test": "pnpm exec jest", + "test": "vitest run", "build": "vite build && tsc --emitDeclarationOnly", "clean": "pnpm tsc --build --clean", "postinstall": "pnpm run build", @@ -69,10 +69,18 @@ "eslint-config-prettier": "8.6.0", "eslint-plugin-prettier": "4.2.1", "io-ts": "2.2.16", - "jest": "27.5.1", "prettier": "2.8.4", "ts-jest": "27.1.5", "typescript": "4.9.5", - "vite": "5.0.5" + "vite": "5.0.5", + "vitest": "0.34.6" + }, + "peerDependencies": { + "isolated-vm": "4.7.2" + }, + "peerDependenciesMeta": { + "isolated-vm": { + "optional": true + } } } \ No newline at end of file diff --git a/packages/hoppscotch-js-sandbox/setupFiles.ts b/packages/hoppscotch-js-sandbox/setupFiles.ts new file mode 100644 index 000000000..11b1985d4 --- /dev/null +++ b/packages/hoppscotch-js-sandbox/setupFiles.ts @@ -0,0 +1,15 @@ +// Vitest doesn't work without globals +// Ref: https://github.com/relmify/jest-fp-ts/issues/11 + +import decodeMatchers from "@relmify/jest-fp-ts/dist/decodeMatchers" +import eitherMatchers from "@relmify/jest-fp-ts/dist/eitherMatchers" +import optionMatchers from "@relmify/jest-fp-ts/dist/optionMatchers" +import theseMatchers from "@relmify/jest-fp-ts/dist/theseMatchers" +import eitherOrTheseMatchers from "@relmify/jest-fp-ts/dist/eitherOrTheseMatchers" +import { expect } from "vitest" + +expect.extend(decodeMatchers.matchers) +expect.extend(eitherMatchers.matchers) +expect.extend(optionMatchers.matchers) +expect.extend(theseMatchers.matchers) +expect.extend(eitherOrTheseMatchers.matchers) diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/base64-helper-functions.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/base64-helper-functions.spec.ts similarity index 95% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/base64-helper-functions.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/base64-helper-functions.spec.ts index c1fc1e106..ff0cf21fc 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/base64-helper-functions.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/base64-helper-functions.spec.ts @@ -1,8 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runPreRequestScript } from "~/pre-request/node-vm" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runPreRequestScript, runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" describe("Base64 helper functions", () => { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/get.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/env/get.spec.ts similarity index 97% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/get.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/env/get.spec.ts index fb433eac8..ed2f78005 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/get.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/env/get.spec.ts @@ -1,8 +1,9 @@ -import "@relmify/jest-fp-ts" import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/getResolve.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/env/getResolve.spec.ts similarity index 98% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/getResolve.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/env/getResolve.spec.ts index 0d64e6d41..d1942ec58 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/getResolve.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/env/getResolve.spec.ts @@ -1,8 +1,9 @@ -import "@relmify/jest-fp-ts" import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/resolve.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/env/resolve.spec.ts similarity index 97% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/resolve.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/env/resolve.spec.ts index 022d7083a..d691a9fd6 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/resolve.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/env/resolve.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/set.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/env/set.spec.ts similarity index 97% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/set.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/env/set.spec.ts index d3726d249..4b35aa02b 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/set.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/env/set.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/unset.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/env/unset.spec.ts similarity index 98% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/unset.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/env/unset.spec.ts index eb6ae5783..be165d873 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/envs/unset.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/env/unset.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse, TestResult } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBe.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBe.spec.ts similarity index 95% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBe.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/expect/toBe.spec.ts index 0d3dd19ac..079fc2198 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBe.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBe.spec.ts @@ -1,8 +1,9 @@ -import "@relmify/jest-fp-ts" import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { @@ -23,7 +24,7 @@ describe("toBe", () => { return expect( func( ` - pw.expect(2).toBe(2) + pw.expect(2).toBe(2) `, fakeResponse )() diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeLevelxxx.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeLevelxxx.spec.ts similarity index 99% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeLevelxxx.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeLevelxxx.spec.ts index dede20810..8a8eae8f2 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeLevelxxx.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeLevelxxx.spec.ts @@ -1,8 +1,9 @@ -import "@relmify/jest-fp-ts" import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeType.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeType.spec.ts similarity index 98% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeType.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeType.spec.ts index 6ab9a9b90..a18600553 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toBeType.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toBeType.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toHaveLength.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toHaveLength.spec.ts similarity index 98% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toHaveLength.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/expect/toHaveLength.spec.ts index 970fe374d..b05a0a499 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toHaveLength.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toHaveLength.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toInclude.spec.ts similarity index 98% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/expect/toInclude.spec.ts index 132120d92..7b68fe062 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/expect/toInclude.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/preRequest.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/pre-request.spec.ts similarity index 95% rename from packages/hoppscotch-js-sandbox/src/__tests__/preRequest.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/pre-request.spec.ts index ce8bf9555..c94a440bd 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/preRequest.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/pre-request.spec.ts @@ -1,6 +1,6 @@ -import "@relmify/jest-fp-ts" +import { describe, expect, test } from "vitest" -import { runPreRequestScript } from "~/pre-request/node-vm" +import { runPreRequestScript } from "~/node" describe("runPreRequestScript", () => { test("returns the updated environment properly", () => { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/utils.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/shared-utils.spec.ts similarity index 78% rename from packages/hoppscotch-js-sandbox/src/__tests__/utils.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/shared-utils.spec.ts index a9871a1b1..f016b891f 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/utils.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/shared-utils.spec.ts @@ -1,4 +1,6 @@ -import { preventCyclicObjects } from "~/utils" +import { preventCyclicObjects } from "~/shared-utils" + +import { describe, expect, test } from "vitest" describe("preventCyclicObjects", () => { test("succeeds with a simple object", () => { diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/test-runner.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/test-runner.spec.ts similarity index 95% rename from packages/hoppscotch-js-sandbox/src/__tests__/testing/test-runner.spec.ts rename to packages/hoppscotch-js-sandbox/src/__tests__/test-runner.spec.ts index 214ee7d6d..293e83d23 100644 --- a/packages/hoppscotch-js-sandbox/src/__tests__/testing/test-runner.spec.ts +++ b/packages/hoppscotch-js-sandbox/src/__tests__/test-runner.spec.ts @@ -1,7 +1,9 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" -import { runTestScript } from "~/test-runner/node-vm" +import { describe, expect, test } from "vitest" + +import { runTestScript } from "~/node" import { TestResponse } from "~/types" const fakeResponse: TestResponse = { diff --git a/packages/hoppscotch-js-sandbox/src/node.ts b/packages/hoppscotch-js-sandbox/src/node.ts deleted file mode 100644 index c5d403251..000000000 --- a/packages/hoppscotch-js-sandbox/src/node.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./pre-request/node-vm" -export * from "./test-runner/node-vm" diff --git a/packages/hoppscotch-js-sandbox/src/node/index.ts b/packages/hoppscotch-js-sandbox/src/node/index.ts new file mode 100644 index 000000000..a329c048b --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/node/index.ts @@ -0,0 +1,2 @@ +export { runPreRequestScript } from "./pre-request" +export { runTestScript } from "./test-runner" diff --git a/packages/hoppscotch-js-sandbox/src/node/pre-request.ts b/packages/hoppscotch-js-sandbox/src/node/pre-request.ts new file mode 100644 index 000000000..ae60c9d76 --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/node/pre-request.ts @@ -0,0 +1,91 @@ +import { pipe } from "fp-ts/function" +import * as TE from "fp-ts/lib/TaskEither" +import { createRequire } from "module" + +import type ivmT from "isolated-vm" + +import { TestResult } from "~/types" +import { getPreRequestScriptMethods } from "~/shared-utils" +import { getSerializedAPIMethods } from "./utils" + +const nodeRequire = createRequire(import.meta.url) +const ivm = nodeRequire("isolated-vm") + +export const runPreRequestScript = ( + preRequestScript: string, + envs: TestResult["envs"] +): TE.TaskEither => + pipe( + TE.tryCatch( + async () => { + const isolate: ivmT.Isolate = new ivm.Isolate() + const context = await isolate.createContext() + return { isolate, context } + }, + (reason) => `Context initialization failed: ${reason}` + ), + TE.chain(({ isolate, context }) => + pipe( + TE.tryCatch( + async () => { + const jail = context.global + + const { pw, updatedEnvs } = getPreRequestScriptMethods(envs) + + const serializedAPIMethods = getSerializedAPIMethods(pw) + jail.setSync("serializedAPIMethods", serializedAPIMethods, { + copy: true, + }) + + jail.setSync("atob", atob) + jail.setSync("btoa", btoa) + + // Methods in the isolate context can't be invoked straightaway + const finalScript = ` + const pw = new Proxy(serializedAPIMethods, { + get: (pwObjTarget, pwObjProp) => { + const topLevelEntry = pwObjTarget[pwObjProp] + + // "pw.env" set of API methods + if (topLevelEntry && typeof topLevelEntry === "object") { + return new Proxy(topLevelEntry, { + get: (subTarget, subProp) => { + const subLevelProperty = subTarget[subProp] + if (subLevelProperty && subLevelProperty.typeof === "function") { + return (...args) => subLevelProperty.applySync(null, args) + } + }, + }) + } + } + }) + + ${preRequestScript} + ` + + // Create a script and compile it + const script = await isolate.compileScript(finalScript) + + // Run the pre-request script in the provided context + await script.run(context) + + return updatedEnvs + }, + (reason) => reason + ), + TE.fold( + (error) => TE.left(`Script execution failed: ${error}`), + (result) => + pipe( + TE.tryCatch( + async () => { + await isolate.dispose() + return result + }, + (disposeError) => `Isolate disposal failed: ${disposeError}` + ) + ) + ) + ) + ) + ) diff --git a/packages/hoppscotch-js-sandbox/src/node/test-runner.ts b/packages/hoppscotch-js-sandbox/src/node/test-runner.ts new file mode 100644 index 000000000..14bf0c3e2 --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/node/test-runner.ts @@ -0,0 +1,217 @@ +import * as E from "fp-ts/Either" +import * as TE from "fp-ts/TaskEither" +import { pipe } from "fp-ts/function" +import { createRequire } from "module" + +import type ivmT from "isolated-vm" + +import { TestResponse, TestResult } from "~/types" +import { + getTestRunnerScriptMethods, + preventCyclicObjects, +} from "~/shared-utils" +import { getSerializedAPIMethods } from "./utils" + +const nodeRequire = createRequire(import.meta.url) +const ivm = nodeRequire("isolated-vm") + +export const runTestScript = ( + testScript: string, + envs: TestResult["envs"], + response: TestResponse +): TE.TaskEither => + pipe( + TE.tryCatch( + async () => { + const isolate: ivmT.Isolate = new ivm.Isolate() + const context = await isolate.createContext() + return { isolate, context } + }, + (reason) => `Context initialization failed: ${reason}` + ), + TE.chain(({ isolate, context }) => + pipe( + TE.tryCatch( + async () => + executeScriptInContext( + testScript, + envs, + response, + isolate, + context + ), + (reason) => `Script execution failed: ${reason}` + ), + TE.chain((result) => + TE.tryCatch( + async () => { + await isolate.dispose() + return result + }, + (disposeReason) => `Isolate disposal failed: ${disposeReason}` + ) + ) + ) + ) + ) +const executeScriptInContext = ( + testScript: string, + envs: TestResult["envs"], + response: TestResponse, + isolate: ivmT.Isolate, + context: ivmT.Context +): Promise => { + return new Promise((resolve, reject) => { + // Parse response object + const responseObjHandle = preventCyclicObjects(response) + if (E.isLeft(responseObjHandle)) { + return reject(`Response parsing failed: ${responseObjHandle.left}`) + } + + const jail = context.global + + const { pw, testRunStack, updatedEnvs } = getTestRunnerScriptMethods(envs) + + const serializedAPIMethods = getSerializedAPIMethods({ + ...pw, + response: responseObjHandle.right, + }) + jail.setSync("serializedAPIMethods", serializedAPIMethods, { copy: true }) + + jail.setSync("atob", atob) + jail.setSync("btoa", btoa) + + jail.setSync("ivm", ivm) + + // Methods in the isolate context can't be invoked straightaway + const finalScript = ` + const pw = new Proxy(serializedAPIMethods, { + get: (pwObj, pwObjProp) => { + // pw.expect(), pw.env, etc. + const topLevelEntry = pwObj[pwObjProp] + + // If the entry exists and is a function + // pw.expect(), pw.test(), etc. + if (topLevelEntry && topLevelEntry.typeof === "function") { + // pw.test() just involves invoking the function via "applySync()" + if (pwObjProp === "test") { + return (...args) => topLevelEntry.applySync(null, args) + } + + // pw.expect() returns an object with matcher methods + return (...args) => { + // Invoke "pw.expect()" and get access to the object with matcher methods + const expectFnResult = topLevelEntry.applySync( + null, + args.map((expectVal) => { + if (typeof expectVal === "object") { + if (expectVal === null) { + return null + } + + // Only arrays and objects stringified here should be parsed from the "pw.expect()" method definition + // The usecase is that any JSON string supplied should be preserved + // An extra "isStringifiedWithinIsolate" prop is added to indicate it has to be parsed + + if (Array.isArray(expectVal)) { + return JSON.stringify({ + arr: expectVal, + isStringifiedWithinIsolate: true, + }) + } + + return JSON.stringify({ + ...expectVal, + isStringifiedWithinIsolate: true, + }) + } + + return expectVal + }) + ) + + // Matcher methods that can be chained with "pw.expect()" + // pw.expect().toBe(), etc + if (expectFnResult.typeof === "object") { + // Access the getter that points to the negated matcher methods via "{ accessors: true }" + const matcherMethods = { + not: expectFnResult.getSync("not", { accessors: true }), + } + + // Serialize matcher methods for use in the isolate context + const matcherMethodNames = [ + "toBe", + "toBeLevel2xx", + "toBeLevel3xx", + "toBeLevel4xx", + "toBeLevel5xx", + "toBeType", + "toHaveLength", + "toInclude", + ] + matcherMethodNames.forEach((methodName) => { + matcherMethods[methodName] = expectFnResult.getSync(methodName) + }) + + return new Proxy(matcherMethods, { + get: (matcherMethodTarget, matcherMethodProp) => { + // pw.expect().not.toBe(), etc + const matcherMethodEntry = matcherMethodTarget[matcherMethodProp] + + if (matcherMethodProp === "not") { + return new Proxy(matcherMethodEntry, { + get: (negatedObjTarget, negatedObjprop) => { + // Return the negated matcher method defn that is invoked from the test script + const negatedMatcherMethodDefn = negatedObjTarget.getSync(negatedObjprop) + return negatedMatcherMethodDefn + }, + }) + } + + // Return the matcher method defn that is invoked from the test script + return matcherMethodEntry + }, + }) + } + } + } + + // "pw.env" set of API methods + if (typeof topLevelEntry === "object" && pwObjProp !== "response") { + return new Proxy(topLevelEntry, { + get: (subTarget, subProp) => { + const subLevelProperty = subTarget[subProp] + if ( + subLevelProperty && + subLevelProperty.typeof === "function" + ) { + return (...args) => subLevelProperty.applySync(null, args) + } + }, + }) + } + + return topLevelEntry + }, + }) + + ${testScript} + ` + + // Create a script and compile it + const script = isolate.compileScript(finalScript) + + // Run the test script in the provided context + script + .then((script) => script.run(context)) + .then(() => { + resolve({ + tests: testRunStack, + envs: updatedEnvs, + }) + }) + .catch((error: Error) => { + reject(error) + }) + }) +} diff --git a/packages/hoppscotch-js-sandbox/src/node/utils.ts b/packages/hoppscotch-js-sandbox/src/node/utils.ts new file mode 100644 index 000000000..7e44e4d54 --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/node/utils.ts @@ -0,0 +1,23 @@ +import { createRequire } from "module" + +const nodeRequire = createRequire(import.meta.url) +const ivm = nodeRequire("isolated-vm") + +// Helper function to recursively wrap methods in `ivm.Reference` +export const getSerializedAPIMethods = ( + namespaceObj: Record +): Record => { + const result: Record = {} + + for (const [key, value] of Object.entries(namespaceObj)) { + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + result[key] = getSerializedAPIMethods(value as Record) + } else if (typeof value === "function") { + result[key] = new ivm.Reference(value) + } else { + result[key] = value + } + } + + return result +} diff --git a/packages/hoppscotch-js-sandbox/src/pre-request/node-vm/index.ts b/packages/hoppscotch-js-sandbox/src/pre-request/node-vm/index.ts deleted file mode 100644 index 220a18d77..000000000 --- a/packages/hoppscotch-js-sandbox/src/pre-request/node-vm/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { pipe } from "fp-ts/function" -import * as TE from "fp-ts/lib/TaskEither" -import { createContext, runInContext } from "vm" - -import { TestResult } from "~/types" -import { getPreRequestScriptMethods } from "~/utils" - -export const runPreRequestScript = ( - preRequestScript: string, - envs: TestResult["envs"] -): TE.TaskEither => - pipe( - TE.tryCatch( - async () => { - return createContext() - }, - (reason) => `Context initialization failed: ${reason}` - ), - TE.chain((context) => - TE.tryCatch( - () => - new Promise((resolve) => { - const { pw, updatedEnvs } = getPreRequestScriptMethods(envs) - - // Expose pw to the context - context.pw = pw - context.atob = atob - context.btoa = btoa - - // Run the pre-request script in the provided context - runInContext(preRequestScript, context) - - resolve(updatedEnvs) - }), - (reason) => `Script execution failed: ${reason}` - ) - ) - ) diff --git a/packages/hoppscotch-js-sandbox/src/utils.ts b/packages/hoppscotch-js-sandbox/src/shared-utils.ts similarity index 85% rename from packages/hoppscotch-js-sandbox/src/utils.ts rename to packages/hoppscotch-js-sandbox/src/shared-utils.ts index a6d298c23..d8908f16d 100644 --- a/packages/hoppscotch-js-sandbox/src/utils.ts +++ b/packages/hoppscotch-js-sandbox/src/shared-utils.ts @@ -182,6 +182,36 @@ const getSharedMethods = (envs: TestResult["envs"]) => { } } +const getResolvedExpectValue = (expectVal: any) => { + if (typeof expectVal !== "string") { + return expectVal + } + + try { + const parsedExpectVal = JSON.parse(expectVal) + + // Supplying non-primitive values is not permitted in the `isStringifiedWithinIsolate` property indicates that the object was stringified before executing the script from the isolate context + // This is done to ensure a JSON string supplied as the "expectVal" is not parsed and preserved as is + if (typeof parsedExpectVal === "object") { + if (parsedExpectVal.isStringifiedWithinIsolate !== true) { + return expectVal + } + + // For an array, the contents are stored in the `arr` property + if (Array.isArray(parsedExpectVal.arr)) { + return parsedExpectVal.arr + } + + delete parsedExpectVal.isStringifiedWithinIsolate + return parsedExpectVal + } + + return expectVal + } catch (_) { + return expectVal + } +} + export function preventCyclicObjects( obj: Record ): E.Left | E.Right> { @@ -215,15 +245,18 @@ export const createExpectation = ( ) => { const result: Record = {} + // Non-primitive values supplied are stringified in the isolate context + const resolvedExpectVal = getResolvedExpectValue(expectVal) + const toBeFn = (expectedVal: any) => { - let assertion = expectVal === expectedVal + let assertion = resolvedExpectVal === expectedVal if (negated) { assertion = !assertion } const status = assertion ? "pass" : "fail" - const message = `Expected '${expectVal}' to${ + const message = `Expected '${resolvedExpectVal}' to${ negated ? " not" : "" } be '${expectedVal}'` @@ -240,7 +273,7 @@ export const createExpectation = ( rangeStart: number, rangeEnd: number ) => { - const parsedExpectVal = parseInt(expectVal) + const parsedExpectVal = parseInt(resolvedExpectVal) if (!Number.isNaN(parsedExpectVal)) { let assertion = @@ -260,7 +293,7 @@ export const createExpectation = ( message, }) } else { - const message = `Expected ${level}-level status but could not parse value '${expectVal}'` + const message = `Expected ${level}-level status but could not parse value '${resolvedExpectVal}'` currTestStack[currTestStack.length - 1].expectResults.push({ status: "error", message, @@ -288,14 +321,14 @@ export const createExpectation = ( "function", ].includes(expectedType) ) { - let assertion = typeof expectVal === expectedType + let assertion = typeof resolvedExpectVal === expectedType if (negated) { assertion = !assertion } const status = assertion ? "pass" : "fail" - const message = `Expected '${expectVal}' to${ + const message = `Expected '${resolvedExpectVal}' to${ negated ? " not" : "" } be type '${expectedType}'` @@ -316,7 +349,12 @@ export const createExpectation = ( } const toHaveLengthFn = (expectedLength: any) => { - if (!(Array.isArray(expectVal) || typeof expectVal === "string")) { + if ( + !( + Array.isArray(resolvedExpectVal) || + typeof resolvedExpectVal === "string" + ) + ) { const message = "Expected toHaveLength to be called for an array or string" currTestStack[currTestStack.length - 1].expectResults.push({ @@ -328,7 +366,7 @@ export const createExpectation = ( } if (typeof expectedLength === "number" && !Number.isNaN(expectedLength)) { - let assertion = expectVal.length === expectedLength + let assertion = resolvedExpectVal.length === expectedLength if (negated) { assertion = !assertion @@ -355,7 +393,12 @@ export const createExpectation = ( } const toIncludeFn = (needle: any) => { - if (!(Array.isArray(expectVal) || typeof expectVal === "string")) { + if ( + !( + Array.isArray(resolvedExpectVal) || + typeof resolvedExpectVal === "string" + ) + ) { const message = "Expected toInclude to be called for an array or string" currTestStack[currTestStack.length - 1].expectResults.push({ status: "error", @@ -382,13 +425,13 @@ export const createExpectation = ( return undefined } - let assertion = expectVal.includes(needle) + let assertion = resolvedExpectVal.includes(needle) if (negated) { assertion = !assertion } - const expectValPretty = JSON.stringify(expectVal) + const expectValPretty = JSON.stringify(resolvedExpectVal) const needlePretty = JSON.stringify(needle) const status = assertion ? "pass" : "fail" const message = `Expected ${expectValPretty} to${ diff --git a/packages/hoppscotch-js-sandbox/src/test-runner/node-vm/index.ts b/packages/hoppscotch-js-sandbox/src/test-runner/node-vm/index.ts deleted file mode 100644 index 89d5a8f68..000000000 --- a/packages/hoppscotch-js-sandbox/src/test-runner/node-vm/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as E from "fp-ts/Either" -import * as TE from "fp-ts/TaskEither" -import { pipe } from "fp-ts/function" -import { createContext, runInContext } from "vm" - -import { TestResponse, TestResult } from "~/types" -import { getTestRunnerScriptMethods, preventCyclicObjects } from "~/utils" - -export const runTestScript = ( - testScript: string, - envs: TestResult["envs"], - response: TestResponse -): TE.TaskEither => - pipe( - TE.tryCatch( - async () => { - return createContext() - }, - (reason) => `Context initialization failed: ${reason}` - ), - TE.chain((context) => - TE.tryCatch( - () => executeScriptInContext(testScript, envs, response, context), - (reason) => `Script execution failed: ${reason}` - ) - ) - ) - -const executeScriptInContext = ( - testScript: string, - envs: TestResult["envs"], - response: TestResponse, - context: any -): Promise => { - return new Promise((resolve, reject) => { - // Parse response object - const responseObjHandle = preventCyclicObjects(response) - if (E.isLeft(responseObjHandle)) { - return reject(`Response parsing failed: ${responseObjHandle.left}`) - } - - const { pw, testRunStack, updatedEnvs } = getTestRunnerScriptMethods(envs) - - // Expose pw to the context - context.pw = { ...pw, response: responseObjHandle.right } - context.atob = atob - context.btoa = btoa - - // Run the test script in the provided context - runInContext(testScript, context) - - resolve({ - tests: testRunStack, - envs: updatedEnvs, - }) - }) -} diff --git a/packages/hoppscotch-js-sandbox/src/web.ts b/packages/hoppscotch-js-sandbox/src/web.ts deleted file mode 100644 index 5077e9311..000000000 --- a/packages/hoppscotch-js-sandbox/src/web.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./pre-request/web-worker" -export * from "./test-runner/web-worker" diff --git a/packages/hoppscotch-js-sandbox/src/web/index.ts b/packages/hoppscotch-js-sandbox/src/web/index.ts new file mode 100644 index 000000000..a329c048b --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/web/index.ts @@ -0,0 +1,2 @@ +export { runPreRequestScript } from "./pre-request" +export { runTestScript } from "./test-runner" diff --git a/packages/hoppscotch-js-sandbox/src/pre-request/web-worker/index.ts b/packages/hoppscotch-js-sandbox/src/web/pre-request/index.ts similarity index 100% rename from packages/hoppscotch-js-sandbox/src/pre-request/web-worker/index.ts rename to packages/hoppscotch-js-sandbox/src/web/pre-request/index.ts diff --git a/packages/hoppscotch-js-sandbox/src/pre-request/web-worker/worker.ts b/packages/hoppscotch-js-sandbox/src/web/pre-request/worker.ts similarity index 93% rename from packages/hoppscotch-js-sandbox/src/pre-request/web-worker/worker.ts rename to packages/hoppscotch-js-sandbox/src/web/pre-request/worker.ts index 0825f3831..694973e11 100644 --- a/packages/hoppscotch-js-sandbox/src/pre-request/web-worker/worker.ts +++ b/packages/hoppscotch-js-sandbox/src/web/pre-request/worker.ts @@ -1,7 +1,7 @@ import * as TE from "fp-ts/TaskEither" import { TestResult } from "~/types" -import { getPreRequestScriptMethods } from "~/utils" +import { getPreRequestScriptMethods } from "~/shared-utils" const executeScriptInContext = ( preRequestScript: string, diff --git a/packages/hoppscotch-js-sandbox/src/test-runner/web-worker/index.ts b/packages/hoppscotch-js-sandbox/src/web/test-runner/index.ts similarity index 100% rename from packages/hoppscotch-js-sandbox/src/test-runner/web-worker/index.ts rename to packages/hoppscotch-js-sandbox/src/web/test-runner/index.ts diff --git a/packages/hoppscotch-js-sandbox/src/test-runner/web-worker/worker.ts b/packages/hoppscotch-js-sandbox/src/web/test-runner/worker.ts similarity index 93% rename from packages/hoppscotch-js-sandbox/src/test-runner/web-worker/worker.ts rename to packages/hoppscotch-js-sandbox/src/web/test-runner/worker.ts index c6b9374a2..0f759c9c9 100644 --- a/packages/hoppscotch-js-sandbox/src/test-runner/web-worker/worker.ts +++ b/packages/hoppscotch-js-sandbox/src/web/test-runner/worker.ts @@ -2,7 +2,10 @@ import * as E from "fp-ts/Either" import * as TE from "fp-ts/TaskEither" import { SandboxTestResult, TestResponse, TestResult } from "~/types" -import { getTestRunnerScriptMethods, preventCyclicObjects } from "~/utils" +import { + getTestRunnerScriptMethods, + preventCyclicObjects, +} from "~/shared-utils" const executeScriptInContext = ( testScript: string, diff --git a/packages/hoppscotch-js-sandbox/vite.config.ts b/packages/hoppscotch-js-sandbox/vite.config.ts index aa01475b0..c47ddd4c7 100644 --- a/packages/hoppscotch-js-sandbox/vite.config.ts +++ b/packages/hoppscotch-js-sandbox/vite.config.ts @@ -7,16 +7,20 @@ export default defineConfig({ emptyOutDir: true, lib: { entry: { - web: "./src/web.ts", - node: "./src/node.ts", + web: "./src/web/index.ts", + node: "./src/node/index.ts", }, name: "js-sandbox", formats: ["es", "cjs"], }, rollupOptions: { - external: ["vm"], + external: ["module"], }, }, + test: { + environment: "node", + setupFiles: ["./setupFiles.ts"], + }, resolve: { alias: { "~": resolve(__dirname, "./src"), diff --git a/packages/hoppscotch-js-sandbox/web.d.ts b/packages/hoppscotch-js-sandbox/web.d.ts index 2ab2266dc..27b45c844 100644 --- a/packages/hoppscotch-js-sandbox/web.d.ts +++ b/packages/hoppscotch-js-sandbox/web.d.ts @@ -1,2 +1,2 @@ -export { default } from "./dist/web.d.ts" -export * from "./dist/web.d.ts" +export { default } from "./dist/web/index.d.ts" +export * from "./dist/web/index.d.ts" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 541e980da..16a955e86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -309,6 +309,9 @@ importers: commander: specifier: 11.1.0 version: 11.1.0 + isolated-vm: + specifier: 4.7.2 + version: 4.7.2 lodash-es: specifier: 4.17.21 version: 4.17.21 @@ -828,6 +831,9 @@ importers: fp-ts: specifier: 2.12.1 version: 2.12.1 + isolated-vm: + specifier: 4.7.2 + version: 4.7.2 lodash: specifier: 4.17.21 version: 4.17.21 @@ -868,9 +874,6 @@ importers: io-ts: specifier: 2.2.16 version: 2.2.16(fp-ts@2.12.1) - jest: - specifier: 27.5.1 - version: 27.5.1 prettier: specifier: 2.8.4 version: 2.8.4 @@ -883,6 +886,9 @@ importers: vite: specifier: 5.0.5 version: 5.0.5(@types/node@17.0.45)(terser@5.27.0) + vitest: + specifier: 0.34.6 + version: 0.34.6(sass@1.69.5)(terser@5.27.0) packages/hoppscotch-selfhost-desktop: dependencies: @@ -3061,7 +3067,7 @@ packages: peerDependencies: vue: 3.3.9 dependencies: - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) /@codemirror/autocomplete@6.13.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.25.1)(@lezer/common@1.2.1): resolution: {integrity: sha512-SuDrho1klTINfbcMPnyro1ZxU9xJtwDMtb62R8TjL/tOl71IoOsvBo1a9x+hDvHhIzkTcJHy2VC+rmpGgYkRSw==} @@ -6120,7 +6126,7 @@ packages: peerDependencies: vue: 3.3.9 dependencies: - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} @@ -8396,7 +8402,7 @@ packages: /@types/graceful-fs@4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 18.18.8 + '@types/node': 17.0.45 dev: true /@types/har-format@1.2.15: @@ -9621,7 +9627,7 @@ packages: regenerator-runtime: 0.13.11 systemjs: 6.14.2 terser: 5.27.0 - vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.27.0) + vite: 3.2.4(@types/node@17.0.27)(terser@5.27.0) /@vitejs/plugin-legacy@2.3.0(terser@5.27.0)(vite@4.5.0): resolution: {integrity: sha512-Bh62i0gzQvvT8AeAAb78nOnqSYXypkRmQmOTImdPZ39meHR9e2une3AIFmVo4s1SDmcmJ6qj18Sa/lRc/14KaA==} @@ -10143,7 +10149,7 @@ packages: '@types/web-bluetooth': 0.0.14 '@vueuse/metadata': 8.7.5 '@vueuse/shared': 8.7.5(vue@3.3.9) - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) vue-demi: 0.14.6(vue@3.3.9) /@vueuse/core@9.12.0(vue@3.3.9): @@ -10202,7 +10208,7 @@ packages: vue: optional: true dependencies: - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) vue-demi: 0.14.6(vue@3.3.9) /@vueuse/shared@9.12.0(vue@3.3.9): @@ -11120,7 +11126,6 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.0 - dev: true /blob@0.0.5: resolution: {integrity: sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==} @@ -11257,7 +11262,6 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -11519,6 +11523,10 @@ packages: optionalDependencies: fsevents: 2.3.3 + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -12233,6 +12241,13 @@ packages: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true @@ -12627,7 +12642,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /engine.io-client@3.5.3: resolution: {integrity: sha512-qsgyc/CEhJ6cgMUwxRRtOndGVhIu5hpL5tR4umSpmX/MvkFoIxUTM7oFMDQumHNzlNLwSVy6qhstFPoWTf7dOw==} @@ -13991,6 +14005,11 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + /expect@27.5.1: resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -14409,6 +14428,10 @@ packages: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: false + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -14592,6 +14615,10 @@ packages: through2: 4.0.2 dev: true + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -15969,6 +15996,14 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /isolated-vm@4.7.2: + resolution: {integrity: sha512-JVEs5gzWObzZK5+OlBplCdYSpokMcdhLSs/xWYYxmYWVfOOFF4oZJsYh7E/FmfX8e7gMioXMpMMeEyX1afuKrg==} + engines: {node: '>=16.0.0'} + requiresBuild: true + dependencies: + prebuild-install: 7.1.2 + dev: false + /isomorphic-fetch@3.0.0: resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} dependencies: @@ -16954,7 +16989,7 @@ packages: jest-util: 27.5.1 natural-compare: 1.4.0 pretty-format: 27.5.1 - semver: 7.5.4 + semver: 7.6.0 transitivePeerDependencies: - supports-color dev: true @@ -17099,7 +17134,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.18.8 + '@types/node': 17.0.45 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -18004,6 +18039,11 @@ packages: engines: {node: '>=12'} dev: true + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -18446,6 +18486,10 @@ packages: - encoding dev: false + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -18548,6 +18592,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: false + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true @@ -18582,6 +18630,13 @@ packages: lower-case: 2.0.2 tslib: 2.6.2 + /node-abi@3.57.0: + resolution: {integrity: sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.0 + dev: false + /node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} @@ -19457,6 +19512,25 @@ packages: punycode: 2.3.0 dev: false + /prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.1 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.6 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.57.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -19819,7 +19893,6 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true /punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -20577,7 +20650,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -20747,6 +20819,18 @@ packages: resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==} dev: true + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -21467,6 +21551,26 @@ packages: engines: {node: '>=6'} dev: true + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false + /tar@6.1.13: resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} engines: {node: '>=10'} @@ -22137,6 +22241,12 @@ packages: typescript: 4.9.5 dev: true + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -23031,7 +23141,7 @@ packages: '@types/eslint': 8.56.2 eslint: 8.57.0 rollup: 2.79.1 - vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.27.0) + vite: 3.2.4(@types/node@17.0.27)(terser@5.27.0) /vite-plugin-eslint@1.8.1(eslint@8.57.0)(vite@4.5.0): resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} @@ -23325,6 +23435,40 @@ packages: - supports-color dev: true + /vite@3.2.4(@types/node@17.0.27)(terser@5.27.0): + resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 17.0.27 + esbuild: 0.15.18 + postcss: 8.4.32 + resolve: 1.22.8 + rollup: 2.79.1 + terser: 5.27.0 + optionalDependencies: + fsevents: 2.3.3 + /vite@3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.27.0): resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -23746,7 +23890,7 @@ packages: '@vue/composition-api': optional: true dependencies: - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) /vue-eslint-parser@9.3.1(eslint@8.47.0): resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} @@ -23978,7 +24122,7 @@ packages: vue: 3.3.9 dependencies: sortablejs: 1.14.0 - vue: 3.3.9(typescript@5.3.2) + vue: 3.3.9(typescript@4.9.5) /w3c-hr-time@1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} @@ -24462,6 +24606,7 @@ packages: /workbox-google-analytics@6.6.0: resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 6.6.0 workbox-core: 6.6.0 @@ -24471,6 +24616,7 @@ packages: /workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 7.0.0 workbox-core: 7.0.0