Repository: rohinivsenthil/postcode Branch: master Commit: 824ff7d0c2d0 Files: 97 Total size: 97.6 KB Directory structure: gitextract_11wa3ryc/ ├── .eslintrc.json ├── .gitignore ├── .husky/ │ └── pre-push ├── .prettierignore ├── .vscode/ │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── .vscodeignore ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src/ │ ├── extension.ts │ └── test/ │ ├── runTest.ts │ └── suite/ │ ├── extension.test.ts │ └── index.ts ├── tsconfig.json ├── webpack.config.js └── webview/ ├── App.css ├── App.tsx ├── acquireVsCodeApi.d.ts ├── components/ │ ├── RequestBar/ │ │ ├── index.tsx │ │ └── styles.css │ ├── RequestOptionsBar/ │ │ ├── index.tsx │ │ └── styles.css │ ├── RequestOptionsWindow/ │ │ ├── index.tsx │ │ └── styles.css │ └── Response/ │ ├── index.tsx │ └── styles.css ├── constants/ │ ├── request-options.ts │ ├── response-options.ts │ ├── response-views.ts │ └── supported-langs.ts ├── features/ │ ├── codeGen/ │ │ ├── CodeSnippet/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── codeGenSlice.ts │ ├── requestAuth/ │ │ ├── BasicAuth/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── BearerToken/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── NoAuth/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── RequestAuth/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestAuthSlice.ts │ ├── requestBody/ │ │ ├── Binary/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── FormData/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── GraphQL/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── NoBody/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── Raw/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── RequestBody/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── UrlEncoded/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestBodySlice.ts │ ├── requestHeader/ │ │ ├── HeadersWindow/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestHeaderSlice.ts │ ├── requestMethod/ │ │ ├── RequestMethodSelector/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestMethodSlice.ts │ ├── requestOptions/ │ │ ├── RequestOptions/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestOptionsSlice.ts │ ├── requestUrl/ │ │ ├── RequestQueryParams/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ ├── RequestUrl/ │ │ │ ├── index.tsx │ │ │ └── styles.css │ │ └── requestUrlSlice.ts │ └── response/ │ ├── ResponseBody/ │ │ ├── index.tsx │ │ └── styles.css │ ├── ResponseHeaders/ │ │ ├── index.tsx │ │ └── styles.css │ ├── ResponseTab/ │ │ ├── index.tsx │ │ └── styles.css │ ├── ResponseWindow/ │ │ ├── index.tsx │ │ └── styles.css │ └── responseSlice.ts ├── index.css ├── index.tsx ├── pages/ │ └── Postcode/ │ ├── index.tsx │ └── styles.css ├── prerender.tsx ├── react-app-env.d.ts ├── redux/ │ ├── hooks.ts │ └── store.ts ├── shared/ │ ├── Editor/ │ │ ├── index.tsx │ │ └── styles.css │ └── KeyValueTable/ │ ├── index.tsx │ └── styles.css └── vscode.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "env": { "browser": true, "es2021": true, "node": true }, "extends": [ "eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": 12, "sourceType": "module" }, "settings": { "react": { "version": "detect" } }, "ignorePatterns": ["out", "dist", "**/*.d.ts"], "plugins": ["react", "@typescript-eslint"], "rules": { "@typescript-eslint/explicit-module-boundary-types": "off" } } ================================================ FILE: .gitignore ================================================ out dist node_modules .vscode-test/ *.vsix ================================================ FILE: .husky/pre-push ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" yarn lint ================================================ FILE: .prettierignore ================================================ out dist ================================================ FILE: .vscode/launch.json ================================================ // A launch configuration that compiles the extension and then opens it inside a new window // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { "version": "0.2.0", "configurations": [ { "name": "Run Extension", "type": "extensionHost", "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "outFiles": ["${workspaceFolder}/dist/**/*.js"], "preLaunchTask": "${defaultBuildTask}" }, { "name": "Extension Tests", "type": "extensionHost", "request": "launch", "args": [ "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" ], "outFiles": ["${workspaceFolder}/out/test/**/*.js"], "preLaunchTask": "npm: test-watch" } ] } ================================================ FILE: .vscode/settings.json ================================================ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { "out": false, // set this to true to hide the "out" folder with the compiled JS files "dist": false // set this to true to hide the "dist" folder with the compiled JS files }, "search.exclude": { "out": true, // set this to false to include "out" folder in search results "dist": true // set this to false to include "dist" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off" } ================================================ FILE: .vscode/tasks.json ================================================ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { "version": "2.0.0", "tasks": [ { "type": "npm", "script": "watch", "problemMatcher": ["$ts-webpack-watch", "$tslint-webpack-watch"], "isBackground": true, "presentation": { "reveal": "never" }, "group": { "kind": "build", "isDefault": true } }, { "type": "npm", "script": "test-watch", "problemMatcher": "$tsc-watch", "isBackground": true, "presentation": { "reveal": "never" }, "group": "build" } ] } ================================================ FILE: .vscodeignore ================================================ .vscode/** .vscode-test/** out/** node_modules/** src/** .gitignore .yarnrc vsc-extension-quickstart.md **/tsconfig.json **/.eslintrc.json **/*.map **/*.ts dist/prerender.js dist/*.prerender.js dist/*.prerender.js.* ================================================ FILE: .yarnrc ================================================ --ignore-engines true ================================================ FILE: CHANGELOG.md ================================================ # Change Log All notable changes to the "postcode" extension will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. ### 1.3.9 - automattically use http protocol if none is specified - make font size relative to vscode editor font size ### 1.3.8 - display response time in response window ### 1.3.7 - add request options for strict ssl ### 1.3.6 - add beautify button for post body ### 1.3.5 - fix basic auth functionality ### 1.3.4 - Add headers tab for response ### 1.3.3 - Improve load time by pre-rendering ### 1.3.2 - Remove excess scroll ### 1.3.1 - Add copy button to code editor ### 1.3.0 - Support code-snippet generation ### 1.2.1 - Clear error when response is updated ### 1.2.0 - Support GraphQL requests ### 1.1.0 - Support formating of responses based on language ### 1.0.0 - Initial release ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Rohini Senthil Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Postcode

API client for VS code 📦


Postcode is a [Visual Studio Code](https://code.visualstudio.com/) [extension](https://marketplace.visualstudio.com/VSCode) that can be used to create and test simple and complex HTTP/s requests, as well as view responses. You can find the extension available [here](https://marketplace.visualstudio.com/items?itemName=rohinivsenthil.postcode).

Release: 1.3.3
## Highlighted Features - **Intuitive UI/UX** similar to Postman fitting seamlessly with any VSCode theme - Supports **GraphQL** requests - Supports **code snippet generation** from requests ## Quick start **Step 1.** Install the Postcode extension for Visual Studio Code **Step 2.** Click on the Postcode icon in the side panel OR run the following command **Postcode: Create Request** **Step 3** Create or test your HTTP/s requests and hit Send to see the responses ## Commands | Command | Description | | ------------------------ | ---------------------------------------------------- | | Postcode: Create Request | Opens a new Postcode tab to create and test requests | ## Issues, feature requests, and contributions ### Issues - If you come across a problem with the extension, please file an [issue](https://github.com/rohinivsenthil/postcode/issues/new) - For list of known issues, please check the [issues tab](https://github.com/rohinivsenthil/postcode/issues/new) ### Feature requests - Find planned features for future releases marked as [feature](https://github.com/rohinivsenthil/postcode/issues?q=is%3Aissue+is%3Aopen+label%3Afeature) under issues tab. - For new feature requests, please file an [issue](https://github.com/rohinivsenthil/postcode/issues/new) ### Contributions Contributions are always welcome! #### Running the extension locally for development 1. Clone the repository and install dependencies by running `yarn install` 2. Press `F5` to open a new window with your extension loaded. 3. Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Postcode: Create Request`. #### Folder structure - **`package.json`** - this is the manifest file in which you declare your extension and command. The plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. - **`webview`**: folder where you will find entire React code - **`src/extension.ts`**: this is the main file where you will provide the implementation of your command. The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. We pass the function containing the implementation of the command as the second parameter to `registerCommand`. #### Making changes - You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. - You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. ## Related - Read the [launch blog](https://rohinivsenthil.medium.com/postcode-vs-code-extension-alternative-to-postman-384816d4cf07) post on Medium - Featured #11 Product of the day on [Product Hunt](https://www.producthunt.com/posts/postcode) - Featured in **Trending this week** on Visual Studio Code Marketplace ================================================ FILE: package.json ================================================ { "name": "postcode", "publisher": "rohinivsenthil", "displayName": "Postcode", "icon": "icons/icon.png", "description": "An API client to test and create HTTP/s requests", "version": "1.3.9", "license": "MIT", "bugs": { "url": "https://github.com/rohinivsenthil/postcode/issues" }, "author": { "name": "Rohini Senthil", "email": "rohinivsenthil@gmail.com" }, "repository": { "type": "git", "url": "https://github.com/rohinivsenthil/postcode" }, "engines": { "vscode": "^1.56.0" }, "categories": [ "Programming Languages" ], "keywords": [ "api-client", "postman", "REST", "graphql", "http" ], "activationEvents": [ "onCommand:postcode.createRequest" ], "main": "./dist/extension.js", "contributes": { "commands": [ { "command": "postcode.createRequest", "title": "Postcode: Create Request" } ], "viewsContainers": { "activitybar": [ { "id": "postcode", "title": "Postcode", "icon": "webview/icons/package.svg" } ] }, "views": { "postcode": [ { "id": "postcode.request", "name": "Request" } ] }, "viewsWelcome": [ { "view": "postcode.request", "contents": "[Create Request](command:postcode.createRequest)" } ] }, "scripts": { "vscode:prepublish": "yarn run package", "compile": "cross-env NODE_ENV=development webpack --progress", "watch": "cross-env NODE_ENV=development webpack --progress --watch", "package": "cross-env NODE_ENV=production webpack --progress", "test-compile": "tsc -p ./", "test-watch": "tsc -watch -p ./", "pretest": "yarn run test-compile && yarn run lint", "lint": "eslint src webview --ext .ts,.tsx", "lint:fix": "eslint --fix src webview --ext .ts,.tsx", "test": "node ./out/test/runTest.js", "prepare": "husky install" }, "devDependencies": { "@svgr/webpack": "^5.5.0", "@types/glob": "^7.1.3", "@types/mocha": "^8.0.4", "@types/node": "^12.11.7", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.4", "@types/react-redux": "^7.1.16", "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^4.14.1", "@typescript-eslint/parser": "^4.14.1", "cross-env": "^7.0.3", "css-loader": "^5.2.4", "eslint": "^7.19.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-react": "^7.23.2", "file-loader": "^6.2.0", "glob": "^7.1.6", "husky": "^7.0.1", "mini-css-extract-plugin": "^1.6.0", "mocha": "^8.2.1", "monaco-editor-webpack-plugin": "^3.1.0", "prettier": "2.3.0", "static-site-generator-webpack-plugin": "^3.4.2", "style-loader": "^2.0.0", "ts-loader": "^8.0.14", "typescript": "^4.1.3", "url-loader": "^4.1.1", "vscode-test": "^1.5.0", "webpack": "^5.19.0", "webpack-cli": "^4.4.0" }, "dependencies": { "@reduxjs/toolkit": "^1.5.1", "axios": "^0.21.1", "buffer": "^6.0.3", "monaco-editor": "^0.24.0", "path-browserify": "^1.0.1", "postman-code-generators": "^1.1.5", "postman-collection": "^3.6.11", "react": "^17.0.2", "react-dom": "^17.0.2", "react-icons": "^4.2.0", "react-redux": "^7.2.4", "url": "^0.11.0" } } ================================================ FILE: src/extension.ts ================================================ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from "vscode"; import * as fs from "fs"; import axios from "axios"; import * as https from "https"; import { RequestOptions } from "../webview/features/requestOptions/requestOptionsSlice"; // this method is called when your extension is activated // your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { const webviewContent = fs .readFileSync( vscode.Uri.joinPath(context.extensionUri, "dist/index.html").fsPath, { encoding: "utf-8" } ) .replace( "styleUri", vscode.Uri.joinPath(context.extensionUri, "/dist/main.css") .with({ scheme: "vscode-resource" }) .toString() ) .replace( "scriptUri", vscode.Uri.joinPath(context.extensionUri, "/dist/webview.js") .with({ scheme: "vscode-resource" }) .toString() ); // The command has been defined in the package.json file // Now provide the implementation of the command with registerCommand // The commandId parameter must match the command field in package.json const disposable = vscode.commands.registerCommand( "postcode.createRequest", () => { // The code you place here will be executed every time your command is executed // Display a message box to the user vscode.window.showInformationMessage("Welcome to Postcode!"); const panel = vscode.window.createWebviewPanel( "postcode", "Postcode", vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: true, localResourceRoots: [ vscode.Uri.joinPath(context.extensionUri, "dist"), ], } ); panel.webview.html = webviewContent; panel.iconPath = vscode.Uri.joinPath( context.extensionUri, "icons/icon.png" ); panel.webview.onDidReceiveMessage( ({ method, url, headers, body, auth, options }) => { // Options Section const requestOptions = options as RequestOptions; let requestStartedAt, responseDuration; if (!url) { panel.webview.postMessage({ type: "response", error: { message: "Request URL is empty" }, }); vscode.window.showInformationMessage("Request URL is empty"); return; } const headersObj = {}; if (auth.type === "bearer") { headersObj["Authorization"] = `Bearer ${auth.bearer.token}`; } headers.forEach(({ key, value, disabled }) => { if (!disabled) { headersObj[key] = value; } }); let data = ""; if (body.mode === "formdata") { const dataObj = new URLSearchParams(); body.formdata.forEach(({ key, value, disabled }) => { if (!disabled) { dataObj.append(key, value); } }); data = dataObj.toString(); headersObj["Content-Type"] = "multipart/form-data"; } else if (body.mode === "urlencoded") { const dataObj = new URLSearchParams(); body.urlencoded.forEach(({ key, value, disabled }) => { if (!disabled) { dataObj.append(key, value); } }); data = dataObj.toString(); headersObj["Content-Type"] = "application/x-www-form-urlencoded"; } else if (body.mode === "raw") { data = body.raw; headersObj["Content-Type"] = { json: "application/json", html: "text/html", xml: "text/xml", text: "text/plain", }[body.options.raw.language]; } else if (body.mode === "file") { data = body.fileData; headersObj["Content-Type"] = "application/octet-stream"; } else if (body.mode === "graphql") { data = JSON.stringify({ query: body.graphql.query, variables: body.graphql.variables, }); headersObj["Content-Type"] = "application/json"; } // Option 1. StrictSSL https.globalAgent.options.rejectUnauthorized = requestOptions.strictSSL === "yes"; axios.interceptors.request.use((config) => { requestStartedAt = new Date().getTime(); return config; }); axios.interceptors.response.use((config) => { responseDuration = new Date().getTime() - requestStartedAt; return config; }); axios({ method, url, baseURL: "", data: data, headers: headersObj, auth: auth.type === "basic" ? auth.basic : undefined, transformResponse: [(data) => data], responseType: "text", validateStatus: () => true, }) .then((resp) => panel.webview.postMessage({ type: "response", data: resp.data, status: resp.status, statusText: resp.statusText, headers: resp.headers, duration: responseDuration, }) ) .catch((err) => { panel.webview.postMessage({ type: "response", error: err, }); vscode.window.showInformationMessage( "Error: Could not send request" ); }); } ); } ); context.subscriptions.push(disposable); } // this method is called when your extension is deactivated // eslint-disable-next-line @typescript-eslint/no-empty-function export function deactivate() {} ================================================ FILE: src/test/runTest.ts ================================================ import * as path from "path"; import { runTests } from "vscode-test"; async function main() { try { // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, "../../"); // The path to test runner // Passed to --extensionTestsPath const extensionTestsPath = path.resolve(__dirname, "./suite/index"); // Download VS Code, unzip it and run the integration test await runTests({ extensionDevelopmentPath, extensionTestsPath }); } catch (err) { console.error("Failed to run tests"); process.exit(1); } } main(); ================================================ FILE: src/test/suite/extension.test.ts ================================================ import * as assert from "assert"; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from "vscode"; // import * as myExtension from '../../extension'; suite("Extension Test Suite", () => { vscode.window.showInformationMessage("Start all tests."); test("Sample test", () => { assert.strictEqual(-1, [1, 2, 3].indexOf(5)); assert.strictEqual(-1, [1, 2, 3].indexOf(0)); }); }); ================================================ FILE: src/test/suite/index.ts ================================================ import * as path from "path"; import * as Mocha from "mocha"; import * as glob from "glob"; export function run(): Promise { // Create the mocha test const mocha = new Mocha({ ui: "tdd", color: true, }); const testsRoot = path.resolve(__dirname, ".."); return new Promise((c, e) => { glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { if (err) { return e(err); } // Add files to the test suite files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); try { // Run the mocha test mocha.run((failures) => { if (failures > 0) { e(new Error(`${failures} tests failed.`)); } else { c(); } }); } catch (err) { console.error(err); e(err); } }); }); } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es6", "outDir": "out", "jsx": "react", "lib": ["es2019", "dom"], "sourceMap": true, "rootDir": ".", "resolveJsonModule": true, "strict": false /* enable all strict type-checking options */ /* Additional Checks */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ }, "exclude": ["node_modules", ".vscode-test"] } ================================================ FILE: webpack.config.js ================================================ /* eslint-disable @typescript-eslint/no-var-requires */ //@ts-check "use strict"; const path = require("path"); const webpack = require("webpack"); const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const StaticSiteGeneratorPlugin = require("static-site-generator-webpack-plugin"); const imageInlineSizeLimit = parseInt( process.env.IMAGE_INLINE_SIZE_LIMIT || "10000" ); const baseConfig = (webpackEnv) => { const isEnvDevelopment = webpackEnv === "development"; const isEnvProduction = webpackEnv === "production"; return { mode: isEnvProduction ? "production" : isEnvDevelopment && "development", bail: isEnvProduction, devtool: isEnvProduction ? "source-map" : isEnvDevelopment && "eval-cheap-module-source-map", resolve: { fallback: { buffer: require.resolve("buffer"), path: require.resolve("path-browserify"), url: require.resolve("url"), }, extensions: [".ts", ".tsx", ".js"], }, module: { rules: [ { oneOf: [ { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], loader: require.resolve("url-loader"), options: { limit: imageInlineSizeLimit, name: "static/media/[name].[hash:8].[ext]", }, }, { test: /\.svg$/, use: [ require.resolve("@svgr/webpack"), require.resolve("url-loader"), ], }, { test: /\.tsx?$/, exclude: /node_modules/, loader: require.resolve("ts-loader"), }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: require.resolve("css-loader"), options: { importLoaders: 1, sourceMap: isEnvProduction || isEnvDevelopment, }, }, ], sideEffects: true, }, { loader: require.resolve("file-loader"), exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], options: { name: "media/[name].[hash:8].[ext]", }, }, ], }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: "ignore.css", }), ], }; }; const extensionConfig = (webpackEnv) => { return { ...baseConfig(webpackEnv), target: "node", entry: "./src/extension.ts", output: { path: path.resolve(__dirname, "dist"), filename: "extension.js", libraryTarget: "commonjs2", }, externals: { vscode: "commonjs vscode" }, }; }; const webviewConfig = (webpackEnv) => { return { ...baseConfig(webpackEnv), entry: "./webview/index.tsx", output: { path: path.resolve(__dirname, "dist"), filename: "webview.js", }, plugins: [ new MiniCssExtractPlugin(), new MonacoWebpackPlugin({ languages: [ "csharp", "dart", "go", "graphql", "html", "java", "typescript", "json", "objective-c", "php", "powershell", "python", "ruby", "shell", "swift", "xml", ], }), new webpack.ProvidePlugin({ Buffer: ["buffer", "Buffer"], process: "process/browser", }), ], }; }; const prerenderConfig = (webpackEnv) => { const config = baseConfig(webpackEnv); return { ...config, target: "node", entry: "./webview/prerender.tsx", output: { path: path.resolve(__dirname, "dist"), filename: "prerender.js", libraryTarget: "commonjs2", }, plugins: [ new MiniCssExtractPlugin({ filename: "ignore.css", }), new StaticSiteGeneratorPlugin({ paths: ["/"], }), new webpack.ProvidePlugin({ Buffer: ["buffer", "Buffer"], process: "process/browser", }), ], }; }; module.exports = [extensionConfig, webviewConfig, prerenderConfig]; ================================================ FILE: webview/App.css ================================================ .App { background-color: var(--background); } ================================================ FILE: webview/App.tsx ================================================ import * as React from "react"; import "./App.css"; import { responseUpdated } from "./features/response/responseSlice"; import { Postcode } from "./pages/Postcode"; import { useAppDispatch } from "./redux/hooks"; const App = () => { const dispatch = useAppDispatch(); React.useEffect(() => { window.addEventListener("message", (event) => { if (event.data.type === "response") { dispatch(responseUpdated(event.data)); } }); }, []); return (
); }; export default App; ================================================ FILE: webview/acquireVsCodeApi.d.ts ================================================ declare var acquireVsCodeApi: any; ================================================ FILE: webview/components/RequestBar/index.tsx ================================================ import * as React from "react"; import { Url } from "postman-collection"; import vscode from "../../vscode"; import { RequestMethodSelector } from "../../features/requestMethod/RequestMethodSelector"; import { RequestUrl } from "../../features/requestUrl/RequestUrl"; import { responseLoadingStarted } from "../../features/response/responseSlice"; import { selectRequestAuth } from "../../features/requestAuth/requestAuthSlice"; import { selectRequestBody } from "../../features/requestBody/requestBodySlice"; import { selectRequestHeaders } from "../../features/requestHeader/requestHeaderSlice"; import { selectRequestUrl } from "../../features/requestUrl/requestUrlSlice"; import { selectRequestMethod } from "../../features/requestMethod/requestMethodSlice"; import { selectRequestOptions } from "../../features/requestOptions/requestOptionsSlice"; import { useAppDispatch, useAppSelector } from "../../redux/hooks"; import "./styles.css"; export const RequestBar = () => { const dispatch = useAppDispatch(); const requestMethod = useAppSelector(selectRequestMethod); const requestHeaders = useAppSelector(selectRequestHeaders); const requestBody = useAppSelector(selectRequestBody); const requestUrl = useAppSelector(selectRequestUrl); const requestAuth = useAppSelector(selectRequestAuth); const requestOptions = useAppSelector(selectRequestOptions); return (
{ dispatch(responseLoadingStarted()); const { protocol } = Url.parse(requestUrl); vscode.postMessage({ method: requestMethod, auth: requestAuth, body: requestBody, headers: requestHeaders, url: protocol ? requestUrl : `http://${requestUrl}`, options: requestOptions, }); e.preventDefault(); }} > ); }; ================================================ FILE: webview/components/RequestBar/styles.css ================================================ .request-bar { display: flex; padding: 0 20px; padding-top: 30px; } .input-request-url { flex: 2; padding: 13px 20px; color: var(--default-text); display: inline-block; outline: none; border: 0; border-radius: 0 2px 2px 0; background-color: var(--input-field); font-size: var(--default-font-size); } .input-request-url:focus { outline: none; } .input-request-url:hover { background-color: var(--input-field-hover); } .button-request-send { background-color: var(--send-button); color: #fff; font-weight: bold; padding: 13px 20px; margin: 0 0 0 12px; display: inline-block; outline: none; border-radius: 5%; border: 0; cursor: pointer; font-size: var(--default-font-size); } .button-request-send:hover { background-color: var(--send-button-hover); } ================================================ FILE: webview/components/RequestOptionsBar/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import * as propTypes from "prop-types"; import { requestOptions } from "../../constants/request-options"; import { useAppSelector } from "../../redux/hooks"; import { selectRequestHeaders } from "../../features/requestHeader/requestHeaderSlice"; export const RequestOptionsTab = (props) => { const { selected, setSelected } = props; const header = useAppSelector(selectRequestHeaders); return (
{requestOptions.map((option) => ( ))}
); }; RequestOptionsTab.propTypes = { selected: propTypes.string.isRequired, setSelected: propTypes.func.isRequired, }; ================================================ FILE: webview/components/RequestOptionsBar/styles.css ================================================ .request-options-tab-wrapper { display: flex; justify-content: space-between; padding: 15px 20px 0 20px; } .request-options { display: flex; } .request-option { padding: 0px 5px 10px 5px; margin: 0 10px 0 0; display: flex; cursor: pointer; background-color: transparent; color: var(--default-text-light); outline: none; border: 3px solid transparent; border-radius: 0; font-weight: 500; font-size: var(--default-font-size); } .request-option:hover { color: var(--default-text); } .request-option-selected { color: var(--default-text); border-bottom: 3px solid var(--primary); } .request-options-header-length { margin-left: 4px; color: var(--tab-info); } .button-request { display: inline-block; cursor: pointer; background-color: transparent; color: var(--primary); outline: none; border: 3px solid transparent; border-radius: 0; font-weight: 500; font-size: var(--default-font-size); } .button-request:hover { color: var(--primary-dark); } .hidden { display: none; } ================================================ FILE: webview/components/RequestOptionsWindow/index.tsx ================================================ import * as React from "react"; import { RequestQueryParams } from "../../features/requestUrl/RequestQueryParams"; import { RequestAuth } from "../../features/requestAuth/RequestAuth"; import { Body } from "../../features/requestBody/RequestBody"; import { Headers } from "../../features/requestHeader/HeadersWindow"; import { CodeSnippet } from "../../features/codeGen/CodeSnippet"; import { RequestOptions } from "../../features/requestOptions/RequestOptions"; import * as propTypes from "prop-types"; import "./styles.css"; export const RequestOptionsWindow = (props) => { const { selected } = props; return (
{selected === "params" ? ( ) : selected === "authorization" ? ( ) : selected === "body" ? ( ) : selected === "headers" ? ( ) : selected === "code" ? ( ) : selected === "options" ? ( ) : null}
); }; RequestOptionsWindow.propTypes = { selected: propTypes.string.isRequired, }; ================================================ FILE: webview/components/RequestOptionsWindow/styles.css ================================================ .request-options-window-wrapper { flex: 1; padding: 5px 20px 10px 20px; overflow: scroll; } ================================================ FILE: webview/components/Response/index.tsx ================================================ import * as React from "react"; import { ResponseTab } from "../../features/response/ResponseTab"; import { ResponseWindow } from "../../features/response/ResponseWindow"; import { ReactComponent as PackageIcon } from "../../icons/package.svg"; import "./styles.css"; import { useAppSelector } from "../../redux/hooks"; import { selectResponse } from "../../features/response/responseSlice"; import { supportedLangs } from "../../constants/supported-langs"; export const Response = () => { const response = useAppSelector(selectResponse); const [selected, setSelected] = React.useState("body"); const [language, setLanguage] = React.useState(supportedLangs[0].value); if (response.loading) { return (
Sending request ...
); } else if (response.initial) { return (
Hit Send to get a response
); } else if (response.error) { return (
Could not send request
{`Error: ${response.error.message}`}
); } else { return (
); } }; ================================================ FILE: webview/components/Response/styles.css ================================================ .response-body-wrapper { display: flex; flex-direction: column; flex: 1; font-size: var(--default-font-size); border-top: var(--default-border-size) solid var(--border); } .error-response-wrapper { display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: var(--default-font-size); border-top: var(--default-border-size) solid var(--border); } .img-error-response { margin: 15px; fill: var(--logo-color); width: 20%; height: 20%; } .error-message { margin: 10px; color: var(--error-message); } .initial-response-wrapper { border-top: var(--default-border-size) solid var(--border); display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--default-text-light); font-size: var(--default-font-size); flex: 1; } .img-initial-response { fill: var(--logo-color); width: 20%; height: 20%; } .initial-text { padding: 10px; } .loader-wrapper { border-top: var(--default-border-size) solid var(--border); display: flex; flex: 1; flex-direction: column; justify-content: center; align-items: center; font-size: var(--default-font-size); } .loader, .loader:before, .loader:after { border-radius: 50%; width: 20px; height: 20px; -webkit-animation-fill-mode: both; animation-fill-mode: both; -webkit-animation: load7 1.8s infinite ease-in-out; animation: load7 1.8s infinite ease-in-out; } .loader { color: var(--border); position: relative; text-indent: -9999em; -webkit-transform: translateZ(0); -ms-transform: translateZ(0); transform: translateZ(0); -webkit-animation-delay: -0.16s; animation-delay: -0.16s; } .loader:before, .loader:after { content: ""; position: absolute; top: 0; } .loader:before { left: -3.5em; -webkit-animation-delay: -0.32s; animation-delay: -0.32s; } .loader:after { left: 3.5em; } @-webkit-keyframes load7 { 0%, 80%, 100% { box-shadow: 0 2.5em 0 -1.3em; } 40% { box-shadow: 0 2.5em 0 0; } } @keyframes load7 { 0%, 80%, 100% { box-shadow: 0 2.5em 0 -1.3em; } 40% { box-shadow: 0 2.5em 0 0; } } ================================================ FILE: webview/constants/request-options.ts ================================================ export const requestOptions = [ { name: "Params", value: "params", }, { name: "Authorization", value: "authorization", }, { name: "Headers", value: "headers", }, { name: "Body", value: "body", }, { name: "Options", value: "options", }, ]; ================================================ FILE: webview/constants/response-options.ts ================================================ export const responseOptions = [ { name: "Body", value: "body", }, { name: "Headers", value: "headers", }, ]; ================================================ FILE: webview/constants/response-views.ts ================================================ export const responseViews = [ { name: "Pretty", value: "pretty", }, { name: "Raw", value: "raw", }, ]; ================================================ FILE: webview/constants/supported-langs.ts ================================================ export const supportedLangs = [ { name: "JSON", value: "json", }, { name: "HTML", value: "html", }, { name: "XML", value: "xml", }, { name: "Text", value: "text", }, ]; ================================================ FILE: webview/features/codeGen/CodeSnippet/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { codeGenLanguageUpdated, codeGenOptions, codeGenVariantUpdated, selectCodeGenEditorLanguage, selectCodeGenLanguage, selectCodeGenLanguageKey, selectCodeGenVariant, selectRequest, } from "../codeGenSlice"; import * as codegen from "postman-code-generators"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; const Editor = React.lazy(() => import("../../../shared/Editor")); export const CodeSnippet = () => { const [code, setCode] = React.useState(""); const language = useAppSelector(selectCodeGenLanguage); const variant = useAppSelector(selectCodeGenVariant); const editorLanguage = useAppSelector(selectCodeGenEditorLanguage); const languageKey = useAppSelector(selectCodeGenLanguageKey); const dispatch = useAppDispatch(); const request = useAppSelector(selectRequest); React.useEffect(() => { codegen.convert(languageKey, variant, request, {}, (err, snippet) => { setCode(snippet); }); }, [languageKey, variant, request]); return (
loading
}>
); }; ================================================ FILE: webview/features/codeGen/CodeSnippet/styles.css ================================================ .code-gen-wrapper { height: 100%; display: flex; flex-direction: column; } .code-gen-options { display: flex; align-items: center; } .code-display { height: 95%; border-top: var(--default-border-size) solid var(--border); margin-top: 15px; } .code-gen-editor { height: 95%; } .select-code-option { padding: 4px; margin-right: 8px; display: inline-block; font-weight: 600; outline: none; border: var(--default-border-size) solid var(--border); border-radius: 2px; background-color: var(--background); color: var(--default-text-light); cursor: pointer; font-size: var(--default-font-size); } .select-code-option:hover { color: var(--default-text); } .select-code-option:focus { outline: none; } ================================================ FILE: webview/features/codeGen/codeGenSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; import { Request } from "postman-collection"; export const codeGenOptions = [ { language: "C", variants: ["libcurl"], editor: "c", key: "c" }, { language: "C#", variants: ["RestSharp"], editor: "csharp", key: "csharp" }, { language: "cURL", variants: ["cURL"], editor: "shell", key: "curl" }, { language: "Dart", variants: ["http"], editor: "dart", key: "dart" }, { language: "Go", variants: ["Native"], editor: "go", key: "go" }, { language: "HTTP", variants: ["HTTP"], editor: "text", key: "http" }, { language: "Java", variants: ["OkHttp", "Unirest"], editor: "java" }, { language: "JavaScript", variants: ["Fetch", "jQuery", "XHR"], editor: "javascript", key: "javascript", }, { language: "NodeJs", variants: ["Axios", "Native", "Request", "Unirest"], editor: "javascript", key: "nodejs", }, { language: "Objective-C", variants: ["NSURLSession"], editor: "objective-c", key: "objective-c", }, { language: "OCaml", variants: ["Cohttp"], editor: "text", key: "ocaml" }, { language: "PHP", variants: ["cURL", "pecl_http", "HTTP_Request2"], editor: "php", key: "php", }, { language: "PowerShell", variants: ["RestMethod"], editor: "powershell", key: "powershell", }, { language: "Python", variants: ["http.client", "Requests"], editor: "python", key: "python", }, { language: "Ruby", variants: ["Net:HTTP"], editor: "ruby", key: "ruby" }, { language: "Shell", variants: ["Httpie", "wget"], editor: "shell", key: "shell", }, { language: "Swift", variants: ["URLSession"], editor: "swift", key: "swift", }, ]; export interface codeGenOptionState { language: string; variant: string; } const initialState = { language: "C", variant: "libcurl" }; const codeGenSlice = createSlice({ name: "codeGenOptions", initialState, reducers: { codeGenLanguageUpdated(state, action: PayloadAction) { state.language = action.payload; state.variant = codeGenOptions.filter( ({ language }) => language === action.payload )[0].variants[0]; }, codeGenVariantUpdated(state, action: PayloadAction) { state.variant = action.payload; }, }, }); export const { codeGenLanguageUpdated, codeGenVariantUpdated } = codeGenSlice.actions; export const selectCodeGenLanguage = (state: RootState) => state.codeGenOptions.language; export const selectCodeGenVariant = (state: RootState) => state.codeGenOptions.variant; export const selectCodeGenEditorLanguage = (state: RootState) => codeGenOptions.filter( ({ language }) => language === state.codeGenOptions.language )[0].editor; export const selectCodeGenLanguageKey = (state: RootState) => codeGenOptions.filter( ({ language }) => language === state.codeGenOptions.language )[0].key; export const selectRequest = (state: RootState) => new Request({ method: state.requestMethod, url: state.requestUrl, header: state.requestHeader, body: { mode: state.requestBody.mode, options: state.requestBody.options, [state.requestBody.mode]: state.requestBody[state.requestBody.mode], }, auth: { type: state.requestAuth.type, [state.requestAuth.type]: state.requestAuth[state.requestAuth.type], }, }); export default codeGenSlice.reducer; ================================================ FILE: webview/features/requestAuth/BasicAuth/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestAuthOptionsUpdated, selectBasicAuthOptions, } from "../requestAuthSlice"; export const BasicAuth = () => { const basicAuthOptions = useAppSelector(selectBasicAuthOptions); const dispatch = useAppDispatch(); return (
Username
dispatch( requestAuthOptionsUpdated({ username: e.target.value, password: basicAuthOptions.password, }) ) } />
Password
dispatch( requestAuthOptionsUpdated({ username: basicAuthOptions.username, password: e.target.value, }) ) } />
); }; ================================================ FILE: webview/features/requestAuth/BasicAuth/styles.css ================================================ .basic-auth-input-group { padding: 15px 0 0 0; display: flex; align-items: center; justify-content: center; } .label-basic-auth { font-size: var(--default-font-size); color: var(--default-text); padding-right: 20px; } .input-basic-auth { width: 60%; padding: 13px 20px; color: var(--default-text); display: inline-block; outline: none; border: 0; border-radius: 0 2px 2px 0; background-color: var(--input-field); font-size: var(--default-font-size); } .input-basic-auth:focus { outline: none; } .input-basic-auth:hover { background-color: var(--input-field-hover); } ================================================ FILE: webview/features/requestAuth/BearerToken/index.tsx ================================================ import * as React from "react"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestAuthOptionsUpdated, selectBearerAuthOptions, } from "../requestAuthSlice"; import "./styles.css"; export const BearerToken = () => { const bearerAuthOptions = useAppSelector(selectBearerAuthOptions); const dispatch = useAppDispatch(); return (
Token
dispatch(requestAuthOptionsUpdated({ token: e.target.value })) } />
); }; ================================================ FILE: webview/features/requestAuth/BearerToken/styles.css ================================================ .bearer-token-wrapper { display: flex; justify-content: center; align-items: center; padding: 15px 0; } .label-bearer-token { font-size: var(--default-font-size); color: var(--default-text); padding-right: 20px; } .input-bearer-token { width: 60%; padding: 13px 20px; color: var(--default-text); display: inline-block; outline: none; border: 0; border-radius: 0 2px 2px 0; background-color: var(--input-field); font-size: var(--default-font-size); } .input-bearer-token:focus { outline: none; } .input-bearer-token:hover { background-color: var(--input-field-hover); } ================================================ FILE: webview/features/requestAuth/NoAuth/index.tsx ================================================ import * as React from "react"; import "./styles.css"; export const NoAuth = () => { return (
This request does not use any authorization.
); }; ================================================ FILE: webview/features/requestAuth/NoAuth/styles.css ================================================ .no-auth-wrapper { display: flex; justify-content: center; } ================================================ FILE: webview/features/requestAuth/RequestAuth/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { NoAuth } from "../NoAuth"; import { BearerToken } from "../BearerToken"; import { BasicAuth } from "../BasicAuth"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestAuthTypes, requestAuthTypeUpdated, selectRequestAuthType, } from "../requestAuthSlice"; export const RequestAuth = () => { const requestAuthType = useAppSelector(selectRequestAuthType); const dispatch = useAppDispatch(); return (
Authorization Type:
{requestAuthType === "noauth" ? ( ) : requestAuthType === "bearer" ? ( ) : requestAuthType === "basic" ? ( ) : null}
); }; ================================================ FILE: webview/features/requestAuth/RequestAuth/styles.css ================================================ .req-auth-wrapper { margin-top: 15px; } .auth-type { display: flex; align-items: center; } .label-auth-type { font-size: var(--default-font-size); color: var(--default-text); padding-right: 12px; } .select-auth-type { padding: 4px; display: inline-block; font-weight: 600; outline: none; border: var(--default-border-size) solid var(--border); border-radius: 2px; background-color: var(--background); color: var(--default-text-light); cursor: pointer; font-size: var(--default-font-size); } .select-auth-type:hover { color: var(--default-text); } .select-auth-type:focus { outline: none; } .req-auth-type-window { border-top: var(--default-border-size) solid var(--border); margin-top: 15px; } ================================================ FILE: webview/features/requestAuth/requestAuthSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; export const requestAuthTypes = [ { name: "No Auth", value: "noauth" }, { name: "Bearer Token", value: "bearer" }, { name: "Basic Auth", value: "basic" }, ]; export interface BearerAuthOptions { token: string; } export interface BasicAuthOptions { username: string; password: string; } export interface RequestAuthState { type: string; bearer: BearerAuthOptions; basic: BasicAuthOptions; } const initialState: RequestAuthState = { type: "noauth", bearer: { token: "" }, basic: { username: "", password: "" }, }; const requestAuthSlice = createSlice({ name: "requestAuth", initialState, reducers: { requestAuthTypeUpdated(state, action: PayloadAction) { state.type = action.payload; }, requestAuthOptionsUpdated( state, action: PayloadAction ) { state[state.type] = action.payload; }, }, }); export const { requestAuthTypeUpdated, requestAuthOptionsUpdated } = requestAuthSlice.actions; export const selectRequestAuth = (state: RootState) => state.requestAuth; export const selectRequestAuthType = (state: RootState) => state.requestAuth.type; export const selectBearerAuthOptions = (state: RootState) => state.requestAuth.bearer; export const selectBasicAuthOptions = (state: RootState) => state.requestAuth.basic; export default requestAuthSlice.reducer; ================================================ FILE: webview/features/requestBody/Binary/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestBodyBinaryUpdated, selectRequestBodyFile, } from "../requestBodySlice"; export const Binary = () => { const binary = useAppSelector(selectRequestBodyFile); const dispatch = useAppDispatch(); return (
{ if (e.target.files.length) { e.target.files[0].text().then((data) => dispatch( requestBodyBinaryUpdated({ name: e.target.files[0].name, data, }) ) ); } }} >
{binary}
); }; ================================================ FILE: webview/features/requestBody/Binary/styles.css ================================================ .binary-wrapper { border-top: var(--default-border-size) solid var(--border); display: flex; margin-top: 15px; color: var(--default-text-light); font-size: var(--default-font-size); } .input-file-wrapper { margin-top: 20px; display: flex; align-items: center; } .input-file-upload { display: none; } .label-file-upload { padding: 6px; cursor: pointer; background-color: var(--input-field); margin-right: 10px; } ================================================ FILE: webview/features/requestBody/FormData/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { KeyValueTable } from "../../../shared/KeyValueTable"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { selectRequestBodyFormData, requestBodyFormDataItemAdded, requestBodyFormDataItemDeleted, requestBodyFormDataItemUpdated, } from "../requestBodySlice"; export const FormData = () => { const urlEncoded = useAppSelector(selectRequestBodyFormData); const dispatch = useAppDispatch(); return (
dispatch(requestBodyFormDataItemAdded(value))} onRowDelete={(idx) => dispatch(requestBodyFormDataItemDeleted(idx))} onRowUpdate={(idx, value) => dispatch(requestBodyFormDataItemUpdated({ idx, value })) } />
); }; ================================================ FILE: webview/features/requestBody/FormData/styles.css ================================================ .form-data-wrapper { margin-top: 15px; } ================================================ FILE: webview/features/requestBody/GraphQL/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestBodyGraphqlQueryUpdated, requestBodyGraphqlVariablesUpdated, selectRequestBodyGraphqlQuery, selectRequestBodyGraphqlVariables, } from "../requestBodySlice"; const Editor = React.lazy(() => import("../../../shared/Editor")); export const GraphQL = () => { const query = useAppSelector(selectRequestBodyGraphqlQuery); const variables = useAppSelector(selectRequestBodyGraphqlVariables); const dispatch = useAppDispatch(); return (
QUERY
loading
}> dispatch(requestBodyGraphqlQueryUpdated(data))} />
VARIABLES
loading
}> dispatch(requestBodyGraphqlVariablesUpdated(data)) } /> ); }; ================================================ FILE: webview/features/requestBody/GraphQL/styles.css ================================================ .gql-wrapper { display: flex; height: 100%; margin-top: 10px; } .gql-section { height: 95%; padding: 0 10px 0 10px; display: flex; flex-direction: column; flex: 1; } .gql-section-heading { font-size: var(--default-font-size); padding-bottom: 10px; font-weight: 700; } .gql-editor { height: 90%; } ================================================ FILE: webview/features/requestBody/NoBody/index.tsx ================================================ import * as React from "react"; import "./styles.css"; export const None = () => { return (
This request does not have a body
); }; ================================================ FILE: webview/features/requestBody/NoBody/styles.css ================================================ .none-wrapper { border-top: var(--default-border-size) solid var(--border); display: flex; justify-content: center; margin-top: 15px; } .none-prompt { color: var(--default-text-light); font-size: var(--default-font-size); margin-top: 20px; } ================================================ FILE: webview/features/requestBody/Raw/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestBodyRawUpdated, selectRequestBodyRaw, selectRequestBodyRawFormat, selectRequestBodyRawLanguage, } from "../requestBodySlice"; const Editor = React.lazy(() => import("../../../shared/Editor")); export const Raw = () => { const raw = useAppSelector(selectRequestBodyRaw); const language = useAppSelector(selectRequestBodyRawLanguage); const format = useAppSelector(selectRequestBodyRawFormat); const dispatch = useAppDispatch(); return (
loading
}> dispatch(requestBodyRawUpdated(data))} /> ); }; ================================================ FILE: webview/features/requestBody/Raw/styles.css ================================================ .raw-wrapper { height: 95%; margin-top: 15px; } .raw-editor { height: 95%; } ================================================ FILE: webview/features/requestBody/RequestBody/index.tsx ================================================ import * as React from "react"; import { Binary } from "../Binary"; import { FormData } from "../FormData"; import { None } from "../NoBody"; import { Raw } from "../Raw"; import { UrlEncoded } from "../UrlEncoded"; import { GraphQL } from "../GraphQL"; import { requestBodyModes, requestBodyRawLanguages, requestBodyRawLanguageUpdated, requestBodyModeUpdated, selectRequestBodyMode, selectRequestBodyRawLanguage, requestBodyRawFormatUpdated, } from "../requestBodySlice"; import "./styles.css"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; export const Body = () => { const bodyMode = useAppSelector(selectRequestBodyMode); const language = useAppSelector(selectRequestBodyRawLanguage); const hideBeautifyButton = bodyMode === "raw" && language === "json"; const dispatch = useAppDispatch(); return (
{requestBodyModes.map(({ name, value }) => (
e.target.checked && dispatch(requestBodyModeUpdated(value)) } className="radio-body-option" checked={bodyMode === value ? true : false} />
))} {bodyMode === "raw" ? ( ) : null}
{bodyMode === "none" ? ( ) : bodyMode === "formdata" ? ( ) : bodyMode === "urlencoded" ? ( ) : bodyMode === "raw" ? ( ) : bodyMode === "file" ? ( ) : bodyMode === "graphql" ? ( ) : null}
); }; ================================================ FILE: webview/features/requestBody/RequestBody/styles.css ================================================ .request-body-wrapper { display: flex; flex-direction: column; height: 100%; } .request-body-window-wrapper { flex: 1; } .body-options-wrapper { display: flex; align-items: center; } .body-options { display: flex; padding: 6px 5px 6px 0px; margin: 0 10px 0 0; justify-content: center; align-items: center; } .radio-body-option { cursor: pointer; outline: none; border-radius: 0; margin: 0px 8px; } .radio-body-option:checked { outline: none; } .radio-body-option:focus { outline: none; } .label-body-option { color: var(--default-text-light); font-size: var(--default-font-size); } .select-raw-lang { padding: 4px; margin-left: 5px; display: inline-block; font-weight: 600; outline: none; border: var(--default-border-size) solid var(--border); border-radius: 2px; background-color: var(--background); color: var(--default-text-light); cursor: pointer; font-size: var(--small-font-size); } .select-raw-lang:hover { color: var(--default-text); } .select-raw-lang:focus { outline: none; } #request-beautify { margin-left: 10px; } ================================================ FILE: webview/features/requestBody/UrlEncoded/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { KeyValueTable } from "../../../shared/KeyValueTable"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { selectRequestBodyUrlEncoded, requestBodyUrlEncodedItemAdded, requestBodyUrlEncodedItemDeleted, requestBodyUrlEncodedItemUpdated, } from "../requestBodySlice"; export const UrlEncoded = () => { const urlEncoded = useAppSelector(selectRequestBodyUrlEncoded); const dispatch = useAppDispatch(); return (
dispatch(requestBodyUrlEncodedItemAdded(value))} onRowDelete={(idx) => dispatch(requestBodyUrlEncodedItemDeleted(idx))} onRowUpdate={(idx, value) => dispatch(requestBodyUrlEncodedItemUpdated({ idx, value })) } />
); }; ================================================ FILE: webview/features/requestBody/UrlEncoded/styles.css ================================================ .url-encoded-wrapper { margin-top: 15px; } ================================================ FILE: webview/features/requestBody/requestBodySlice.ts ================================================ /* eslint-disable @typescript-eslint/no-explicit-any */ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; export const requestBodyModes = [ { name: "none", value: "none" }, { name: "form-data", value: "formdata" }, { name: "x-www-form-urlencoded", value: "urlencoded" }, { name: "raw", value: "raw" }, { name: "binary", value: "file" }, { name: "GraphQL", value: "graphql" }, ]; export const requestBodyRawLanguages = [ { name: "JSON", value: "json" }, { name: "HTML", value: "html" }, { name: "XML", value: "xml" }, { name: "Text", value: "text" }, ]; export interface RequestBodyState { mode?: string; disabled: boolean; raw: string; file: string; fileData: string; formdata: { key: string; value: string; description: string }[]; urlencoded: { key: string; value: string; description: string }[]; options: any; graphql: { query: string; variables: string }; format: boolean; } const initialState: RequestBodyState = { disabled: true, format: false, raw: "", file: "", fileData: "", formdata: [], urlencoded: [], options: { raw: { language: "json" } }, graphql: { query: "", variables: "{}" }, }; const requestBodySlice = createSlice({ name: "requestBody", initialState, reducers: { requestBodyFormDataItemAdded(state, action: PayloadAction) { state.formdata.push(action.payload); }, requestBodyFormDataItemDeleted(state, action: PayloadAction) { state.formdata.splice(action.payload, 1); }, requestBodyFormDataItemUpdated(state, action: PayloadAction) { state.formdata[action.payload.idx] = action.payload.value; }, requestBodyUrlEncodedItemAdded(state, action: PayloadAction) { state.urlencoded.push(action.payload); }, requestBodyUrlEncodedItemDeleted(state, action: PayloadAction) { state.urlencoded.splice(action.payload, 1); }, requestBodyUrlEncodedItemUpdated(state, action: PayloadAction) { state.urlencoded[action.payload.idx] = action.payload.value; }, requestBodyRawUpdated(state, action: PayloadAction) { state.raw = action.payload; }, requestBodyRawFormatUpdated(state, action: PayloadAction) { state.format = action.payload; }, requestBodyRawLanguageUpdated(state, action: PayloadAction) { state.options.raw.language = action.payload; }, requestBodyBinaryUpdated(state, action: PayloadAction) { state.file = action.payload.name; state.fileData = action.payload.data; }, requestBodyGraphqlQueryUpdated(state, action: PayloadAction) { state.graphql.query = action.payload; }, requestBodyGraphqlVariablesUpdated(state, action: PayloadAction) { state.graphql.variables = action.payload; }, requestBodyModeUpdated(state, action: PayloadAction) { if (action.payload === "none") { state.mode = undefined; state.disabled = true; } else { state.mode = action.payload; state.disabled = false; } }, }, }); export const { requestBodyBinaryUpdated, requestBodyFormDataItemAdded, requestBodyFormDataItemDeleted, requestBodyFormDataItemUpdated, requestBodyModeUpdated, requestBodyRawUpdated, requestBodyRawFormatUpdated, requestBodyRawLanguageUpdated, requestBodyUrlEncodedItemAdded, requestBodyUrlEncodedItemDeleted, requestBodyUrlEncodedItemUpdated, requestBodyGraphqlQueryUpdated, requestBodyGraphqlVariablesUpdated, } = requestBodySlice.actions; export const selectRequestBody = (state: RootState) => state.requestBody; export const selectRequestBodyMode = (state: RootState) => state.requestBody.mode || "none"; export const selectRequestBodyRaw = (state: RootState) => state.requestBody.raw; export const selectRequestBodyRawLanguage = (state: RootState) => state.requestBody.options.raw.language; export const selectRequestBodyFile = (state: RootState) => state.requestBody.file; export const selectRequestBodyFileData = (state: RootState) => state.requestBody.fileData; export const selectRequestBodyFormData = (state: RootState) => state.requestBody.formdata; export const selectRequestBodyUrlEncoded = (state: RootState) => state.requestBody.urlencoded; export const selectRequestBodyGraphqlQuery = (state: RootState) => state.requestBody.graphql.query; export const selectRequestBodyGraphqlVariables = (state: RootState) => state.requestBody.graphql.variables; export const selectRequestBodyRawFormat = (state: RootState) => state.requestBody.format; export default requestBodySlice.reducer; ================================================ FILE: webview/features/requestHeader/HeadersWindow/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { KeyValueTable } from "../../../shared/KeyValueTable"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestHeaderAdded, requestHeaderUpdated, requestHeaderDeleted, selectRequestHeaders, } from "../requestHeaderSlice"; export const Headers = () => { const header = useAppSelector(selectRequestHeaders); const dispatch = useAppDispatch(); return (
dispatch(requestHeaderAdded(value))} onRowDelete={(idx) => dispatch(requestHeaderDeleted(idx))} onRowUpdate={(idx, value) => dispatch(requestHeaderUpdated({ idx, value })) } />
); }; ================================================ FILE: webview/features/requestHeader/HeadersWindow/styles.css ================================================ .headers-wrapper { margin-top: 15px; } ================================================ FILE: webview/features/requestHeader/requestHeaderSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; interface Header { key: string; value: string; description: string; disabled: boolean; } const initialState: Header[] = [ { key: "Cache-Control", value: "no-cache", description: "", disabled: false, }, { key: "Accept", value: "*/*", description: "", disabled: false, }, { key: "Accept-Encoding", value: "gzip, deflate", description: "", disabled: false, }, { key: "Connection", value: "keep-alive", description: "", disabled: false, }, ]; const requestHeaderSlice = createSlice({ name: "requestHeader", initialState, reducers: { requestHeaderUpdated(state, action: PayloadAction) { state[action.payload.idx] = action.payload.value; }, requestHeaderAdded(state, action: PayloadAction
) { state.push(action.payload); }, requestHeaderDeleted(state, action: PayloadAction) { state.splice(action.payload, 1); }, }, }); export const { requestHeaderAdded, requestHeaderUpdated, requestHeaderDeleted, } = requestHeaderSlice.actions; export const selectRequestHeaders = (state: RootState) => state.requestHeader; export default requestHeaderSlice.reducer; ================================================ FILE: webview/features/requestMethod/RequestMethodSelector/index.tsx ================================================ import * as React from "react"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestMethodUpdated, selectRequestMethod, requestMethods, } from "../requestMethodSlice"; import "./styles.css"; export const RequestMethodSelector = () => { const requestMethod = useAppSelector(selectRequestMethod); const dispatch = useAppDispatch(); return ( ); }; ================================================ FILE: webview/features/requestMethod/RequestMethodSelector/styles.css ================================================ .request-method-selector { padding: 12px 20px; display: inline-block; font-weight: 600; outline: none; border: 0; border-radius: 2px 0 0 2px; background-color: var(--input-field); color: var(--default-text); cursor: pointer; font-size: var(--default-font-size); } .request-method-selector:hover { background-color: var(--input-field-hover); } .request-method-selector:focus { outline: none; } ================================================ FILE: webview/features/requestMethod/requestMethodSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; export const requestMethods = [ { name: "GET", value: "get" }, { name: "POST", value: "post" }, { name: "PUT", value: "put" }, { name: "PATCH", value: "patch" }, { name: "DELETE", value: "delete" }, { name: "OPTIONS", value: "options" }, { name: "HEAD", value: "head" }, ]; const initialState = "get"; const requestMethodSlice = createSlice({ name: "requestMethod", initialState, reducers: { requestMethodUpdated(state, action: PayloadAction) { return action.payload; }, }, }); export const { requestMethodUpdated } = requestMethodSlice.actions; export const selectRequestMethod = (state: RootState) => state.requestMethod; export default requestMethodSlice.reducer; ================================================ FILE: webview/features/requestOptions/RequestOptions/index.tsx ================================================ import * as React from "react"; import { useDispatch } from "react-redux"; import { useAppSelector } from "../../../redux/hooks"; import { requestOptionsUpdated, selectRequestOptions, requestOptionsList, } from "../requestOptionsSlice"; import "./styles.css"; export const RequestOptions = () => { const requestOptions = useAppSelector(selectRequestOptions); const dispatch = useDispatch(); return (
{requestOptionsList.map(({ name, value, type, ...optionDetails }) => (
{`${name}: `}
{ type === "select" ? ( ) : null // Note: Augment this switch later with different renderers for // different types of options }
))}
); }; ================================================ FILE: webview/features/requestOptions/RequestOptions/styles.css ================================================ .req-options-wrapper { margin-top: 15px; } .options { display: flex; align-items: center; } .req-option-label { font-size: var(--default-font-size); color: var(--default-text); padding-right: 12px; } .req-option-switch { padding: 4px; display: inline-block; font-weight: 600; outline: none; border: var(--default-border-size) solid var(--border); border-radius: 2px; background-color: var(--background); color: var(--default-text-light); cursor: pointer; font-size: var(--default-font-size); } ================================================ FILE: webview/features/requestOptions/requestOptionsSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; export const requestOptionsList = [ { name: "Strict SSL", value: "strictSSL", type: "select", options: [ { key: "yes", value: "Yes" }, { key: "no", value: "No" }, ], default: "Yes", }, ]; export interface RequestOptions { strictSSL: string; } const initialState = { strictSSL: "yes", }; const requestOptionsSlice = createSlice({ name: "requestOptions", initialState, reducers: { requestOptionsUpdated(_, action: PayloadAction) { return action.payload; }, }, }); export const { requestOptionsUpdated } = requestOptionsSlice.actions; export const selectRequestOptions = (state: RootState) => state.requestOptions; export default requestOptionsSlice.reducer; ================================================ FILE: webview/features/requestUrl/RequestQueryParams/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { KeyValueTable } from "../../../shared/KeyValueTable"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestQueryParamAdded, requestQueryParamDeleted, requestQueryParamUpdated, selectRequestQueryParams, } from "../requestUrlSlice"; export const RequestQueryParams = () => { const queryParams = useAppSelector(selectRequestQueryParams); const dispatch = useAppDispatch(); return (
dispatch(requestQueryParamAdded(value))} onRowDelete={(idx) => dispatch(requestQueryParamDeleted(idx))} onRowUpdate={(idx, value) => dispatch(requestQueryParamUpdated({ idx, value })) } />
); }; ================================================ FILE: webview/features/requestUrl/RequestQueryParams/styles.css ================================================ .params-wrapper { margin-top: 15px; } ================================================ FILE: webview/features/requestUrl/RequestUrl/index.tsx ================================================ import * as React from "react"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { requestUrlUpdated, selectRequestUrl } from "../requestUrlSlice"; import "./styles.css"; export const RequestUrl = () => { const requestUrl = useAppSelector(selectRequestUrl); const dispatch = useAppDispatch(); return ( dispatch(requestUrlUpdated(e.target.value))} /> ); }; ================================================ FILE: webview/features/requestUrl/RequestUrl/styles.css ================================================ .input-request-url { flex: 2; padding: 13px 20px; color: var(--default-text); display: inline-block; outline: none; border: 0; border-radius: 0 2px 2px 0; background-color: var(--input-field); font-size: var(--default-font-size); } .input-request-url:focus { outline: none; } .input-request-url:hover { background-color: var(--input-field-hover); } ================================================ FILE: webview/features/requestUrl/requestUrlSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; import { Url } from "postman-collection"; interface QueryParam { key: string; value: string; description: string; disabled: boolean; } interface Variable { key: string; value: string; description: string; } export interface RequestUrlState { auth?: string; hash?: string; host?: string[]; path?: string[]; port?: string; protocol?: string; query: QueryParam[]; variables: Variable[]; } const initialState: RequestUrlState = { query: [], variables: [], }; const requestUrlSlice = createSlice({ name: "requestUrl", initialState, reducers: { requestUrlUpdated(state, action: PayloadAction) { const { query, ...other } = Url.parse(action.payload); return { ...other, query: [ ...state.query.filter(({ disabled }) => disabled), ...(query || []), ], }; }, requestQueryParamAdded(state, action: PayloadAction) { state.query.push(action.payload); }, requestQueryParamUpdated(state, action: PayloadAction) { state.query[action.payload.idx] = action.payload.value; }, requestQueryParamDeleted(state, action: PayloadAction) { state.query.splice(action.payload, 1); }, // requestUrlVariableUpdated(state, action: PayloadAction) {}, }, }); export const { requestUrlUpdated, requestQueryParamAdded, requestQueryParamUpdated, requestQueryParamDeleted, // requestUrlVariableUpdated, } = requestUrlSlice.actions; export const selectRequestUrl = (state: RootState) => new Url(state.requestUrl).toString(); export const selectRequestQueryParams = (state: RootState) => state.requestUrl.query; export const selectRequestVariables = (state: RootState) => state.requestUrl.variables; export default requestUrlSlice.reducer; ================================================ FILE: webview/features/response/ResponseBody/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import { useAppSelector } from "../../../redux/hooks"; import { selectResponse } from "../responseSlice"; import * as propTypes from "prop-types"; const Editor = React.lazy(() => import("../../../shared/Editor")); export const ResponseBody = (props) => { const { language } = props; const response = useAppSelector(selectResponse); return (
loading
}> ); }; ResponseBody.propTypes = { language: propTypes.string.isRequired, }; ================================================ FILE: webview/features/response/ResponseBody/styles.css ================================================ .response-window { height: 100%; } .response-editor { height: 95%; } ================================================ FILE: webview/features/response/ResponseHeaders/index.tsx ================================================ import * as React from "react"; import { useAppSelector } from "../../../redux/hooks"; import { KeyValueTable } from "../../../shared/KeyValueTable"; import { selectResponseHeaders } from "../responseSlice"; import "./styles.css"; export const ResponseHeaders = () => { const headers = useAppSelector(selectResponseHeaders); return (
); }; ================================================ FILE: webview/features/response/ResponseHeaders/styles.css ================================================ .response-headers { display: flex; height: 80%; } ================================================ FILE: webview/features/response/ResponseTab/index.tsx ================================================ import * as React from "react"; import "./styles.css"; import * as propTypes from "prop-types"; import { responseOptions } from "../../../constants/response-options"; import { supportedLangs } from "../../../constants/supported-langs"; import { useAppSelector } from "../../../redux/hooks"; import { selectResponse } from "../responseSlice"; const ResponseInfo = ({ responseTitle, info }) => { return ( <>
{responseTitle}
{info}
); }; export const ResponseTab = (props) => { const { selected, setSelected, language, setLanguage } = props; const response = useAppSelector(selectResponse); return (
{responseOptions.map((option) => ( ))} {selected === "body" ? ( ) : null}
); }; ResponseInfo.propTypes = { responseTitle: propTypes.string.isRequired, info: propTypes.string.isRequired, }; ResponseTab.propTypes = { selected: propTypes.string.isRequired, setSelected: propTypes.func.isRequired, language: propTypes.string.isRequired, setLanguage: propTypes.func.isRequired, }; ================================================ FILE: webview/features/response/ResponseTab/styles.css ================================================ .response-options-tab-wrapper { display: flex; justify-content: space-between; align-items: center; padding: 15px 20px 5px 20px; } .response-options { display: flex; } .response-option { padding: 5px 5px 5px 0; margin: 0 10px 0 0; display: flex; cursor: pointer; background-color: transparent; color: var(--default-text-light); outline: none; border: none; border-radius: 0; font-weight: 500; font-size: var(--default-font-size); } .response-option:hover { color: var(--default-text); } .response-option-selected { color: var(--default-text); } .response-options-header-length { margin-left: 4px; color: var(--tab-info); } .response-status { font-size: var(--default-font-size); display: flex; align-items: center; } .text-response-info { color: var(--tab-info); margin-left: 5px; margin-right: 5px; } .select-res-lang { margin-left: 10px; padding: 5px; display: inline-block; font-weight: 600; outline: none; border: var(--default-border-size) solid var(--border); border-radius: 2px; background-color: var(--background); color: var(--default-text-light); cursor: pointer; font-size: var(--small-font-size); } .select-res-lang:hover { color: var(--default-text); } .select-res-lang:focus { outline: none; } ================================================ FILE: webview/features/response/ResponseWindow/index.tsx ================================================ import * as React from "react"; import * as propTypes from "prop-types"; import "./styles.css"; import { ResponseBody } from "../ResponseBody"; import { ResponseHeaders } from "../ResponseHeaders"; export const ResponseWindow = (props) => { const { selected, language } = props; return (
{selected === "body" ? ( ) : selected === "headers" ? ( ) : null}
); }; ResponseWindow.propTypes = { selected: propTypes.string.isRequired, language: propTypes.string.isRequired, }; ================================================ FILE: webview/features/response/ResponseWindow/styles.css ================================================ .response-options-window-wrapper { flex: 1; padding: 5px 20px 10px 20px; } ================================================ FILE: webview/features/response/responseSlice.ts ================================================ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "../../redux/store"; export interface ResponseState { status?: number; statusText?: string; data?: string; initial: boolean; error?: Error; loading?: boolean; headers?: { key: string; value: string }[]; duration?: number; } const initialState: ResponseState = { initial: true }; const responseSlice = createSlice({ name: "response", initialState, reducers: { responseUpdated(state, action: PayloadAction) { return { ...action.payload, headers: action.payload.headers && Object.entries(action.payload.headers).map(([key, value]) => ({ key, value, })), initial: false, loading: false, }; }, responseLoadingStarted(state) { state.loading = true; }, }, }); export const { responseUpdated, responseLoadingStarted } = responseSlice.actions; export const selectResponse = (state: RootState) => state.response; export const selectResponseHeaders = (state: RootState) => state.response.headers; export default responseSlice.reducer; ================================================ FILE: webview/index.css ================================================ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; --primary: var(--vscode-button-hoverBackground); --primary-dark: var(--vscode-button-background); --background: var(--vscode-editor-background); --input-field: var(--vscode-input-background); --input-field-focus: var(--vscode-input-background); --input-field-hover: var(--vscode-input-background); --input-field-unchecked: #808080; --default-text: var(--vscode-foreground); --default-text-light: var(--vscode-descriptionForeground); --border: var(--vscode-tree-tableColumnsBorder); --send-button: var(--vscode-button-background); --send-button-hover: var(--vscode-button-hoverBackground); --tab-info: #259c47; --logo-color: var(--vscode-tree-tableColumnsBorder); --error-message: var(--vscode-errorForeground); --default-font-size: var(--vscode-editor-font-size); --small-font-size: width: calc(var(--vscode-editor-font-size) * 0.727);; --default-border-size: 0.1px; } html, body, #root, .App { height: 100%; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } ================================================ FILE: webview/index.tsx ================================================ import * as React from "react"; import * as ReactDOM from "react-dom"; import { Provider } from "react-redux"; import { store } from "./redux/store"; import "./index.css"; import App from "./App"; ReactDOM.hydrate( , document.getElementById("root") ); ================================================ FILE: webview/pages/Postcode/index.tsx ================================================ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from "react"; import "./styles.css"; import { RequestBar } from "../../components/RequestBar"; import { RequestOptionsTab } from "../../components/RequestOptionsBar"; import { RequestOptionsWindow } from "../../components/RequestOptionsWindow"; import { Response } from "../../components/Response"; import { requestOptions } from "../../constants/request-options"; export const Postcode = () => { const [selectedOption, setSelectedOption] = React.useState( requestOptions[0].value ); return (
); }; ================================================ FILE: webview/pages/Postcode/styles.css ================================================ .request-wrapper { display: flex; flex-direction: column; height: inherit; } .request-options-wrapper { display: flex; flex-direction: column; min-height: 50%; } .response-wrapper { display: flex; flex-direction: column; height: 50%; } ================================================ FILE: webview/prerender.tsx ================================================ import { Provider } from "react-redux"; import * as React from "react"; import { renderToString } from "react-dom/server"; import App from "./App"; import { store } from "./redux/store"; export default () => { const html = renderToString( ); const preloadedState = store.getState(); return `
${html}
`; }; ================================================ FILE: webview/react-app-env.d.ts ================================================ /// /// /// declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: "development" | "production" | "test"; readonly PUBLIC_URL: string; } } declare module "*.avif" { const src: string; export default src; } declare module "*.bmp" { const src: string; export default src; } declare module "*.gif" { const src: string; export default src; } declare module "*.jpg" { const src: string; export default src; } declare module "*.jpeg" { const src: string; export default src; } declare module "*.png" { const src: string; export default src; } declare module "*.webp" { const src: string; export default src; } declare module "*.svg" { import * as React from "react"; export const ReactComponent: React.FunctionComponent< React.SVGProps & { title?: string } >; const src: string; export default src; } declare module "*.module.css" { const classes: { readonly [key: string]: string }; export default classes; } declare module "*.module.scss" { const classes: { readonly [key: string]: string }; export default classes; } declare module "*.module.sass" { const classes: { readonly [key: string]: string }; export default classes; } ================================================ FILE: webview/redux/hooks.ts ================================================ import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import type { RootState, AppDispatch } from "./store"; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = () => useDispatch(); export const useAppSelector: TypedUseSelectorHook = useSelector; ================================================ FILE: webview/redux/store.ts ================================================ import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; import requestAuthReducer from "../features/requestAuth/requestAuthSlice"; import requestBodyReducer from "../features/requestBody/requestBodySlice"; import requestHeaderReducer from "../features/requestHeader/requestHeaderSlice"; import requestMethodReducer from "../features/requestMethod/requestMethodSlice"; import requestUrlReducer from "../features/requestUrl/requestUrlSlice"; import responseReducer from "../features/response/responseSlice"; import codeGenOptionsReducer from "../features/codeGen/codeGenSlice"; import requestOptionsReducer from "../features/requestOptions/requestOptionsSlice"; let preloadedState; if (typeof window !== "undefined") { preloadedState = (window as any).__PRELOADED_STATE__; delete (window as any).__PRELOADED_STATE__; } export const store = configureStore({ reducer: { requestAuth: requestAuthReducer, requestBody: requestBodyReducer, requestHeader: requestHeaderReducer, requestMethod: requestMethodReducer, requestUrl: requestUrlReducer, response: responseReducer, codeGenOptions: codeGenOptionsReducer, requestOptions: requestOptionsReducer, }, preloadedState, }); export type AppDispatch = typeof store.dispatch; export type RootState = ReturnType; export type AppThunk = ThunkAction< ReturnType, RootState, unknown, Action >; ================================================ FILE: webview/shared/Editor/index.tsx ================================================ import * as React from "react"; import * as monaco from "monaco-editor"; import * as propTypes from "prop-types"; import "./styles.css"; import { useAppDispatch } from "../../redux/hooks"; import { requestBodyRawFormatUpdated } from "../../features/requestBody/requestBodySlice"; const Editor = (props) => { const { value, language, onChange, readOnly, className, copyButton, format } = props; const divEl = React.useRef(null); const [editor, setEditor] = React.useState(undefined); const [copy, setCopy] = React.useState("Copy"); const dispatch = useAppDispatch(); React.useEffect(() => { if (divEl.current) { const tmpEditor = monaco.editor.create(divEl.current, { minimap: { enabled: false }, scrollBeyondLastLine: false, theme: "vs-dark", value, language, readOnly, }); window.addEventListener("resize", () => { tmpEditor.layout(); }); if (onChange) { tmpEditor.onDidChangeModelContent(() => { onChange(tmpEditor.getValue()); }); } setEditor(tmpEditor); return () => { tmpEditor.dispose(); }; } }, []); React.useEffect(() => { if (editor) { if (editor.getValue() !== value) { editor.setValue(value); } const model = editor.getModel(); monaco.editor.setModelLanguage(model, language); if (format) { editor.updateOptions({ readOnly: false }); setTimeout(() => { editor .getAction("editor.action.formatDocument") .run() .then(() => { editor.updateOptions({ readOnly }); dispatch(requestBodyRawFormatUpdated(false)); }); }, 300); } } }, [value, language, editor, format]); return (
{copyButton && ( )}
); }; Editor.propTypes = { value: propTypes.string.isRequired, language: propTypes.string.isRequired, onChange: propTypes.func, className: propTypes.string, readOnly: propTypes.bool, copyButton: propTypes.bool, format: propTypes.bool, }; export default Editor; ================================================ FILE: webview/shared/Editor/styles.css ================================================ .monaco-editor { border: var(--default-border-size) solid var(--border); } .postcode-editor { position: relative; } .copy-button { font-size: var(--default-font-size); background-color: rgb(51, 51, 51); border: none; color: #fff; border-radius: 0.4rem; cursor: pointer; outline: none; padding: 0.4rem 0.5rem; position: absolute; transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; z-index: 10; right: 0.5rem; top: 0.5rem; opacity: 0; visibility: hidden; } .postcode-editor:hover .copy-button { opacity: 1; visibility: visible; } ================================================ FILE: webview/shared/KeyValueTable/index.tsx ================================================ import * as React from "react"; import * as propTypes from "prop-types"; import { FaTrashAlt } from "react-icons/fa"; import "./styles.css"; export const KeyValueRow = (props) => { const { itemKey, itemValue, itemDescription, itemDisabled, actions, onDelete, onChange, fixed, } = props; return ( {!fixed && ( {actions && ( onChange({ key: itemKey, value: itemValue, description: itemDescription, disabled: !e.target.checked, }) } /> )} )} onChange({ key: e.target.value, value: itemValue, description: itemDescription, disabled: itemDisabled, }) } /> onChange({ key: itemKey, value: e.target.value, description: itemDescription, disabled: itemDisabled, }) } /> {!fixed && ( onChange({ key: itemKey, value: itemValue, description: e.target.value, disabled: itemDisabled, }) } /> )} {!fixed && ( {actions && ( )} )} ); }; export const KeyValueTable = (props) => { const { data, fixed, onRowUpdate, onRowAdd, onRowDelete } = props; return ( {!fixed && } {!fixed && } {!fixed && } {(fixed ? data : [...data, {}]).map( ({ key, value, description, disabled }, idx) => ( onRowDelete(idx)} onChange={(item) => idx === data.length ? onRowAdd(item) : onRowUpdate(idx, item) } key={idx} actions={idx !== data.length} /> ) )}
KEY VALUEDESCRIPTION
); }; KeyValueTable.propTypes = { data: propTypes.array.isRequired, fixed: propTypes.bool, onRowDelete: propTypes.func, onRowAdd: propTypes.func, onRowUpdate: propTypes.func, }; KeyValueRow.propTypes = { fixed: propTypes.bool, itemKey: propTypes.string.isRequired, itemValue: propTypes.string.isRequired, itemDescription: propTypes.string.isRequired, itemDisabled: propTypes.bool.isRequired, actions: propTypes.bool.isRequired, onChange: propTypes.func.isRequired, onDelete: propTypes.func.isRequired, }; ================================================ FILE: webview/shared/KeyValueTable/styles.css ================================================ .kv-table { border-collapse: collapse; width: 100%; } .kv-table, td, th { border: var(--default-border-size) solid var(--border); } th, .kv-input { color: var(--default-text); padding: 10px 12px; } th { text-align: left; font-weight: 700; font-size: var(--small-font-size); } .kv-action-cell { text-align: center; width: 30px; } .kv-input { box-sizing: border-box; width: 100%; border: 0; background-color: transparent; font-size: var(--default-font-size); } .kv-input:focus { outline: none; } .kv-disabled .kv-input { color: var(--input-field-unchecked); } .kv-delete-button { cursor: pointer; } ================================================ FILE: webview/vscode.ts ================================================ let vscode; if (typeof acquireVsCodeApi !== "undefined") { vscode = acquireVsCodeApi(); } export default vscode;