diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7f768954..060c0d1d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,7 +10,8 @@ If relevant, mention your manual testing here. If possible, include screenshots. - [ ] Issue has been linked to this PR - [ ] Code has been reviewed by person creating the PR -- [ ] Commit messages, branch names, code formatting adheres to our guidelines +- [ ] Commit messages, branch names, code formatting adheres to our [Contributing Guidelines +](https://github.com/EXXETA/rufus/blob/main/CONTRIBUTING.md) - [ ] Automated tests have been written, if possible - [ ] Manual testing has been performed - [ ] Documentation has been updated, if necessary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f0ca3d1..1ecfd100 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,11 @@ jobs: - name: Install dependencies run: npm ci + - name: Prettier check + run: npx prettier '**/*.{ts,tsx}' --check + - name: Run tests run: npm test - name: Build package - run: npm run package \ No newline at end of file + run: npm run package diff --git a/.gitignore b/.gitignore index 649402a2..c6f0ee20 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,20 @@ out/ # IntelliJ .idea +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + # SCSS *.css.d.ts *.sass.d.ts diff --git a/.prettierrc b/.prettierrc index 1dc71645..2c3bf697 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "trailingComma": "es5", "tabWidth": 2, "semi": true, - "singleQuote": true + "singleQuote": true, + "printWidth": 100 } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9bf4d12b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b2dc766..0d8b8968 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,6 +55,40 @@ For this repository we have agreed on the following git workflow: - Preferably rebase your branch onto `main` before completing the PR - Once the `main` branch reaches a stable state, we can create a release by... - creating an annotated tag `vX.Y.Z` for the target commit on the `main` branch - - tag message should be: `Release vX.Y.Z` + - tag message should be: `Release vX.Y.Z` - creating a release on github that references the tag - - attaching the release binaries to the github release \ No newline at end of file + - attaching the release binaries to the github release + +## Code Formatting + +We use [Prettier](https://prettier.io/) for TypeScript code formatting. +To ensure that the code formatting follows our configuration +in [.prettierrc](https://github.com/EXXETA/rufus/blob/main/.prettierrc), the Prettier plugin should +be installed in your IDE. +The setup for Prettier is described in the following sections for IntelliJ and Visual Studio Code: + +### IntelliJ + +1. Navigate to **Settings** > **Plugins**. +2. Install [Prettier](https://plugins.jetbrains.com/plugin/10456-prettier) from marketplace. +3. Install [Save Actions X](https://plugins.jetbrains.com/plugin/22113-save-actions-x) from + marketplace. +4. Activate options "Activate save actions on save (before saving each file, performs the configured + actions below)" and "Reformat file": + ![Code Formatting IntelliJ](images/contributing/code-formatting-intellij.png) + +### Visual Studio Code + +Install [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +from Extensions. When installation is completed, Prettier will be used directly as the default +formatter when saving a file, as it is already configured in `.vscode/settings.json`. + +--- +After following these instructions, the code should always be formatted automatically when saving a +file. + +However, you can check all Typescript files for code style issues with this command: +`npx prettier '**/*.{ts,tsx}' --check` + +If you want Prettier to fix code style issues, you can use the following command: +`npx prettier '**/*.{ts,tsx}' --write` diff --git a/forge.config.ts b/forge.config.ts index 63bd0dd1..eb188ffd 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -14,7 +14,7 @@ import { rendererConfig } from './webpack.renderer.config'; const config: ForgeConfig = { packagerConfig: { asar: true, - icon: './images/icon' + icon: './images/icon', }, rebuildConfig: {}, makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})], @@ -30,16 +30,16 @@ const config: ForgeConfig = { js: './src/renderer/index.tsx', name: 'main_window', preload: { - js: './src/main/preload.ts' - } - } - ] + js: './src/main/preload.ts', + }, + }, + ], }, devServer: { client: { - overlay: { runtimeErrors: error => !error.message.startsWith('ResizeObserver loop') } - } - } + overlay: { runtimeErrors: (error) => !error.message.startsWith('ResizeObserver loop') }, + }, + }, }), // Fuses are used to enable/disable various Electron functionality // at package time, before code signing the application @@ -50,9 +50,9 @@ const config: ForgeConfig = { [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, [FuseV1Options.EnableNodeCliInspectArguments]: false, [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, - [FuseV1Options.OnlyLoadAppFromAsar]: true - }) - ] + [FuseV1Options.OnlyLoadAppFromAsar]: true, + }), + ], }; export default config; diff --git a/images/contributing/code-formatting-intellij.png b/images/contributing/code-formatting-intellij.png new file mode 100644 index 00000000..ea3c3f66 Binary files /dev/null and b/images/contributing/code-formatting-intellij.png differ diff --git a/package-lock.json b/package-lock.json index 0e8e3cf2..7c8c6e40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,7 @@ "monaco-editor-webpack-plugin": "^7.1.0", "node-loader": "^2.0.0", "postcss": "^8.4.38", + "prettier": "^3.3.3", "style-loader": "^4.0.0", "tailwindcss": "^3.4.4", "ts-jest": "^29.2.5", @@ -13203,6 +13204,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", diff --git a/package.json b/package.json index d566d5e8..2a6e44c9 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "monaco-editor-webpack-plugin": "^7.1.0", "node-loader": "^2.0.0", "postcss": "^8.4.38", + "prettier": "^3.3.3", "style-loader": "^4.0.0", "tailwindcss": "^3.4.4", "ts-jest": "^29.2.5", diff --git a/scripts/setup-jest.ts b/scripts/setup-jest.ts index d37f5b9d..883f0092 100644 --- a/scripts/setup-jest.ts +++ b/scripts/setup-jest.ts @@ -20,10 +20,8 @@ jest.mock('electron', () => ({ getName: () => 'DiagClient', }, safeStorage: { - encryptString: (plainText: string) => - Buffer.from(plainText).toString('base64'), - decryptString: (encrypted: Buffer) => - Buffer.from(encrypted.toString(), 'base64').toString(), + encryptString: (plainText: string) => Buffer.from(plainText).toString('base64'), + decryptString: (encrypted: Buffer) => Buffer.from(encrypted.toString(), 'base64').toString(), }, })); jest.mock('node:fs', () => fs); diff --git a/src/main/environment/service/environment-service.ts b/src/main/environment/service/environment-service.ts index ae64cf04..b056bc7c 100644 --- a/src/main/environment/service/environment-service.ts +++ b/src/main/environment/service/environment-service.ts @@ -14,8 +14,7 @@ const persistenceService = PersistenceService.instance; * request body, headers, etc.). */ export class EnvironmentService implements Initializable { - public static readonly instance: EnvironmentService = - new EnvironmentService(); + public static readonly instance: EnvironmentService = new EnvironmentService(); public currentCollection: Collection; @@ -34,9 +33,7 @@ export class EnvironmentService implements Initializable { * @returns The stream with the variables replaced. */ public setVariablesInStream(stream: Readable) { - return stream.pipe( - new TemplateReplaceStream(this.getVariableValue.bind(this)), - ); + return stream.pipe(new TemplateReplaceStream(this.getVariableValue.bind(this))); } /** @@ -80,9 +77,7 @@ export class EnvironmentService implements Initializable { * @param path The path of the collection to load and set as the current collection. */ public async changeCollection(path: string) { - return (this.currentCollection = await persistenceService.loadCollection( - path, - )); + return (this.currentCollection = await persistenceService.loadCollection(path)); } /** @@ -94,10 +89,7 @@ export class EnvironmentService implements Initializable { * @returns The value of the variable if it exists and is enabled, otherwise undefined. */ private getVariableValue(key: string) { - return ( - this.currentCollection.variables[key]?.value ?? - this.getSystemVariableValue(key) - ); + return this.currentCollection.variables[key]?.value ?? this.getSystemVariableValue(key); } /** diff --git a/src/main/error/internal-error.ts b/src/main/error/internal-error.ts index 7963f90f..e67d4599 100644 --- a/src/main/error/internal-error.ts +++ b/src/main/error/internal-error.ts @@ -6,7 +6,11 @@ export enum InternalErrorType { } export class InternalError extends Error { - constructor(public readonly type: InternalErrorType, message: string, public readonly cause?: Error) { + constructor( + public readonly type: InternalErrorType, + message: string, + public readonly cause?: Error + ) { super(message); this.name = 'InternalError'; } diff --git a/src/main/event/main-event-service.test.ts b/src/main/event/main-event-service.test.ts index 4fe1327f..6af1b6c1 100644 --- a/src/main/event/main-event-service.test.ts +++ b/src/main/event/main-event-service.test.ts @@ -3,17 +3,14 @@ import path from 'node:path'; import { tmpdir } from 'node:os'; import { fs } from 'memfs'; -jest.mock( - 'electron', - () => ({ - ipcMain: { - handle: jest.fn(), - }, - app: { - getPath: jest.fn().mockReturnValue(''), - }, - }), -); +jest.mock('electron', () => ({ + ipcMain: { + handle: jest.fn(), + }, + app: { + getPath: jest.fn().mockReturnValue(''), + }, +})); const eventService = MainEventService.instance; @@ -30,7 +27,6 @@ describe('MainEventService', () => { }); it('should read the file correctly providing no parameters', async () => { - // Act const buffer = await eventService.readFile(TEST_FILE_PATH); @@ -39,7 +35,6 @@ describe('MainEventService', () => { }); it('should read the file correctly with offset', async () => { - // Act const buffer = await eventService.readFile(TEST_FILE_PATH, 1); @@ -48,12 +43,10 @@ describe('MainEventService', () => { }); it('should read the file correctly with offset and length', async () => { - // Act const buffer = await eventService.readFile(TEST_FILE_PATH, 1, 2); // Assert expect(Buffer.from(buffer).toString()).toBe(TEST_STRING.substring(1, 3)); }); - }); diff --git a/src/main/event/main-event-service.ts b/src/main/event/main-event-service.ts index 7552b6a6..47b9d208 100644 --- a/src/main/event/main-event-service.ts +++ b/src/main/event/main-event-service.ts @@ -20,7 +20,7 @@ declare type AsyncFunction = (...args: unknown[]) => Promise; * @param fn The function to wrap. */ function wrapWithErrorHandler, R>(fn: F) { - return async function(...args: Parameters) { + return async function (...args: Parameters) { try { return (await fn(...args)) as R; } catch (error) { @@ -36,14 +36,13 @@ function wrapWithErrorHandler, R>(fn: F) { * @param functionName The name of the function to register. */ function registerEvent(instance: T, functionName: keyof T) { - if (typeof functionName !== 'string' || functionName === 'constructor') - return; + if (typeof functionName !== 'string' || functionName === 'constructor') return; const method = instance[functionName]; if (typeof method === 'function') { console.debug(`Registering event function "${functionName}()" on backend`); ipcMain.handle(functionName as string, (_event, ...args) => - wrapWithErrorHandler(method as unknown as AsyncFunction)(...args), + wrapWithErrorHandler(method as unknown as AsyncFunction)(...args) ); } } @@ -84,7 +83,7 @@ export class MainEventService implements IEventService { offset, 'and length limited to', length ?? 'unlimited', - 'bytes', + 'bytes' ); let file: FileHandle | null = null; @@ -105,10 +104,7 @@ export class MainEventService implements IEventService { } } - async saveRequest( - request: RufusRequest, - textBody?: string, - ) { + async saveRequest(request: RufusRequest, textBody?: string) { await persistenceService.saveRequest(request, textBody); } diff --git a/src/main/filesystem/filesystem-service.ts b/src/main/filesystem/filesystem-service.ts index 653f62f3..6b4a5f2b 100644 --- a/src/main/filesystem/filesystem-service.ts +++ b/src/main/filesystem/filesystem-service.ts @@ -6,13 +6,10 @@ import fs from 'node:fs'; * Singleton service for file system operations */ export class FileSystemService { - private static readonly _instance: FileSystemService = new FileSystemService(); private static readonly _tempDir = app?.getPath('temp') ?? ''; - constructor() { - - } + constructor() {} public static get instance() { return this._instance; @@ -36,5 +33,4 @@ export class FileSystemService { // how does this even work... return fs.createReadStream(filePath); } - } diff --git a/src/main/import/service/import-service.test.ts b/src/main/import/service/import-service.test.ts index cf46fc71..6135b672 100644 --- a/src/main/import/service/import-service.test.ts +++ b/src/main/import/service/import-service.test.ts @@ -6,22 +6,28 @@ import { RufusRequest } from 'shim/objects/request'; import { tmpdir } from 'node:os'; import path from 'node:path'; -const POSTMAN_COLLECTION = '{"info":{"name":"HTTP Status Messages","id":"my-collection-id","_postman_schema":"https://schema.getpostman.com/#2.0.0","version":{"major":"2","minor":"0","patch":"0","prerelease":"draft.1"}},"variable":[{"id":"var-1","type":"string","value":"hello-world"},{"id":"var-2","type":"number","value":"123"},{"id":"var-3","type":"boolean","value":"true"},{"id":"var-4","type":"any","value":"hello-world-any"},{"id":"var-5","value":null}],"event":[{"listen":"test","id":"my-global-script-1","script":{"type":"text/javascript","exec":["const package1 = pm.require(\\"package1\\");","console.log(\\"hello\\", package1);"],"packages":{"package1":{"id":"script-package-1"}}}},{"listen":"prerequest","script":{"type":"text/javascript","exec":["const package1 = pm.require(\\"package1\\");","console.log(\\"hello\\", package1);"],"packages":{"package1":{"id":"script-package-1"}}}}],"item":[{"id":"request-200","description":{"content":"

This is H1

italic ","version":"2.0.1-abc+efg"},"name":"200 ok","request":"http://echo.getpostman.com/status/200","response":[{"name":"a sample response","originalRequest":"http://echo.getpostman.com/status/200","status":"200 OK","code":200,"header":"Content-Type: application/json\\r\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\r\\n","cookie":[{"domain":".httpbin.org","expires":1502442248,"hostOnly":false,"httpOnly":false,"key":"_ga","path":"/","secure":false,"session":false,"_postman_storeId":"0","value":"GA1.2.113558537.1435817423"}],"body":"response body"}],"event":[{"listen":"test","script":"my-global-script-1"},{"listen":"test","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");"}}],"proxy":{"match":"https://*.getpostman.com/*","server":"https://proxy.com"},"protocolProfileBehavior":{"disableBodyPruning":true}},{"id":"request-200-post","description":{"content":"

This is H1

italic ","version":"2.0.1-abc+efg"},"name":"200 ok","request":{"description":{"content":"my description","type":"text/markdown"},"method":"POST","url":"http://echo.getpostman.com/post","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"urlencoded","urlencoded":[{"key":"yo","value":"mate"}]}},"response":[{"name":"a sample response","originalRequest":"http://echo.getpostman.com/post","status":"200 OK","code":200,"header":"Content-Type: application/json\\r\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\r\\n","cookie":[{"domain":".httpbin.org","expires":1502442248,"hostOnly":false,"httpOnly":false,"key":"_ga","path":"/","secure":false,"session":false,"_postman_storeId":"0","value":"GA1.2.113558537.1435817423"}],"body":"response body"}],"event":[{"listen":"test","script":"my-global-script-1"},{"listen":"test","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");"}}]},{"name":"This is a folder","id":"my-folder-1","_my_meta":"hello","item":[{"id":"request-200","name":"201","request":{"url":"http://echo.getpostman.com/status/201","method":"PUT","body":{"mode":"urlencoded","urlencoded":[{"key":"yo","value":"mate"}]},"header":"Content-Type: application/json\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\n"}},{"id":"request-post","name":"201","request":{"url":"http://echo.getpostman.com/post","body":{"mode":"raw","raw":"blahblah"},"auth":{"type":"basic","basic":{"username":"yosam","password":"asdhjajsd"}}}},{"id":"request-400","name":"400 bad request","request":"http://shamasis:pass@echo.getpostman.com:9443/status/400/?query=string&a=b&abcd#{{search}}"},{"id":"This is a sub folder","name":"my-folder-2","item":[{"id":"blank-folder","name":"This is a blank","item":[{"id":"request-gg","name":"gg not found","request":{"description":{"content":"Some stuff I want to say about this request. It\'s in *markdown* too.","type":"text/markdown","version":"1.2.3+hi"},"url":{"description":"This is a nice URL.","protocol":"https","port":"8443","path":"path/to/document","host":"sub.example.com."},"header":[{"key":"Host","value":"sub.example.com"},{"key":"Content-Type","value":"application/json"}]},"event":[{"listen":"test","script":{"type":"text/javascript","exec":["postman.setEnvironmentVariable(\\"username\\", \\"a85\\");","postman.setEnvironmentVariable(\\"repository\\", \\"Newman\\")"]}},{"listen":"prerequest","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");\\r\\nconsole.log(\'hi\')"}}]}]},{"id":"solo-folder","name":"Solo Folder","item":[{"id":"request-404","name":"404 not found","request":{"description":{"content":"Some stuff I want to say about this request. It\'s in *markdown* too.","type":"text/markdown","version":"1.2.3+hi"},"url":{"description":"This is a nice URL.","protocol":"https","port":"8443","path":"path/to/document","host":"sub.example.com."},"auth":{"type":"hawk","basic":{"username":"yosam","password":"asdhjajsd"},"hawk":{"authKey":"asjehcgfdjyrggucgn"}},"header":[{"key":"Access-Control-Allow-Credentials","value":"true","description":"Setting this header to \'true\' means that the server allows cookies (or other user credentials) to be included on cross-origin requests."},{"key":"Server","value":"nginx","description":"Server Name"}]},"event":[{"listen":"test","script":{"type":"text/javascript","exec":["postman.setEnvironmentVariable(\\"username\\", \\"a85\\");","postman.setEnvironmentVariable(\\"repository\\", \\"Newman\\")"]}},{"listen":"prerequest","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");\\r\\nconsole.log(\'hi\')"}}]}]}]}]}],"protocolProfileBehavior":{"disableBodyPruning":false}}'; +const POSTMAN_COLLECTION = + '{"info":{"name":"HTTP Status Messages","id":"my-collection-id","_postman_schema":"https://schema.getpostman.com/#2.0.0","version":{"major":"2","minor":"0","patch":"0","prerelease":"draft.1"}},"variable":[{"id":"var-1","type":"string","value":"hello-world"},{"id":"var-2","type":"number","value":"123"},{"id":"var-3","type":"boolean","value":"true"},{"id":"var-4","type":"any","value":"hello-world-any"},{"id":"var-5","value":null}],"event":[{"listen":"test","id":"my-global-script-1","script":{"type":"text/javascript","exec":["const package1 = pm.require(\\"package1\\");","console.log(\\"hello\\", package1);"],"packages":{"package1":{"id":"script-package-1"}}}},{"listen":"prerequest","script":{"type":"text/javascript","exec":["const package1 = pm.require(\\"package1\\");","console.log(\\"hello\\", package1);"],"packages":{"package1":{"id":"script-package-1"}}}}],"item":[{"id":"request-200","description":{"content":"

This is H1

italic ","version":"2.0.1-abc+efg"},"name":"200 ok","request":"http://echo.getpostman.com/status/200","response":[{"name":"a sample response","originalRequest":"http://echo.getpostman.com/status/200","status":"200 OK","code":200,"header":"Content-Type: application/json\\r\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\r\\n","cookie":[{"domain":".httpbin.org","expires":1502442248,"hostOnly":false,"httpOnly":false,"key":"_ga","path":"/","secure":false,"session":false,"_postman_storeId":"0","value":"GA1.2.113558537.1435817423"}],"body":"response body"}],"event":[{"listen":"test","script":"my-global-script-1"},{"listen":"test","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");"}}],"proxy":{"match":"https://*.getpostman.com/*","server":"https://proxy.com"},"protocolProfileBehavior":{"disableBodyPruning":true}},{"id":"request-200-post","description":{"content":"

This is H1

italic ","version":"2.0.1-abc+efg"},"name":"200 ok","request":{"description":{"content":"my description","type":"text/markdown"},"method":"POST","url":"http://echo.getpostman.com/post","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"urlencoded","urlencoded":[{"key":"yo","value":"mate"}]}},"response":[{"name":"a sample response","originalRequest":"http://echo.getpostman.com/post","status":"200 OK","code":200,"header":"Content-Type: application/json\\r\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\r\\n","cookie":[{"domain":".httpbin.org","expires":1502442248,"hostOnly":false,"httpOnly":false,"key":"_ga","path":"/","secure":false,"session":false,"_postman_storeId":"0","value":"GA1.2.113558537.1435817423"}],"body":"response body"}],"event":[{"listen":"test","script":"my-global-script-1"},{"listen":"test","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");"}}]},{"name":"This is a folder","id":"my-folder-1","_my_meta":"hello","item":[{"id":"request-200","name":"201","request":{"url":"http://echo.getpostman.com/status/201","method":"PUT","body":{"mode":"urlencoded","urlencoded":[{"key":"yo","value":"mate"}]},"header":"Content-Type: application/json\\nAuthorization: Hawk id=\\"dh37fgj492je\\", ts=\\"1448549987\\", nonce=\\"eOJZCd\\", mac=\\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\\"\\n"}},{"id":"request-post","name":"201","request":{"url":"http://echo.getpostman.com/post","body":{"mode":"raw","raw":"blahblah"},"auth":{"type":"basic","basic":{"username":"yosam","password":"asdhjajsd"}}}},{"id":"request-400","name":"400 bad request","request":"http://shamasis:pass@echo.getpostman.com:9443/status/400/?query=string&a=b&abcd#{{search}}"},{"id":"This is a sub folder","name":"my-folder-2","item":[{"id":"blank-folder","name":"This is a blank","item":[{"id":"request-gg","name":"gg not found","request":{"description":{"content":"Some stuff I want to say about this request. It\'s in *markdown* too.","type":"text/markdown","version":"1.2.3+hi"},"url":{"description":"This is a nice URL.","protocol":"https","port":"8443","path":"path/to/document","host":"sub.example.com."},"header":[{"key":"Host","value":"sub.example.com"},{"key":"Content-Type","value":"application/json"}]},"event":[{"listen":"test","script":{"type":"text/javascript","exec":["postman.setEnvironmentVariable(\\"username\\", \\"a85\\");","postman.setEnvironmentVariable(\\"repository\\", \\"Newman\\")"]}},{"listen":"prerequest","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");\\r\\nconsole.log(\'hi\')"}}]}]},{"id":"solo-folder","name":"Solo Folder","item":[{"id":"request-404","name":"404 not found","request":{"description":{"content":"Some stuff I want to say about this request. It\'s in *markdown* too.","type":"text/markdown","version":"1.2.3+hi"},"url":{"description":"This is a nice URL.","protocol":"https","port":"8443","path":"path/to/document","host":"sub.example.com."},"auth":{"type":"hawk","basic":{"username":"yosam","password":"asdhjajsd"},"hawk":{"authKey":"asjehcgfdjyrggucgn"}},"header":[{"key":"Access-Control-Allow-Credentials","value":"true","description":"Setting this header to \'true\' means that the server allows cookies (or other user credentials) to be included on cross-origin requests."},{"key":"Server","value":"nginx","description":"Server Name"}]},"event":[{"listen":"test","script":{"type":"text/javascript","exec":["postman.setEnvironmentVariable(\\"username\\", \\"a85\\");","postman.setEnvironmentVariable(\\"repository\\", \\"Newman\\")"]}},{"listen":"prerequest","script":{"type":"text/javascript","exec":"console.log(\\"hello\\");\\r\\nconsole.log(\'hi\')"}}]}]}]}]}],"protocolProfileBehavior":{"disableBodyPruning":false}}'; const POSTMAN_COLLECTION_FILE_PATH = path.join(tmpdir(), 'postman-collection.json'); jest.mock('main/persistence/service/persistence-service'); describe('ImportService', () => { it('should import a Postman collection', async () => { - // Arrange const targetDirPath = tmpdir(); const importService = ImportService.instance; await fs.writeFile(POSTMAN_COLLECTION_FILE_PATH, POSTMAN_COLLECTION); - (PersistenceService.instance.saveCollectionRecursive as jest.Mock).mockImplementation(async () => null); + (PersistenceService.instance.saveCollectionRecursive as jest.Mock).mockImplementation( + async () => null + ); // Act - const result = await importService.importCollection(POSTMAN_COLLECTION_FILE_PATH, targetDirPath, 'Postman'); + const result = await importService.importCollection( + POSTMAN_COLLECTION_FILE_PATH, + targetDirPath, + 'Postman' + ); // Assert expect(result.type).toBe('collection'); @@ -44,11 +50,13 @@ describe('ImportService', () => { expect(secondChild.title).toBe('200 ok'); expect(secondChild.url).toBe('http://echo.getpostman.com/post'); expect(secondChild.method).toBe('POST'); - expect(secondChild.headers).toEqual([{ - 'isActive': true, - 'key': 'Content-Type', - 'value': 'application/json', - }]); + expect(secondChild.headers).toEqual([ + { + isActive: true, + key: 'Content-Type', + value: 'application/json', + }, + ]); expect(secondChild.body).toEqual(null); const thirdChild = childrenLevel1[2] as Folder; @@ -63,15 +71,19 @@ describe('ImportService', () => { expect(firstChildLevel2.title).toBe('201'); expect(firstChildLevel2.url).toBe('http://echo.getpostman.com/status/201'); expect(firstChildLevel2.method).toBe('PUT'); - expect(firstChildLevel2.headers).toEqual([{ - 'isActive': true, - 'key': 'Content-Type', - 'value': 'application/json', - }, { - 'isActive': true, - 'key': 'Authorization', - 'value': 'Hawk id="dh37fgj492je", ts="1448549987", nonce="eOJZCd", mac="O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k="', - }]); + expect(firstChildLevel2.headers).toEqual([ + { + isActive: true, + key: 'Content-Type', + value: 'application/json', + }, + { + isActive: true, + key: 'Authorization', + value: + 'Hawk id="dh37fgj492je", ts="1448549987", nonce="eOJZCd", mac="O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k="', + }, + ]); expect(firstChildLevel2.body).toEqual(null); const secondChildLevel2 = childrenLevel2[1] as RufusRequest; @@ -81,10 +93,9 @@ describe('ImportService', () => { expect(secondChildLevel2.method).toBe('GET'); expect(secondChildLevel2.headers).toEqual([]); expect(secondChildLevel2.body).toEqual({ - 'mimeType': 'text/plain', - 'text': 'blahblah', - 'type': 'text', + mimeType: 'text/plain', + text: 'blahblah', + type: 'text', }); }); }); - diff --git a/src/main/import/service/import-service.ts b/src/main/import/service/import-service.ts index c38d467b..690b3360 100644 --- a/src/main/import/service/import-service.ts +++ b/src/main/import/service/import-service.ts @@ -12,7 +12,6 @@ export interface CollectionImporter { const persistenceService = PersistenceService.instance; export class ImportService { - public static readonly instance = new ImportService(); static { @@ -25,17 +24,25 @@ export class ImportService { this.importers.set(strategy, importer); } - public async importCollection(srcFilePath: string, targetDirPath: string, strategy: ImportStrategy) { + public async importCollection( + srcFilePath: string, + targetDirPath: string, + strategy: ImportStrategy + ) { const importer = this.importers.get(strategy); if (importer === undefined) { - throw new InternalError(InternalErrorType.UNSUPPORTED_IMPORT_STRATEGY, `No importer registered for strategy "${strategy}"`); + throw new InternalError( + InternalErrorType.UNSUPPORTED_IMPORT_STRATEGY, + `No importer registered for strategy "${strategy}"` + ); } - console.info(`Importing collection from "${srcFilePath}" to "${targetDirPath}" using strategy "${strategy}"`); + console.info( + `Importing collection from "${srcFilePath}" to "${targetDirPath}" using strategy "${strategy}"` + ); const collection = await importer.importCollection(srcFilePath, targetDirPath); console.info('Successfully imported collection:', collection); await persistenceService.saveCollectionRecursive(collection); return collection; } - -} \ No newline at end of file +} diff --git a/src/main/import/service/postman-importer.ts b/src/main/import/service/postman-importer.ts index a7aa30b9..e8bdbdd2 100644 --- a/src/main/import/service/postman-importer.ts +++ b/src/main/import/service/postman-importer.ts @@ -20,37 +20,39 @@ import { VARIABLE_NAME_REGEX, VariableObject } from 'shim/variables'; * folders and requests. It imports using the DFS algorithm. */ export class PostmanImporter implements CollectionImporter { - public async importCollection(srcFilePath: string, targetDirPath: string) { - // read Postman collection const json = JSON.parse(await fs.readFile(srcFilePath, 'utf8')) as CollectionDefinition; const postmanCollection = new PostmanCollection(json); - const variablesArray = - postmanCollection.variables + const variablesArray = postmanCollection.variables .all() - .filter(variable => variable.id !== undefined && VARIABLE_NAME_REGEX.test(variable.id)) - .map(variable => - [ - variable.id, - { - value: variable.toString(), - enabled: !variable.disabled, - }, - ] as [string, VariableObject]); + .filter((variable) => variable.id !== undefined && VARIABLE_NAME_REGEX.test(variable.id)) + .map( + (variable) => + [ + variable.id, + { + value: variable.toString(), + enabled: !variable.disabled, + }, + ] as [string, VariableObject] + ); console.info('Loaded', variablesArray.length, 'collection variables'); // create collection directory const dirName = path.basename(targetDirPath); const dirPath = path.join(targetDirPath, dirName); if (await exists(dirPath)) { - throw new InternalError(InternalErrorType.COLLECTION_LOAD_ERROR, `Directory "${dirPath}" already exists`); + throw new InternalError( + InternalErrorType.COLLECTION_LOAD_ERROR, + `Directory "${dirPath}" already exists` + ); } else { await fs.mkdir(dirPath); } const variables: Record = {}; - variablesArray.forEach(([key, val]) => variables[key] = val); + variablesArray.forEach(([key, val]) => (variables[key] = val)); const collection: RufusCollection = { id: postmanCollection.id, @@ -66,7 +68,10 @@ export class PostmanImporter implements CollectionImporter { return collection; } - private async importItems(parent: RufusCollection | RufusFolder, items: (Item | ItemGroup)[]) { + private async importItems( + parent: RufusCollection | RufusFolder, + items: (Item | ItemGroup)[] + ) { for (const item of items) { if (item instanceof ItemGroup) { await this.importFolder(parent, item); @@ -76,7 +81,10 @@ export class PostmanImporter implements CollectionImporter { } } - private async importFolder(parent: RufusCollection | RufusFolder, postmanFolder: ItemGroup) { + private async importFolder( + parent: RufusCollection | RufusFolder, + postmanFolder: ItemGroup + ) { const folder: RufusFolder = { id: postmanFolder.id, parentId: parent.id, @@ -118,7 +126,7 @@ export class PostmanImporter implements CollectionImporter { title: item.name, url: request.url.toString(), method: request.method as RequestMethod, - headers: request.headers.all().map(header => ({ + headers: request.headers.all().map((header) => ({ key: header.key, value: header.value, isActive: !header.disabled, diff --git a/src/main/main.ts b/src/main/main.ts index b5684ff1..6f3dc442 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -3,7 +3,6 @@ import 'main/event/main-event-service'; import { EnvironmentService } from 'main/environment/service/environment-service'; import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'; - // This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack // plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on // whether you're running in development or production). @@ -26,8 +25,8 @@ const createWindow = async () => { minWidth: 1024, minHeight: 728, webPreferences: { - preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY - } + preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, + }, }); // Open links in default browser diff --git a/src/main/menu.ts b/src/main/menu.ts index ba0fb770..fcb15c8d 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -1,10 +1,4 @@ -import { - app, - Menu, - shell, - BrowserWindow, - MenuItemConstructorOptions, -} from 'electron'; +import { app, Menu, shell, BrowserWindow, MenuItemConstructorOptions } from 'electron'; interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { selector?: string; @@ -19,17 +13,12 @@ export default class MenuBuilder { } buildMenu(): Menu { - if ( - process.env.NODE_ENV === 'development' || - process.env.DEBUG_PROD === 'true' - ) { + if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { this.setupDevelopmentEnvironment(); } const template = - process.platform === 'darwin' - ? this.buildDarwinTemplate() - : this.buildDefaultTemplate(); + process.platform === 'darwin' ? this.buildDarwinTemplate() : this.buildDefaultTemplate(); const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); @@ -163,9 +152,7 @@ export default class MenuBuilder { { label: 'Documentation', click() { - shell.openExternal( - 'https://github.com/electron/electron/tree/main/docs#readme', - ); + shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme'); }, }, { @@ -184,8 +171,7 @@ export default class MenuBuilder { }; const subMenuView = - process.env.NODE_ENV === 'development' || - process.env.DEBUG_PROD === 'true' + process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true' ? subMenuViewDev : subMenuViewProd; @@ -213,8 +199,7 @@ export default class MenuBuilder { { label: '&View', submenu: - process.env.NODE_ENV === 'development' || - process.env.DEBUG_PROD === 'true' + process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true' ? [ { label: '&Reload', @@ -227,9 +212,7 @@ export default class MenuBuilder { label: 'Toggle &Full Screen', accelerator: 'F11', click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen(), - ); + this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); }, }, { @@ -245,9 +228,7 @@ export default class MenuBuilder { label: 'Toggle &Full Screen', accelerator: 'F11', click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen(), - ); + this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); }, }, ], @@ -264,9 +245,7 @@ export default class MenuBuilder { { label: 'Documentation', click() { - shell.openExternal( - 'https://github.com/electron/electron/tree/main/docs#readme', - ); + shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme'); }, }, { diff --git a/src/main/network/service/http-service.test.ts b/src/main/network/service/http-service.test.ts index 96bc6f2d..99f55ce6 100644 --- a/src/main/network/service/http-service.test.ts +++ b/src/main/network/service/http-service.test.ts @@ -8,7 +8,6 @@ import { IncomingHttpHeaders } from 'undici/types/header'; describe('HttpService', () => { it('fetchAsync should make an HTTP call and return the body on read', async () => { - // Arrange const text = 'Hello, world!'; const url = new URL('https://example.com/api/data'); @@ -47,7 +46,12 @@ describe('HttpService', () => { 'content-length': '5000', }; const url = new URL('https://example.com/api/data'); - const httpService = setupMockHttpService(url, RequestMethod.get, responseBodyMock, responseHeadersMock); + const httpService = setupMockHttpService( + url, + RequestMethod.get, + responseBodyMock, + responseHeadersMock + ); const request: RufusRequest = { id: randomUUID(), parentId: randomUUID(), @@ -69,10 +73,14 @@ describe('HttpService', () => { totalSizeInBytes: 5054, }); }); - }); -function setupMockHttpService(url: URL, method: RequestMethod, body: object | string | null, headers?: IncomingHttpHeaders) { +function setupMockHttpService( + url: URL, + method: RequestMethod, + body: object | string | null, + headers?: IncomingHttpHeaders +) { let bodyString; switch (typeof body) { case 'string': diff --git a/src/main/network/service/http-service.ts b/src/main/network/service/http-service.ts index 93e226fe..40d2e17b 100644 --- a/src/main/network/service/http-service.ts +++ b/src/main/network/service/http-service.ts @@ -19,7 +19,6 @@ const persistenceService = PersistenceService.instance; * Singleton service for making HTTP requests */ export class HttpService { - public static readonly instance = new HttpService(); private readonly _dispatcher?: Dispatcher; @@ -39,15 +38,15 @@ export class HttpService { const now = getSteadyTimestamp(); const body = await this.readBody(request); - const responseData = await undici.request( - request.url, - { - dispatcher: this._dispatcher, - method: request.method, - headers: { ['content-type']: this.getContentType(request), ...this.rufusHeadersToUndiciHeaders(request.headers) }, - body: body, + const responseData = await undici.request(request.url, { + dispatcher: this._dispatcher, + method: request.method, + headers: { + ['content-type']: this.getContentType(request), + ...this.rufusHeadersToUndiciHeaders(request.headers), }, - ); + body: body, + }); const duration = getDurationFromNow(now); console.info(`Received response in ${duration} milliseconds:`, responseData); @@ -65,7 +64,10 @@ export class HttpService { metaInfo: { status: responseData.statusCode, duration: duration, - size: calculateResponseSize(responseData.headers, responseData.body != null ? bodyFile.name : null), + size: calculateResponseSize( + responseData.headers, + responseData.body != null ? bodyFile.name : null + ), }, headers: Object.freeze(responseData.headers), bodyFilePath: responseData.body != null ? bodyFile.name : null, @@ -124,4 +126,3 @@ export class HttpService { return headers; } } - diff --git a/src/main/persistence/service/info-files.ts b/src/main/persistence/service/info-files.ts index 85804627..d92bc8e5 100644 --- a/src/main/persistence/service/info-files.ts +++ b/src/main/persistence/service/info-files.ts @@ -11,18 +11,18 @@ export type RequestInfoFile = { method: RequestMethod; headers: RufusHeader[]; body: RequestBody; -} +}; export type FolderInfoFile = { version: string; title: string; -} +}; export type CollectionInfoFile = { version: string; title: string; variables: Record; -} +}; export type InfoFile = RequestInfoFile | FolderInfoFile | CollectionInfoFile; diff --git a/src/main/persistence/service/persistence-service.test.ts b/src/main/persistence/service/persistence-service.test.ts index 596f51b8..ea0a8f38 100644 --- a/src/main/persistence/service/persistence-service.test.ts +++ b/src/main/persistence/service/persistence-service.test.ts @@ -66,7 +66,6 @@ async function streamToString(stream: Readable) { } describe('PersistenceService', () => { - let collection: Collection; beforeEach(async () => { collection = getExampleCollection(); @@ -76,7 +75,9 @@ describe('PersistenceService', () => { it('loadDefaultCollection() should return the existing default collection if it exists', async () => { // Arrange const defaultCollection = {} as Collection; - const loadCollectionSpy = jest.spyOn(persistenceService, 'loadCollection').mockResolvedValueOnce(defaultCollection); + const loadCollectionSpy = jest + .spyOn(persistenceService, 'loadCollection') + .mockResolvedValueOnce(defaultCollection); await mkdir(collectionDirPath); await writeFile(path.join(collectionDirPath, 'collection.json'), ''); @@ -92,7 +93,9 @@ describe('PersistenceService', () => { // Arrange const defaultCollection = {} as Collection; jest.mocked(generateDefaultCollection).mockReturnValueOnce(defaultCollection); - const saveCollectionRecursiveSpy = jest.spyOn(persistenceService, 'saveCollectionRecursive').mockResolvedValueOnce(); + const saveCollectionRecursiveSpy = jest + .spyOn(persistenceService, 'saveCollectionRecursive') + .mockResolvedValueOnce(); // Act const result = await persistenceService.loadDefaultCollection(); @@ -168,7 +171,9 @@ describe('PersistenceService', () => { await persistenceService.saveCollectionRecursive(collection); - const oldInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; + const oldInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; request.method = RequestMethod.put; // Assert @@ -178,7 +183,9 @@ describe('PersistenceService', () => { await persistenceService.saveRequest(request); // Assert - const newInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; + const newInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; expect(newInfo.method).toBe(request.method); expect(newInfo).not.toEqual(oldInfo); }); @@ -223,7 +230,9 @@ describe('PersistenceService', () => { // Assert expect(await exists(path.join(collection.dirPath, 'collection.json'))).toBe(true); expect(await exists(path.join(collection.dirPath, folder.title, 'folder.json'))).toBe(true); - expect(await exists(path.join(collection.dirPath, folder.title, request.title, 'request.json'))).toBe(true); + expect( + await exists(path.join(collection.dirPath, folder.title, request.title, 'request.json')) + ).toBe(true); }); it('saveChanges() should overwrite the request metadata with its draft metadata', async () => { @@ -237,8 +246,12 @@ describe('PersistenceService', () => { request.method = RequestMethod.post; await persistenceService.saveRequest(request); - let originalInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; - const draftInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, '~request.json'), 'utf-8')) as RequestInfoFile; + let originalInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; + const draftInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, '~request.json'), 'utf-8') + ) as RequestInfoFile; // Assert expect(await exists(path.join(collection.dirPath, request.title, 'request.json'))).toBe(true); @@ -251,7 +264,9 @@ describe('PersistenceService', () => { // Assert expect(await exists(path.join(collection.dirPath, request.title, 'request.json'))).toBe(true); expect(await exists(path.join(collection.dirPath, request.title, '~request.json'))).toBe(false); - originalInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; + originalInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; expect(originalInfo).toEqual(draftInfo); }); @@ -266,7 +281,9 @@ describe('PersistenceService', () => { request.method = RequestMethod.post; await persistenceService.saveRequest(request); - const oldInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; + const oldInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; // Assert expect(await exists(path.join(collection.dirPath, request.title, 'request.json'))).toBe(true); @@ -278,7 +295,9 @@ describe('PersistenceService', () => { // Assert expect(await exists(path.join(collection.dirPath, request.title, 'request.json'))).toBe(true); expect(await exists(path.join(collection.dirPath, request.title, '~request.json'))).toBe(false); - const newInfo = JSON.parse(await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8')) as RequestInfoFile; + const newInfo = JSON.parse( + await readFile(path.join(collection.dirPath, request.title, 'request.json'), 'utf-8') + ) as RequestInfoFile; expect(oldInfo).toEqual(newInfo); }); @@ -309,8 +328,12 @@ describe('PersistenceService', () => { await persistenceService.saveRequest(request, textBody); // Assert - expect(await exists(path.join(collection.dirPath, request.title, TEXT_BODY_FILE_NAME))).toBe(true); - expect(await exists(path.join(collection.dirPath, request.title, DRAFT_TEXT_BODY_FILE_NAME))).toBe(false); + expect(await exists(path.join(collection.dirPath, request.title, TEXT_BODY_FILE_NAME))).toBe( + true + ); + expect( + await exists(path.join(collection.dirPath, request.title, DRAFT_TEXT_BODY_FILE_NAME)) + ).toBe(false); // Act const result = await persistenceService.loadTextBodyOfRequest(request); @@ -330,8 +353,12 @@ describe('PersistenceService', () => { await persistenceService.saveRequest(request, textBody); // Assert - expect(await exists(path.join(collection.dirPath, request.title, TEXT_BODY_FILE_NAME))).toBe(false); - expect(await exists(path.join(collection.dirPath, request.title, DRAFT_TEXT_BODY_FILE_NAME))).toBe(true); + expect(await exists(path.join(collection.dirPath, request.title, TEXT_BODY_FILE_NAME))).toBe( + false + ); + expect( + await exists(path.join(collection.dirPath, request.title, DRAFT_TEXT_BODY_FILE_NAME)) + ).toBe(true); // Act const result = await persistenceService.loadTextBodyOfRequest(request); @@ -358,4 +385,4 @@ describe('PersistenceService', () => { result.children[0].parentId = collection.id; expect(result).toEqual(collection); }); -}); \ No newline at end of file +}); diff --git a/src/main/persistence/service/persistence-service.ts b/src/main/persistence/service/persistence-service.ts index 27a0a83c..e59dede1 100644 --- a/src/main/persistence/service/persistence-service.ts +++ b/src/main/persistence/service/persistence-service.ts @@ -22,10 +22,7 @@ import { randomUUID } from 'node:crypto'; * to and from the file system. */ export class PersistenceService { - private static readonly DEFAULT_COLLECTION_DIR = path.join( - USER_DATA_DIR, - 'default-collection', - ); + private static readonly DEFAULT_COLLECTION_DIR = path.join(USER_DATA_DIR, 'default-collection'); public static readonly instance = new PersistenceService(); @@ -56,7 +53,7 @@ export class PersistenceService { public async moveChild( child: Folder | RufusRequest, oldParent: Folder | Collection, - newParent: Folder | Collection, + newParent: Folder | Collection ) { const childDirName = this.getDirName(child); const oldChildDirPath = this.getDirPath(child); @@ -140,7 +137,7 @@ export class PersistenceService { * @param fileName the name of the information file */ private async saveInfoFile(object: RufusObject, dirPath: string, fileName: string) { - console.info('Saving object', object.id ??= randomUUID()); + console.info('Saving object', (object.id ??= randomUUID())); if (!isCollection(object) && object.parentId == null) { throw new Error('Object must have a parent'); } else if (!(await exists(dirPath))) { @@ -181,7 +178,7 @@ export class PersistenceService { request.draft = false; const infoFileName = request.type + '.json'; const dirPath = this.getDirPath(request); - if (!await exists(path.join(dirPath, '~' + infoFileName))) { + if (!(await exists(path.join(dirPath, '~' + infoFileName)))) { return { draft: false }; } @@ -227,7 +224,7 @@ export class PersistenceService { // delete draft files await fs.unlink(path.join(dirPath, '~' + infoFileName)); - if (isRequest(request) && await exists(path.join(dirPath, DRAFT_TEXT_BODY_FILE_NAME))) { + if (isRequest(request) && (await exists(path.join(dirPath, DRAFT_TEXT_BODY_FILE_NAME)))) { await fs.unlink(path.join(dirPath, DRAFT_TEXT_BODY_FILE_NAME)); } @@ -283,7 +280,9 @@ export class PersistenceService { public async loadCollection(dirPath: string): Promise { const type = 'collection' as const; const infoFileName = type + '.json'; - const infoFileContents = await JSON.parse(await fs.readFile(path.join(dirPath, infoFileName), 'utf8')) as CollectionInfoFile; + const infoFileContents = (await JSON.parse( + await fs.readFile(path.join(dirPath, infoFileName), 'utf8') + )) as CollectionInfoFile; delete infoFileContents.version; const id = randomUUID(); this.idToPathMap.set(id, dirPath); @@ -296,18 +295,17 @@ export class PersistenceService { }); } - private async loadRequest( - parentId: string, - dirPath: string, - ): Promise { + private async loadRequest(parentId: string, dirPath: string): Promise { const type = 'request' as const; let infoFileName = type + '.json'; let draft = false; - if (!await exists(path.join(dirPath, infoFileName))) { + if (!(await exists(path.join(dirPath, infoFileName)))) { infoFileName = '~' + infoFileName; draft = true; } - const infoFileContents = await JSON.parse(await fs.readFile(path.join(dirPath, infoFileName), 'utf8')) as RequestInfoFile; + const infoFileContents = (await JSON.parse( + await fs.readFile(path.join(dirPath, infoFileName), 'utf8') + )) as RequestInfoFile; delete infoFileContents.version; const id = randomUUID(); this.idToPathMap.set(id, dirPath); @@ -323,7 +321,9 @@ export class PersistenceService { private async loadFolder(parentId: string, dirPath: string): Promise { const type = 'folder' as const; const infoFileName = type + '.json'; - const infoFileContents = await JSON.parse(await fs.readFile(path.join(dirPath, infoFileName), 'utf8')) as FolderInfoFile; + const infoFileContents = (await JSON.parse( + await fs.readFile(path.join(dirPath, infoFileName), 'utf8') + )) as FolderInfoFile; delete infoFileContents.version; const id = randomUUID(); this.idToPathMap.set(id, dirPath); @@ -338,7 +338,7 @@ export class PersistenceService { private async loadChildren( parentId: string, - parentDirPath: string, + parentDirPath: string ): Promise<(Folder | RufusRequest)[]> { const children: (Folder | RufusRequest)[] = []; @@ -356,20 +356,25 @@ export class PersistenceService { } // TODO: simplify type detection - private async load(parentId: string, dirPath: string, type?: T['type']): Promise { - if (type === 'folder' || await exists(path.join(dirPath, 'folder.json'))) { - return await this.loadFolder(parentId, dirPath) as T; - } else if (type === 'request' || await exists(path.join(dirPath, 'request.json')) || await exists(path.join(dirPath, '~request.json'))) { - return await this.loadRequest(parentId, dirPath) as T; + private async load( + parentId: string, + dirPath: string, + type?: T['type'] + ): Promise { + if (type === 'folder' || (await exists(path.join(dirPath, 'folder.json')))) { + return (await this.loadFolder(parentId, dirPath)) as T; + } else if ( + type === 'request' || + (await exists(path.join(dirPath, 'request.json'))) || + (await exists(path.join(dirPath, '~request.json'))) + ) { + return (await this.loadRequest(parentId, dirPath)) as T; } throw new Error(`Could not determine type of object at "${dirPath}"`); } - private updatePathMapRecursively( - child: Folder | RufusRequest, - newParentDirPath: string, - ) { + private updatePathMapRecursively(child: Folder | RufusRequest, newParentDirPath: string) { const oldDirPath = this.idToPathMap.get(child.id); const dirName = path.basename(oldDirPath); const newDirPath = path.join(newParentDirPath, dirName); @@ -399,8 +404,8 @@ export class PersistenceService { private sanitizeTitle(title: string) { return title - .toLowerCase() - .replace(/\s/g, '-') - .replace(/[^a-z0-9-]/g, ''); + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^a-z0-9-]/g, ''); } } diff --git a/src/main/preload.ts b/src/main/preload.ts index 84ade0eb..55efa215 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -1,6 +1,6 @@ // Disable no-unused-vars, broken for spread args /* eslint no-unused-vars: off */ -import {contextBridge, ipcRenderer} from 'electron'; +import { contextBridge, ipcRenderer } from 'electron'; const electronHandler = { ipcRenderer: { diff --git a/src/main/shared/initializable.ts b/src/main/shared/initializable.ts index 510baee9..d5a76182 100644 --- a/src/main/shared/initializable.ts +++ b/src/main/shared/initializable.ts @@ -8,4 +8,4 @@ export interface Initializable { */ export function isInitializable(object: any): object is Initializable { return 'init' in object && typeof object.init === 'function'; -} \ No newline at end of file +} diff --git a/src/main/util/fs-util.ts b/src/main/util/fs-util.ts index 611b09b7..c74feb05 100644 --- a/src/main/util/fs-util.ts +++ b/src/main/util/fs-util.ts @@ -14,4 +14,4 @@ export async function exists(filePath: string) { return false; } return true; -} \ No newline at end of file +} diff --git a/src/main/util/size-calculation.test.ts b/src/main/util/size-calculation.test.ts index d3179055..b5f7db97 100644 --- a/src/main/util/size-calculation.test.ts +++ b/src/main/util/size-calculation.test.ts @@ -49,7 +49,10 @@ describe('calculateResponseSize', () => { }); it('calculates size correctly with multiple header values', () => { - const headers = { 'set-cookie': ['cookie1=value1', 'cookie2=value2'], 'content-type': 'application/json' }; + const headers = { + 'set-cookie': ['cookie1=value1', 'cookie2=value2'], + 'content-type': 'application/json', + }; const result = calculateResponseSize(headers); expect(result).toEqual({ totalSizeInBytes: 76, @@ -60,17 +63,20 @@ describe('calculateResponseSize', () => { it('calculates size of header correctly for several cases combined', () => { const headers = { - 'Date': 'Tue, 22 Oct 2024 08:47:51 GMT', - 'Expires': '-1', + Date: 'Tue, 22 Oct 2024 08:47:51 GMT', + Expires: '-1', 'Cache-Control': ['private', 'max-age=0'], 'Content-Type': 'text/html; charset=ISO-8859-1', - 'Content-Security-Policy-Report-Only': 'object-src \'none\';base-uri \'self\';script-src \'nonce-PiU4uUsxJHMfI86u0fq6eA\' \'strict-dynamic\' \'report-sample\' \'unsafe-eval\' \'unsafe-inline\' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp', - 'Server': 'gws', + 'Content-Security-Policy-Report-Only': + "object-src 'none';base-uri 'self';script-src 'nonce-PiU4uUsxJHMfI86u0fq6eA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp", + Server: 'gws', 'X-XSS-Protection': '0', 'X-Frame-Options': 'SAMEORIGIN', - 'Set-Cookie': ['AEC=AVYB7cpFDNfvuiVCfhVB5CNosUmOPykRrb9AaXhY-Erip_-YyWGHFC5gPMc; expires=Sun, 20-Apr-2025 08:47:51 GMT; path=/; domain=.google.de; Secure; HttpOnly; SameSite=lax'], + 'Set-Cookie': [ + 'AEC=AVYB7cpFDNfvuiVCfhVB5CNosUmOPykRrb9AaXhY-Erip_-YyWGHFC5gPMc; expires=Sun, 20-Apr-2025 08:47:51 GMT; path=/; domain=.google.de; Secure; HttpOnly; SameSite=lax', + ], 'Accept-Ranges': 'none', - 'Vary': 'Accept-Encoding', + Vary: 'Accept-Encoding', 'Transfer-Encoding': 'chunked', }; const result = calculateResponseSize(headers); @@ -80,4 +86,4 @@ describe('calculateResponseSize', () => { bodySizeInBytes: 0, }); }); -}); \ No newline at end of file +}); diff --git a/src/main/util/size-calculation.ts b/src/main/util/size-calculation.ts index 5171570a..489d7c57 100644 --- a/src/main/util/size-calculation.ts +++ b/src/main/util/size-calculation.ts @@ -7,7 +7,10 @@ import fs from 'node:fs'; * @param headers response headers * @param filePath path to the response body file */ -export function calculateResponseSize(headers: IncomingHttpHeaders, filePath?: string): ResponseSize { +export function calculateResponseSize( + headers: IncomingHttpHeaders, + filePath?: string +): ResponseSize { const headersSizeInBytes = calculateHeadersSize(headers); const bodySizeInBytes = calculateBodySize(headers, filePath); const totalSizeInBytes = bodySizeInBytes + headersSizeInBytes; @@ -19,16 +22,18 @@ function calculateHeadersSize(headers: IncomingHttpHeaders) { const COLON_SIZE = 1; const SPACE_SIZE = 1; const COMMA_SIZE = 1; - return Object.entries(headers).reduce( - (size, [key, values]) => { - let valuesSize; - if (Array.isArray(values)) { - valuesSize = values.reduce((valueSizeTmp, value) => valueSizeTmp + value.length + COMMA_SIZE + SPACE_SIZE, -(COMMA_SIZE + SPACE_SIZE)); - } else { - valuesSize = values.length; - } - return size + key.length + valuesSize + COLON_SIZE + SPACE_SIZE + NEW_LINE_SIZE; - }, 0); + return Object.entries(headers).reduce((size, [key, values]) => { + let valuesSize; + if (Array.isArray(values)) { + valuesSize = values.reduce( + (valueSizeTmp, value) => valueSizeTmp + value.length + COMMA_SIZE + SPACE_SIZE, + -(COMMA_SIZE + SPACE_SIZE) + ); + } else { + valuesSize = values.length; + } + return size + key.length + valuesSize + COLON_SIZE + SPACE_SIZE + NEW_LINE_SIZE; + }, 0); } function calculateBodySize(headers: IncomingHttpHeaders, filePath?: string) { @@ -40,4 +45,4 @@ function calculateBodySize(headers: IncomingHttpHeaders, filePath?: string) { if (filePath == null) return 0; return fs.statSync(filePath).size; -} \ No newline at end of file +} diff --git a/src/main/util/time-util.ts b/src/main/util/time-util.ts index 48d2ea1e..020e44c4 100644 --- a/src/main/util/time-util.ts +++ b/src/main/util/time-util.ts @@ -1,4 +1,4 @@ -declare type TimeUnit = "ns" | "us" | "ms" | "s"; +declare type TimeUnit = 'ns' | 'us' | 'ms' | 's'; /** * Returns a timestamp that can be used to measure time intervals. @@ -12,17 +12,17 @@ export function getSteadyTimestamp() { * @param timestamp the timestamp to compare to * @param unit the unit to return the difference in. Default is "ms". */ -export function getDurationFromNow(timestamp: bigint, unit = "ms" as TimeUnit) { +export function getDurationFromNow(timestamp: bigint, unit = 'ms' as TimeUnit) { const diff = Number(getSteadyTimestamp() - timestamp); switch (unit) { - case "ns": + case 'ns': return diff; - case "us": + case 'us': return diff / 1e3; - case "ms": + case 'ms': return diff / 1e6; - case "s": + case 's': return diff / 1e9; default: throw new Error(`Unknown unit: ${unit}`); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 3598df1a..b01b7fe5 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,23 +1,22 @@ -import "@/styles/tailwind.css"; -import "@/styles/global.css"; -import {Sidebar} from '@/view/Sidebar'; -import {MainWindow} from "@/view/MainWindow"; -import {Toaster} from "@/components/ui/toaster"; +import '@/styles/tailwind.css'; +import '@/styles/global.css'; +import { Sidebar } from '@/view/Sidebar'; +import { MainWindow } from '@/view/MainWindow'; +import { Toaster } from '@/components/ui/toaster'; // import { Header } from '@/components/Header'; export const App = () => { - return (
{/*
*/}
- - + +
- +
); -} +}; diff --git a/src/renderer/components/icons/Icon.tsx b/src/renderer/components/icons/Icon.tsx index 3fa727f9..9b8df11e 100644 --- a/src/renderer/components/icons/Icon.tsx +++ b/src/renderer/components/icons/Icon.tsx @@ -11,21 +11,14 @@ const Icon: React.FC = ({ children, size = 24, color = 'currentColor', - viewBox = `0 0 ${size*4/3} ${size*4/3}`, + viewBox = `0 0 ${(size * 4) / 3} ${(size * 4) / 3}`, ...props }) => { return ( - - {children} - + + {children} + ); }; export default Icon; - diff --git a/src/renderer/components/icons/index.tsx b/src/renderer/components/icons/index.tsx index 8cf9bb0b..2f1ca751 100644 --- a/src/renderer/components/icons/index.tsx +++ b/src/renderer/components/icons/index.tsx @@ -1,99 +1,129 @@ import React from 'react'; import Icon from './Icon'; -export const MoreIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const MoreIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const AddIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const AddIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const ArrowForwardIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const ArrowForwardIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const BookmarkIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const BookmarkIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const CloseIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const CloseIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const DeleteIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const DeleteIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const InfoIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const InfoIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const SearchIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const SearchIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const SettingsIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const SettingsIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const SwapIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const SwapIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; -export const SmallArrow: React.FC & { size?: number; color?: string; }> = (props) => { +export const SmallArrow: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - - + + + + ); }; -export const CheckedIcon: React.FC & { size?: number; color?: string; }> = (props) => { +export const CheckedIcon: React.FC< + React.SVGProps & { size?: number; color?: string } +> = (props) => { return ( - - - + + + ); }; diff --git a/src/renderer/components/mainWindow/Header.tsx b/src/renderer/components/mainWindow/Header.tsx index d53b9bf9..2de55d83 100644 --- a/src/renderer/components/mainWindow/Header.tsx +++ b/src/renderer/components/mainWindow/Header.tsx @@ -1,34 +1,40 @@ -import {Button} from '@/components/ui/button'; -import {FileIcon, PlusIcon} from '@radix-ui/react-icons'; -import {HttpHeaders} from 'shim/headers'; +import { Button } from '@/components/ui/button'; +import { FileIcon, PlusIcon } from '@radix-ui/react-icons'; +import { HttpHeaders } from 'shim/headers'; export type HeaderProps = { headers?: HttpHeaders; -} +}; export const Header = (props: HeaderProps) => { - const {headers} = props; + const { headers } = props; // TODO: appropriate logic should be provided return (
- ) -} + ); +}; diff --git a/src/renderer/components/mainWindow/MainBody.tsx b/src/renderer/components/mainWindow/MainBody.tsx index c90a283e..56ddaa96 100644 --- a/src/renderer/components/mainWindow/MainBody.tsx +++ b/src/renderer/components/mainWindow/MainBody.tsx @@ -5,15 +5,19 @@ import { HttpHeaders } from '../../../shim/headers'; export type RequestBodyProps = { headers?: HttpHeaders; body?: string; -} +}; export function MainBody(props: RequestBodyProps) { const { body, headers } = props; return (
-
-
+
+ +
+
+ +
); } diff --git a/src/renderer/components/mainWindow/MainTopBar.tsx b/src/renderer/components/mainWindow/MainTopBar.tsx index 21beadc3..8b8131d3 100644 --- a/src/renderer/components/mainWindow/MainTopBar.tsx +++ b/src/renderer/components/mainWindow/MainTopBar.tsx @@ -17,77 +17,95 @@ import { RendererEventService } from '@/services/event/renderer-event-service'; export type RequestProps = { onResponse: (response: RufusResponse) => Promise; -} +}; const httpService = HttpService.instance; const eventService = RendererEventService.instance; export function MainTopBar({ onResponse }: RequestProps) { const dispatch = useDispatch(); - const requestEditor = useSelector(state => state.requests.requestEditor) as editor.ICodeEditor | undefined; - const requestIndex = useSelector(state => state.requests.selectedRequest); - const requests = useSelector(state => state.requests.requests); + const requestEditor = useSelector((state) => state.requests.requestEditor) as + | editor.ICodeEditor + | undefined; + const requestIndex = useSelector((state) => state.requests.selectedRequest); + const requests = useSelector((state) => state.requests.requests); const request = requests[requestIndex]; const selectedHttpMethod = request?.method; const url = request?.url; - const handleUrlChange = useCallback((event: ChangeEvent) => { - if (request == null) return; + const handleUrlChange = useCallback( + (event: ChangeEvent) => { + if (request == null) return; - dispatch(updateRequest({ - index: requestIndex, - request: { ...request, url: event.target.value, draft: true }, - })); - }, [request]); + dispatch( + updateRequest({ + index: requestIndex, + request: { ...request, url: event.target.value, draft: true }, + }) + ); + }, + [request] + ); - const handleHttpMethodChange = useCallback((method: RequestMethod) => { - if (request == null) return; + const handleHttpMethodChange = useCallback( + (method: RequestMethod) => { + if (request == null) return; - dispatch(updateRequest({ - index: requestIndex, - request: { ...request, method, draft: true }, - })); - }, [request]); + dispatch( + updateRequest({ + index: requestIndex, + request: { ...request, method, draft: true }, + }) + ); + }, + [request] + ); - const sendRequest = useCallback(useErrorHandler(async () => { - dispatch( - clearMetaInfo(), - ); - if (request == null) return; - if (!request.url || !request.method) { - throw new Error('Missing URL or HTTP method'); - } + const sendRequest = useCallback( + useErrorHandler(async () => { + dispatch(clearMetaInfo()); + if (request == null) return; + if (!request.url || !request.method) { + throw new Error('Missing URL or HTTP method'); + } - await eventService.saveRequest(request, requestEditor?.getValue()); + await eventService.saveRequest(request, requestEditor?.getValue()); - const response = await httpService.sendRequest(request); - dispatch( - setMetaInfo(response.metaInfo), - ); + const response = await httpService.sendRequest(request); + dispatch(setMetaInfo(response.metaInfo)); - // Send the request and pass the response to the onResponse callback - // ToDo: We should get rid of this callback and set response in shared state - await onResponse(response); - }), [request, requestEditor, onResponse]); + // Send the request and pass the response to the onResponse callback + // ToDo: We should get rid of this callback and set response in shared state + await onResponse(response); + }), + [request, requestEditor, onResponse] + ); - const saveRequest = useCallback(useErrorHandler(async () => { - if (request == null) return; + const saveRequest = useCallback( + useErrorHandler(async () => { + if (request == null) return; - // save request draft with the current editor content - console.info('Saving request:', request); - await eventService.saveRequest(request, requestEditor?.getValue()); + // save request draft with the current editor content + console.info('Saving request:', request); + await eventService.saveRequest(request, requestEditor?.getValue()); - // override existing request with the saved draft - dispatch(updateRequest({ - index: requestIndex, - request: await eventService.saveChanges(request), - })); - }), [request, requestEditor]); + // override existing request with the saved draft + dispatch( + updateRequest({ + index: requestIndex, + request: await eventService.saveChanges(request), + }) + ); + }), + [request, requestEditor] + ); return (
- + diff --git a/src/renderer/components/mainWindow/bodyTabs/InputTabs.tsx b/src/renderer/components/mainWindow/bodyTabs/InputTabs.tsx index 0efc0ad7..d5ee88d7 100644 --- a/src/renderer/components/mainWindow/bodyTabs/InputTabs.tsx +++ b/src/renderer/components/mainWindow/bodyTabs/InputTabs.tsx @@ -40,34 +40,45 @@ import { RufusHeader } from 'shim/objects/headers'; export function InputTabs() { const dispatch = useDispatch(); - const requestBody = useSelector(({ requests }: RootState) => requests.requests[requests.selectedRequest]?.body); + const requestBody = useSelector( + ({ requests }: RootState) => requests.requests[requests.selectedRequest]?.body + ); const headers = useSelector(selectHeaders); const [isOpen, setIsOpen] = useState(false); - const changeBodyType = useCallback((type: RequestBodyType) => { - switch (type) { - case RequestBodyType.TEXT: - dispatch(setRequestBody({ type, mimeType: 'text/plain' })); - break; - case RequestBodyType.FILE: - dispatch(setRequestBody({ type })); - break; - } - }, [dispatch]); + const changeBodyType = useCallback( + (type: RequestBodyType) => { + switch (type) { + case RequestBodyType.TEXT: + dispatch(setRequestBody({ type, mimeType: 'text/plain' })); + break; + case RequestBodyType.FILE: + dispatch(setRequestBody({ type })); + break; + } + }, + [dispatch] + ); - const setRequestBodyFile = useCallback((file?: File) => { - if (file == null) return; - dispatch(setRequestBody({ - type: RequestBodyType.FILE, - filePath: file.path, - mimeType: file.type === '' ? undefined : file.type, - })); - }, [dispatch]); + const setRequestBodyFile = useCallback( + (file?: File) => { + if (file == null) return; + dispatch( + setRequestBody({ + type: RequestBodyType.FILE, + filePath: file.path, + mimeType: file.type === '' ? undefined : file.type, + }) + ); + }, + [dispatch] + ); - const onEditorMount = useCallback((editor: editor.ICodeEditor) => { - dispatch(setRequestEditor(editor)); - /*editor.onDidChangeModelContent(() => { + const onEditorMount = useCallback( + (editor: editor.ICodeEditor) => { + dispatch(setRequestEditor(editor)); + /*editor.onDidChangeModelContent(() => { if (request != null && !request.draft) { dispatch(updateRequest({ index: selectedRequestIndex, @@ -75,7 +86,9 @@ export function InputTabs() { })); } });*/ - }, [dispatch]); + }, + [dispatch] + ); const renderEditor = useCallback(() => { return ( @@ -91,7 +104,8 @@ export function InputTabs() { return ( setRequestBodyFile(v.target.files[0])} - placeholder="Select a file" type="file" + placeholder="Select a file" + type="file" /> ); }, [setRequestBodyFile]); @@ -100,41 +114,55 @@ export function InputTabs() { dispatch(addHeader()); }, [dispatch]); - const handleDeleteHeader = useCallback((index: number) => { - dispatch(deleteHeader(index)); - }, [dispatch]); + const handleDeleteHeader = useCallback( + (index: number) => { + dispatch(deleteHeader(index)); + }, + [dispatch] + ); const deleteAllHeaders = useCallback(() => { dispatch(clearHeaders()); }, [dispatch]); - const handleUpdateHeader = useCallback((index: number, updatedFields: Partial) => { - dispatch(updateHeader({ index, updatedHeader: updatedFields })); - }, [dispatch]); + const handleUpdateHeader = useCallback( + (index: number, updatedFields: Partial) => { + dispatch(updateHeader({ index, updatedHeader: updatedFields })); + }, + [dispatch] + ); const getActiveRowCount = useCallback(() => { - return headers.filter(header => header.isActive).length; + return headers.filter((header) => header.isActive).length; }, [headers]); - return ( - Body - Query + + Body + + + Query + {getActiveRowCount() === 0 ? 'Headers' : `Headers (${getActiveRowCount()})`} - Auth + + Auth +
- changeBodyType(bodyType as RequestBodyType)} + onOpenChange={(open) => setIsOpen(open)} + defaultValue={'text'} + > @@ -204,9 +232,7 @@ export function InputTabs() { - handleUpdateHeader(index, { key: e.target.value }) - } + onChange={(e) => handleUpdateHeader(index, { key: e.target.value })} className="w-full bg-transparent outline-none" placeholder="Enter header key" /> @@ -216,9 +242,7 @@ export function InputTabs() { - handleUpdateHeader(index, { value: e.target.value }) - } + onChange={(e) => handleUpdateHeader(index, { value: e.target.value })} className="w-full bg-transparent outline-none" placeholder="Enter header value" /> @@ -237,15 +261,21 @@ export function InputTabs() { 'form-checkbox h-4 w-4 appearance-none border rounded-[2px] ', header.isActive ? 'border-[rgba(107,194,224,1)] bg-[rgba(25,54,65,1)]' - : 'border-[rgba(238,238,238,1)] bg-transparent', + : 'border-[rgba(238,238,238,1)] bg-transparent' )} /> {header.isActive && (
- + className={ + 'absolute left-0 top-0 h-4 w-4 flex items-center justify-center pointer-events-none rotate-6' + } + > +
)}
diff --git a/src/renderer/components/mainWindow/bodyTabs/OutputTabs.tsx b/src/renderer/components/mainWindow/bodyTabs/OutputTabs.tsx index 245192d3..8cac144d 100644 --- a/src/renderer/components/mainWindow/bodyTabs/OutputTabs.tsx +++ b/src/renderer/components/mainWindow/bodyTabs/OutputTabs.tsx @@ -7,8 +7,9 @@ import { useRef } from 'react'; import { ResponseStatus } from '@/components/mainWindow/responseStatus/ResponseStatus'; export type OutputTabsProps = { - headers?: HttpHeaders; body?: string; -} + headers?: HttpHeaders; + body?: string; +}; const monacoOptions = { ...DEFAULT_MONACO_OPTIONS, @@ -38,47 +39,41 @@ function getContentType(headers?: HttpHeaders) { } export function OutputTabs(props: OutputTabsProps) { - const { - body, - headers, - } = props; + const { body, headers } = props; const mimeType = getMimeType(getContentType(headers)); console.debug('Using syntax highlighting for mime type', mimeType); const tabsRef = useRef(null); - return ( - - Response Body - Headers - - - + return ( + + + + Response Body + + + Headers + + + - -
- -
-
+ +
+ +
+
- - {!headers - ? ( + + {!headers ? (
- - Please enter URL address and click Send to get a response - + Please enter URL address and click Send to get a response
- ) - : ( + ) : ( @@ -91,17 +86,20 @@ export function OutputTabs(props: OutputTabsProps) { {headers && Object.keys(headers).map((key) => { const value = headers[key]; - const valueToDisplay = value !== undefined ? (Array.isArray(value) ? value : [value]) : ''; + const valueToDisplay = + value !== undefined ? (Array.isArray(value) ? value : [value]) : ''; return ( {key} - {(valueToDisplay as string[]).join(', ')} {/* Full width */} + {(valueToDisplay as string[]).join(', ')}{' '} + {/* Full width */} ); })}
)} -
-
); + +
+ ); } diff --git a/src/renderer/components/mainWindow/mainTopBar/HttpMethodSelect.tsx b/src/renderer/components/mainWindow/mainTopBar/HttpMethodSelect.tsx index ddc82585..525456c2 100644 --- a/src/renderer/components/mainWindow/mainTopBar/HttpMethodSelect.tsx +++ b/src/renderer/components/mainWindow/mainTopBar/HttpMethodSelect.tsx @@ -1,14 +1,24 @@ import * as React from 'react'; -import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { RequestMethod } from 'shim/objects/requestMethod'; -import {httpMethodColor} from "@/services/StyleHelper"; +import { httpMethodColor } from '@/services/StyleHelper'; interface HttpMethodSelectProps { selectedHttpMethod: RequestMethod; onHttpMethodChange: (method: RequestMethod) => void; } -export const HttpMethodSelect: React.FC = ({ selectedHttpMethod, onHttpMethodChange }) => { +export const HttpMethodSelect: React.FC = ({ + selectedHttpMethod, + onHttpMethodChange, +}) => { const httpMethodSelectRef = React.useRef(null); React.useEffect(() => { @@ -25,7 +35,9 @@ export const HttpMethodSelect: React.FC = ({ selectedHttp {Object.entries(RequestMethod).map(([key, value]) => ( - {value} + + {value} + ))} diff --git a/src/renderer/components/mainWindow/mainTopBar/SaveButton.tsx b/src/renderer/components/mainWindow/mainTopBar/SaveButton.tsx index 7e01cdc2..76203f00 100644 --- a/src/renderer/components/mainWindow/mainTopBar/SaveButton.tsx +++ b/src/renderer/components/mainWindow/mainTopBar/SaveButton.tsx @@ -16,7 +16,9 @@ export const SaveButton: React.FC = ({ disabled, onClick }) => return ( ); diff --git a/src/renderer/components/mainWindow/mainTopBar/SendButton.tsx b/src/renderer/components/mainWindow/mainTopBar/SendButton.tsx index ddc2200d..b6ebd4a5 100644 --- a/src/renderer/components/mainWindow/mainTopBar/SendButton.tsx +++ b/src/renderer/components/mainWindow/mainTopBar/SendButton.tsx @@ -8,7 +8,9 @@ interface SendButtonProps { export const SendButton: React.FC = ({ onClick }) => ( ); diff --git a/src/renderer/components/mainWindow/responseStatus/ResponseStatus.tsx b/src/renderer/components/mainWindow/responseStatus/ResponseStatus.tsx index 96a0e6a1..8a35051f 100644 --- a/src/renderer/components/mainWindow/responseStatus/ResponseStatus.tsx +++ b/src/renderer/components/mainWindow/responseStatus/ResponseStatus.tsx @@ -9,7 +9,7 @@ import { useSelector } from 'react-redux'; import { RootState } from '@/state/store'; export function ResponseStatus() { - const metaInfo = useSelector(state => state.requests.metaInfo); + const metaInfo = useSelector((state) => state.requests.metaInfo); if (metaInfo == null) { return ; @@ -23,9 +23,9 @@ export function ResponseStatus() { return ( {statusText} - {durationText} - - {sizeText} + {durationText} + + {sizeText}
Headers: {getSizeText(metaInfo.size.headersSizeInBytes)} Body: {getSizeText(metaInfo.size.bodySizeInBytes)} @@ -33,4 +33,4 @@ export function ResponseStatus() { ); -} \ No newline at end of file +} diff --git a/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.test.ts b/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.test.ts index 36c01920..cac45fe9 100644 --- a/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.test.ts +++ b/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.test.ts @@ -69,4 +69,4 @@ describe('ResponseStatusFormatter', () => { expect(getSizeText(1000)).toBe('1.00 KB'); }); }); -}); \ No newline at end of file +}); diff --git a/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.ts b/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.ts index 9ca14024..c76886e2 100644 --- a/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.ts +++ b/src/renderer/components/mainWindow/responseStatus/ResponseStatusFormatter.ts @@ -68,7 +68,7 @@ const HTTP_STATUS_NAME = { 415: 'Unsupported Media Type', 416: 'Requested Range Not Satisfiable', 417: 'Expectation Failed', - 418: 'I\'m a teapot', + 418: "I'm a teapot", 429: 'Too Many Requests', 500: 'Internal Server Error', 501: 'Not Implemented', @@ -76,4 +76,4 @@ const HTTP_STATUS_NAME = { 503: 'Service Unavailable', 504: 'Gateway Timeout', 505: 'HTTP Version Not Supported', -}; \ No newline at end of file +}; diff --git a/src/renderer/components/shared/Divider.tsx b/src/renderer/components/shared/Divider.tsx index 651627d0..dff5d802 100644 --- a/src/renderer/components/shared/Divider.tsx +++ b/src/renderer/components/shared/Divider.tsx @@ -1,8 +1,8 @@ import { cn } from '@/lib/utils'; interface DividerComponentProps { - orientation?: 'horizontal' | 'vertical' - className?: string + orientation?: 'horizontal' | 'vertical'; + className?: string; } /** @@ -15,8 +15,12 @@ interface DividerComponentProps { * - Import where required * - Set orientation prop for desired output. Default `horizontal` * */ -export const Divider = ({orientation= 'horizontal', className}: DividerComponentProps) => { +export const Divider = ({ orientation = 'horizontal', className }: DividerComponentProps) => { return ( -
- ) -} +
+ ); +}; diff --git a/src/renderer/components/shared/settings/SettingsModal.tsx b/src/renderer/components/shared/settings/SettingsModal.tsx index f854f5a7..db17924a 100644 --- a/src/renderer/components/shared/settings/SettingsModal.tsx +++ b/src/renderer/components/shared/settings/SettingsModal.tsx @@ -1,11 +1,18 @@ -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" -import { FiSettings } from "react-icons/fi" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { FiSettings } from 'react-icons/fi'; export const SettingsModal = () => { return ( - + @@ -16,6 +23,5 @@ export const SettingsModal = () => { - - ) -} + ); +}; diff --git a/src/renderer/components/shared/settings/monaco-settings.ts b/src/renderer/components/shared/settings/monaco-settings.ts index c3fb6ced..d439bc57 100644 --- a/src/renderer/components/shared/settings/monaco-settings.ts +++ b/src/renderer/components/shared/settings/monaco-settings.ts @@ -3,5 +3,5 @@ import IEditorOptions = editor.IEditorOptions; export const DEFAULT_MONACO_OPTIONS: Partial = { minimap: { enabled: false }, - wordWrap: 'on' + wordWrap: 'on', }; diff --git a/src/renderer/components/sidebar/SidebarRequestList.tsx b/src/renderer/components/sidebar/SidebarRequestList.tsx index 7eeb03ee..498e1f49 100644 --- a/src/renderer/components/sidebar/SidebarRequestList.tsx +++ b/src/renderer/components/sidebar/SidebarRequestList.tsx @@ -32,46 +32,55 @@ export const SidebarRequestList = ({ requests = [] }: SidebarRequestListProps) = } }, [request?.id, requestEditor?.getModel()]); - const handleRowClick = useCallback(async (index: number) => { - if (request != null) { - await eventService.saveRequest(request, requestEditor?.getValue()); - } - dispatch(setSelectedRequest(index)); - }, [dispatch, requestEditor, requests, request]); + const handleRowClick = useCallback( + async (index: number) => { + if (request != null) { + await eventService.saveRequest(request, requestEditor?.getValue()); + } + dispatch(setSelectedRequest(index)); + }, + [dispatch, requestEditor, requests, request] + ); - const handleDeleteClick = useCallback(async (event: MouseEvent, index: number) => { - const request = requests[index]; - if (request == null) { - return; - } + const handleDeleteClick = useCallback( + async (event: MouseEvent, index: number) => { + const request = requests[index]; + if (request == null) { + return; + } - dispatch(deleteRequest(index)); - if (request.id != null) { - await eventService.deleteObject(request); - } - }, [dispatch, requests]); + dispatch(deleteRequest(index)); + if (request.id != null) { + await eventService.deleteObject(request); + } + }, + [dispatch, requests] + ); return ( - {requests.map((request, index) => ( - handleRowClick(index)} - > - - - - - ))} + {requests.map((request, index) => ( + handleRowClick(index)} + > + + + + + ))}
{request.method} - {request.url} -
{request.url}
-
- handleDeleteClick(event, index)} - className="cursor-pointer" /> -
+ {request.method} + + {request.url} +
{request.url}
+
+ handleDeleteClick(event, index)} + className="cursor-pointer" + /> +
); diff --git a/src/renderer/components/sidebar/SidebarSearch.tsx b/src/renderer/components/sidebar/SidebarSearch.tsx index 4289389e..4f814216 100644 --- a/src/renderer/components/sidebar/SidebarSearch.tsx +++ b/src/renderer/components/sidebar/SidebarSearch.tsx @@ -28,6 +28,5 @@ export const SidebarSearch = () => { Create New Request
- ); }; diff --git a/src/renderer/components/ui/button.tsx b/src/renderer/components/ui/button.tsx index c8341556..e2bbf5e4 100644 --- a/src/renderer/components/ui/button.tsx +++ b/src/renderer/components/ui/button.tsx @@ -10,12 +10,9 @@ const buttonVariants = cva( variants: { variant: { default: 'bg-primary text-primary-foreground hover:bg-primary/90', - destructive: - 'bg-destructive text-destructive-foreground hover:bg-destructive/90', - outline: - 'border border-primary bg-background hover:bg-accent hover:text-accent-foreground', - secondary: - 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-primary bg-background hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', ghost: 'hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline', }, @@ -30,7 +27,7 @@ const buttonVariants = cva( variant: 'default', size: 'default', }, - }, + } ); export interface ButtonProps @@ -43,13 +40,9 @@ const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button'; return ( - + ); - }, + } ); Button.displayName = 'Button'; diff --git a/src/renderer/components/ui/card.tsx b/src/renderer/components/ui/card.tsx index afa13ecf..f3a70ba2 100644 --- a/src/renderer/components/ui/card.tsx +++ b/src/renderer/components/ui/card.tsx @@ -1,79 +1,56 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +const Card = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +Card.displayName = 'Card'; + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +CardHeader.displayName = 'CardHeader'; + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +); +CardTitle.displayName = 'CardTitle'; const CardDescription = React.forwardRef< HTMLParagraphElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } +

+)); +CardDescription.displayName = 'CardDescription'; + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +); +CardContent.displayName = 'CardContent'; + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +CardFooter.displayName = 'CardFooter'; + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; diff --git a/src/renderer/components/ui/dialog.tsx b/src/renderer/components/ui/dialog.tsx index c23630eb..6f1ab0e7 100644 --- a/src/renderer/components/ui/dialog.tsx +++ b/src/renderer/components/ui/dialog.tsx @@ -1,16 +1,16 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-react" +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -const Dialog = DialogPrimitive.Root +const Dialog = DialogPrimitive.Root; -const DialogTrigger = DialogPrimitive.Trigger +const DialogTrigger = DialogPrimitive.Trigger; -const DialogPortal = DialogPrimitive.Portal +const DialogPortal = DialogPrimitive.Portal; -const DialogClose = DialogPrimitive.Close +const DialogClose = DialogPrimitive.Close; const DialogOverlay = React.forwardRef< React.ElementRef, @@ -19,13 +19,13 @@ const DialogOverlay = React.forwardRef< -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, @@ -36,7 +36,7 @@ const DialogContent = React.forwardRef< -)) -DialogContent.displayName = DialogPrimitive.Content.displayName +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; -const DialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DialogHeader.displayName = "DialogHeader" +const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = 'DialogHeader'; -const DialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( +const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
-) -DialogFooter.displayName = "DialogFooter" +); +DialogFooter.displayName = 'DialogFooter'; const DialogTitle = React.forwardRef< React.ElementRef, @@ -85,14 +70,11 @@ const DialogTitle = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< React.ElementRef, @@ -100,11 +82,11 @@ const DialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; export { Dialog, @@ -117,4 +99,4 @@ export { DialogFooter, DialogTitle, DialogDescription, -} +}; diff --git a/src/renderer/components/ui/input.tsx b/src/renderer/components/ui/input.tsx index c254ceda..66f33591 100644 --- a/src/renderer/components/ui/input.tsx +++ b/src/renderer/components/ui/input.tsx @@ -1,9 +1,8 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -export interface InputProps - extends React.InputHTMLAttributes {} +export interface InputProps extends React.InputHTMLAttributes {} const Input = React.forwardRef( ({ className, type, ...props }, ref) => { @@ -11,15 +10,15 @@ const Input = React.forwardRef( - ) + ); } -) -Input.displayName = "Input" +); +Input.displayName = 'Input'; -export { Input } +export { Input }; diff --git a/src/renderer/components/ui/select.tsx b/src/renderer/components/ui/select.tsx index b40f131b..631ff24d 100644 --- a/src/renderer/components/ui/select.tsx +++ b/src/renderer/components/ui/select.tsx @@ -15,106 +15,134 @@ const SelectGroup = SelectPrimitive.Group; const SelectValue = SelectPrimitive.Value; -const SelectTrigger = React.forwardRef, SelectTriggerProps>(({ - className, - children, - isOpen, - ...props -}, ref) => { - return (, + SelectTriggerProps +>(({ className, children, isOpen, ...props }, ref) => { + return ( + span]:line-clamp-1', className)} + className={cn( + 'flex h-10 w-full items-center justify-between bg-card px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', + className + )} {...props} - > - {children} - + {children} + - {/**/} - - - ); + className={cn( + 'transition-transform duration-300 ease-in-out', + isOpen ? 'rotate-180' : 'rotate-0' + )} + > + {/**/} + + + + ); }); SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; -const SelectScrollUpButton = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => (, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + - -)); + > + + +)); SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; -const SelectScrollDownButton = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => (, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + - -)); + > + + +)); SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; -const SelectContent = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - children, - position = 'popper', - ...props -}, ref) => ( - , + React.ComponentPropsWithoutRef +>(({ className, children, position = 'popper', ...props }, ref) => ( + + - - - {children} - - - -)); + + + {children} + + + + +)); SelectContent.displayName = SelectPrimitive.Content.displayName; -const SelectLabel = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => (, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + )); + /> +)); SelectLabel.displayName = SelectPrimitive.Label.displayName; -const SelectItem = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - children, - ...props -}, ref) => (, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + - {children} -)); + > + {children} + +)); SelectItem.displayName = SelectPrimitive.Item.displayName; -const SelectSeparator = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => (, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + )); + /> +)); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { diff --git a/src/renderer/components/ui/separator.tsx b/src/renderer/components/ui/separator.tsx index 6d7f1226..466b4052 100644 --- a/src/renderer/components/ui/separator.tsx +++ b/src/renderer/components/ui/separator.tsx @@ -1,29 +1,24 @@ -import * as React from "react" -import * as SeparatorPrimitive from "@radix-ui/react-separator" +import * as React from 'react'; +import * as SeparatorPrimitive from '@radix-ui/react-separator'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; const Separator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->( - ( - { className, orientation = "horizontal", decorative = true, ...props }, - ref - ) => ( - - ) -) -Separator.displayName = SeparatorPrimitive.Root.displayName +>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( + +)); +Separator.displayName = SeparatorPrimitive.Root.displayName; -export { Separator } +export { Separator }; diff --git a/src/renderer/components/ui/table.tsx b/src/renderer/components/ui/table.tsx index 59ec75ad..5c22ba25 100644 --- a/src/renderer/components/ui/table.tsx +++ b/src/renderer/components/ui/table.tsx @@ -1,123 +1,94 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -const Table = React.forwardRef< - HTMLTableElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( +const Table = React.forwardRef>( + ({ className, ...props }, ref) => (
- +
-)) -Table.displayName = "Table" + ) +); +Table.displayName = 'Table'; const TableHeader = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes + HTMLTableSectionElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( - -)) -TableHeader.displayName = "TableHeader" + +)); +TableHeader.displayName = 'TableHeader'; const TableBody = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes + HTMLTableSectionElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( - -)) -TableBody.displayName = "TableBody" + +)); +TableBody.displayName = 'TableBody'; const TableFooter = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes + HTMLTableSectionElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( - tr]:last:border-b-0", - className - )} - {...props} - /> -)) -TableFooter.displayName = "TableFooter" + tr]:last:border-b-0', className)} + {...props} + /> +)); +TableFooter.displayName = 'TableFooter'; -const TableRow = React.forwardRef< - HTMLTableRowElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( +const TableRow = React.forwardRef>( + ({ className, ...props }, ref) => ( -)) -TableRow.displayName = "TableRow" + ) +); +TableRow.displayName = 'TableRow'; const TableHead = React.forwardRef< - HTMLTableCellElement, - React.ThHTMLAttributes + HTMLTableCellElement, + React.ThHTMLAttributes >(({ className, ...props }, ref) => ( -
-)) -TableHead.displayName = "TableHead" + +)); +TableHead.displayName = 'TableHead'; const TableCell = React.forwardRef< - HTMLTableCellElement, - React.TdHTMLAttributes + HTMLTableCellElement, + React.TdHTMLAttributes >(({ className, ...props }, ref) => ( - + )); -TableCell.displayName = "TableCell"; +TableCell.displayName = 'TableCell'; const TableCaption = React.forwardRef< - HTMLTableCaptionElement, - React.HTMLAttributes + HTMLTableCaptionElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
-)) -TableCaption.displayName = "TableCaption" - -export { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, - TableCaption, -} - - + +)); +TableCaption.displayName = 'TableCaption'; +export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; diff --git a/src/renderer/components/ui/tabs.tsx b/src/renderer/components/ui/tabs.tsx index 3b9c6d5e..2dda9969 100644 --- a/src/renderer/components/ui/tabs.tsx +++ b/src/renderer/components/ui/tabs.tsx @@ -3,40 +3,44 @@ import * as TabsPrimitive from '@radix-ui/react-tabs'; import { cn } from '@/lib/utils'; -const Tabs = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => ()); +const Tabs = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); Tabs.displayName = TabsPrimitive.Root.displayName; -const TabsList = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => ()); +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); TabsList.displayName = TabsPrimitive.List.displayName; const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; // const TabsTrigger = React.forwardRef, React.ComponentPropsWithoutRef>(({ // className, @@ -51,14 +55,19 @@ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName // }); // TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; -const TabsContent = React.forwardRef, React.ComponentPropsWithoutRef>(({ - className, - ...props -}, ref) => ()); +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); TabsContent.displayName = TabsPrimitive.Content.displayName; export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/src/renderer/components/ui/toast.tsx b/src/renderer/components/ui/toast.tsx index a8224775..43be556b 100644 --- a/src/renderer/components/ui/toast.tsx +++ b/src/renderer/components/ui/toast.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from "lucide-react" +import * as React from 'react'; +import * as ToastPrimitives from '@radix-ui/react-toast'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { X } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -const ToastProvider = ToastPrimitives.Provider +const ToastProvider = ToastPrimitives.Provider; const ToastViewport = React.forwardRef< React.ElementRef, @@ -14,34 +14,33 @@ const ToastViewport = React.forwardRef< -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + 'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full', { variants: { variant: { - default: "border bg-background text-foreground", + default: 'border bg-background text-foreground', destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", + 'destructive group border-destructive bg-destructive text-destructive-foreground', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, } -) +); const Toast = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps + React.ComponentPropsWithoutRef & VariantProps >(({ className, variant, ...props }, ref) => { return ( - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; const ToastAction = React.forwardRef< React.ElementRef, @@ -60,13 +59,13 @@ const ToastAction = React.forwardRef< -)) -ToastAction.displayName = ToastPrimitives.Action.displayName +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; const ToastClose = React.forwardRef< React.ElementRef, @@ -75,7 +74,7 @@ const ToastClose = React.forwardRef< -)) -ToastClose.displayName = ToastPrimitives.Close.displayName +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; const ToastTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName + +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; const ToastDescription = React.forwardRef< React.ElementRef, @@ -104,15 +99,15 @@ const ToastDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef +type ToastProps = React.ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement +type ToastActionElement = React.ReactElement; export { type ToastProps, @@ -124,4 +119,4 @@ export { ToastDescription, ToastClose, ToastAction, -} +}; diff --git a/src/renderer/components/ui/toaster.tsx b/src/renderer/components/ui/toaster.tsx index a2209ba5..1b4e5613 100644 --- a/src/renderer/components/ui/toaster.tsx +++ b/src/renderer/components/ui/toaster.tsx @@ -5,11 +5,11 @@ import { ToastProvider, ToastTitle, ToastViewport, -} from "@/components/ui/toast" -import { useToast } from "@/components/ui/use-toast" +} from '@/components/ui/toast'; +import { useToast } from '@/components/ui/use-toast'; export function Toaster() { - const { toasts } = useToast() + const { toasts } = useToast(); return ( @@ -18,16 +18,14 @@ export function Toaster() {
{title && {title}} - {description && ( - {description} - )} + {description && {description}}
{action}
- ) + ); })}
- ) + ); } diff --git a/src/renderer/components/ui/use-toast.ts b/src/renderer/components/ui/use-toast.ts index 2fc025f5..1ebd6948 100644 --- a/src/renderer/components/ui/use-toast.ts +++ b/src/renderer/components/ui/use-toast.ts @@ -1,102 +1,100 @@ // Inspired by react-hot-toast library -import * as React from "react" +import * as React from 'react'; -import type {ToastActionElement, ToastProps,} from "@/components/ui/toast" -import {DisplayableError} from "@/error/DisplayableError"; +import type { ToastActionElement, ToastProps } from '@/components/ui/toast'; +import { DisplayableError } from '@/error/DisplayableError'; -const TOAST_LIMIT = 1 -const TOAST_REMOVE_DELAY = 1000000 +const TOAST_LIMIT = 1; +const TOAST_REMOVE_DELAY = 1000000; type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement -} + id: string; + title?: React.ReactNode; + description?: React.ReactNode; + action?: ToastActionElement; +}; const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", -} as const + ADD_TOAST: 'ADD_TOAST', + UPDATE_TOAST: 'UPDATE_TOAST', + DISMISS_TOAST: 'DISMISS_TOAST', + REMOVE_TOAST: 'REMOVE_TOAST', +} as const; -let count = 0 +let count = 0; function genId() { - count = (count + 1) % Number.MAX_SAFE_INTEGER - return count.toString() + count = (count + 1) % Number.MAX_SAFE_INTEGER; + return count.toString(); } -type ActionType = typeof actionTypes +type ActionType = typeof actionTypes; type Action = | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast -} + type: ActionType['ADD_TOAST']; + toast: ToasterToast; + } | { - type: ActionType["UPDATE_TOAST"] - toast: Partial -} + type: ActionType['UPDATE_TOAST']; + toast: Partial; + } | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] -} + type: ActionType['DISMISS_TOAST']; + toastId?: ToasterToast['id']; + } | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] -} + type: ActionType['REMOVE_TOAST']; + toastId?: ToasterToast['id']; + }; interface State { - toasts: ToasterToast[] + toasts: ToasterToast[]; } -const toastTimeouts = new Map>() +const toastTimeouts = new Map>(); const addToRemoveQueue = (toastId: string) => { if (toastTimeouts.has(toastId)) { - return + return; } const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) + toastTimeouts.delete(toastId); dispatch({ - type: "REMOVE_TOAST", + type: 'REMOVE_TOAST', toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) + }); + }, TOAST_REMOVE_DELAY); - toastTimeouts.set(toastId, timeout) -} + toastTimeouts.set(toastId, timeout); +}; export const reducer = (state: State, action: Action): State => { switch (action.type) { - case "ADD_TOAST": + case 'ADD_TOAST': return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } + }; - case "UPDATE_TOAST": + case 'UPDATE_TOAST': return { ...state, - toasts: state.toasts.map((t) => - t.id === action.toast.id ? {...t, ...action.toast} : t - ), - } + toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)), + }; - case "DISMISS_TOAST": { - const {toastId} = action + case 'DISMISS_TOAST': { + const { toastId } = action; // ! Side effects ! - This could be extracted into a dismissToast() action, // but I'll keep it here for simplicity if (toastId) { - addToRemoveQueue(toastId) + addToRemoveQueue(toastId); } else { state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) + addToRemoveQueue(toast.id); + }); } return { @@ -104,87 +102,87 @@ export const reducer = (state: State, action: Action): State => { toasts: state.toasts.map((t) => t.id === toastId || toastId === undefined ? { - ...t, - open: false, - } + ...t, + open: false, + } : t ), - } + }; } - case "REMOVE_TOAST": + case 'REMOVE_TOAST': if (action.toastId === undefined) { return { ...state, toasts: [], - } + }; } return { ...state, toasts: state.toasts.filter((t) => t.id !== action.toastId), - } + }; } -} +}; -const listeners: Array<(state: State) => void> = [] +const listeners: Array<(state: State) => void> = []; -let memoryState: State = {toasts: []} +let memoryState: State = { toasts: [] }; function dispatch(action: Action) { - memoryState = reducer(memoryState, action) + memoryState = reducer(memoryState, action); listeners.forEach((listener) => { - listener(memoryState) - }) + listener(memoryState); + }); } -type Toast = Omit +type Toast = Omit; -function toast({...props}: Toast) { - const id = genId() +function toast({ ...props }: Toast) { + const id = genId(); const update = (props: ToasterToast) => dispatch({ - type: "UPDATE_TOAST", - toast: {...props, id}, - }) - const dismiss = () => dispatch({type: "DISMISS_TOAST", toastId: id}) + type: 'UPDATE_TOAST', + toast: { ...props, id }, + }); + const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id }); dispatch({ - type: "ADD_TOAST", + type: 'ADD_TOAST', toast: { ...props, id, open: true, onOpenChange: (open) => { - if (!open) dismiss() + if (!open) dismiss(); }, }, - }) + }); return { id: id, dismiss, update, - } + }; } function useToast() { - const [state, setState] = React.useState(memoryState) + const [state, setState] = React.useState(memoryState); React.useEffect(() => { - listeners.push(setState) + listeners.push(setState); return () => { - const index = listeners.indexOf(setState) + const index = listeners.indexOf(setState); if (index > -1) { - listeners.splice(index, 1) + listeners.splice(index, 1); } - } - }, [state]) + }; + }, [state]); return { ...state, toast, - dismiss: (toastId?: string) => dispatch({type: "DISMISS_TOAST", toastId}), - } + dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }), + }; } /** @@ -201,7 +199,9 @@ function useErrorHandler any>(fn: F) { } catch (error) { getToastForError(error); } - } as (...args: Parameters) => ReturnType extends Promise ? Promise : (ReturnType | void); + } as ( + ...args: Parameters + ) => ReturnType extends Promise ? Promise : ReturnType | void; } /** @@ -210,7 +210,7 @@ function useErrorHandler any>(fn: F) { * @param error The error to display. */ function getToastForError(error: any) { - const options: Toast = {variant: "destructive"}; + const options: Toast = { variant: 'destructive' }; console.error(error); if (error instanceof DisplayableError) { @@ -224,4 +224,4 @@ function getToastForError(error: any) { toast(options); } -export {useToast, toast, useErrorHandler} +export { useToast, toast, useErrorHandler }; diff --git a/src/renderer/error/DisplayableError.ts b/src/renderer/error/DisplayableError.ts index 3b6af1f7..31be017f 100644 --- a/src/renderer/error/DisplayableError.ts +++ b/src/renderer/error/DisplayableError.ts @@ -3,11 +3,14 @@ * An error that can be displayed to the user as toast. It has a title and a description. */ export class DisplayableError extends Error { + public static readonly DEFAULT_TITLE = 'Unexpected Error'; + public static readonly DEFAULT_DESCRIPTION = 'See the console for more information.'; - public static readonly DEFAULT_TITLE = "Unexpected Error"; - public static readonly DEFAULT_DESCRIPTION = "See the console for more information."; - - constructor(public readonly description: string, public readonly title = DisplayableError.DEFAULT_TITLE, public readonly cause?: any) { + constructor( + public readonly description: string, + public readonly title = DisplayableError.DEFAULT_TITLE, + public readonly cause?: any + ) { super(description); Reflect.setPrototypeOf(this, DisplayableError.prototype); this.name = DisplayableError.name; diff --git a/src/renderer/error/MainProcessError.ts b/src/renderer/error/MainProcessError.ts index 6b6c35a1..00fe8111 100644 --- a/src/renderer/error/MainProcessError.ts +++ b/src/renderer/error/MainProcessError.ts @@ -2,7 +2,6 @@ * An error that occurs in the main process and is forwarded to the renderer process via the event service. */ export class MainProcessError extends Error { - constructor(message?: string) { super(message); Reflect.setPrototypeOf(this, MainProcessError.prototype); diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index d8eb9b01..d69411e9 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -19,9 +19,12 @@ const root = createRoot(container); // load Monaco Editor loader.config({ monaco }); -RendererEventService.instance.loadCollection().then(collection => { - const requests = collection.children.filter(c => c.type === 'request') as RufusRequest[]; +RendererEventService.instance.loadCollection().then((collection) => { + const requests = collection.children.filter((c) => c.type === 'request') as RufusRequest[]; store.dispatch(initialize({ requests, collectionId: collection.id })); - root.render(); + root.render( + + + + ); }); - diff --git a/src/renderer/lib/utils.ts b/src/renderer/lib/utils.ts index d084ccad..9ad0df42 100644 --- a/src/renderer/lib/utils.ts +++ b/src/renderer/lib/utils.ts @@ -1,6 +1,6 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/src/renderer/services/StyleHelper.ts b/src/renderer/services/StyleHelper.ts index 9174cc01..2690fdc9 100644 --- a/src/renderer/services/StyleHelper.ts +++ b/src/renderer/services/StyleHelper.ts @@ -1,4 +1,4 @@ -import {RequestMethod} from "shim/objects/requestMethod"; +import { RequestMethod } from 'shim/objects/requestMethod'; export const httpMethodColor = (request: RequestMethod) => { switch (request) { @@ -21,4 +21,4 @@ export const httpMethodColor = (request: RequestMethod) => { case RequestMethod.trace: return 'http-method-color-trace'; } -} +}; diff --git a/src/renderer/services/event/renderer-event-service.ts b/src/renderer/services/event/renderer-event-service.ts index 17333045..82b4b17b 100644 --- a/src/renderer/services/event/renderer-event-service.ts +++ b/src/renderer/services/event/renderer-event-service.ts @@ -29,7 +29,7 @@ for (const methodName of METHOD_NAMES) { * @param methodName The name of the event method to call in the main process. */ function createEventMethod(methodName: keyof IEventService) { - return async function(...args: any[]) { + return async function (...args: any[]) { const result = await window.electron.ipcRenderer.invoke(methodName, ...args); if (result instanceof Error) { throw new MainProcessError(result.message); diff --git a/src/renderer/services/http/http-service.ts b/src/renderer/services/http/http-service.ts index 3b8fa540..302e30c9 100644 --- a/src/renderer/services/http/http-service.ts +++ b/src/renderer/services/http/http-service.ts @@ -5,7 +5,6 @@ import { RufusRequest } from 'shim/objects/request'; const eventService = RendererEventService.instance; export class HttpService { - public static readonly instance: HttpService = new HttpService(); /** @@ -35,5 +34,4 @@ export class HttpService { throw new DisplayableError(description, 'Could not send Request', error); } } - } diff --git a/src/renderer/state/requestsSlice.ts b/src/renderer/state/requestsSlice.ts index 465f737f..3bad0487 100644 --- a/src/renderer/state/requestsSlice.ts +++ b/src/renderer/state/requestsSlice.ts @@ -13,11 +13,11 @@ export const requestsSlice = createSlice({ metaInfo: null as MetaInfo | null, selectedRequest: 0, collectionId: '', - requestEditor: undefined as (undefined | editor.ICodeEditor), - requestBody: undefined as (undefined | RequestBody), + requestEditor: undefined as undefined | editor.ICodeEditor, + requestBody: undefined as undefined | RequestBody, }, reducers: { - initialize(state, action: PayloadAction<{ requests: RufusRequest[], collectionId: string }>) { + initialize(state, action: PayloadAction<{ requests: RufusRequest[]; collectionId: string }>) { state.requests = action.payload.requests; state.collectionId = action.payload.collectionId; }, @@ -64,15 +64,25 @@ export const requestsSlice = createSlice({ addHeader: (state) => { state.requests[state.selectedRequest].headers.push({ key: '', value: '', isActive: false }); }, - updateHeader: (state, action: PayloadAction<{ - index: number; - updatedHeader: Partial; - }>) => { + updateHeader: ( + state, + action: PayloadAction<{ + index: number; + updatedHeader: Partial; + }> + ) => { const { index, updatedHeader } = action.payload; - state.requests[state.selectedRequest].headers = state.requests[state.selectedRequest].headers.toSpliced(index, 1, { ...state.requests[state.selectedRequest].headers[index], ...updatedHeader }); + state.requests[state.selectedRequest].headers = state.requests[ + state.selectedRequest + ].headers.toSpliced(index, 1, { + ...state.requests[state.selectedRequest].headers[index], + ...updatedHeader, + }); }, deleteHeader: (state, action: PayloadAction) => { - state.requests[state.selectedRequest].headers = state.requests[state.selectedRequest].headers.toSpliced(action.payload, 1); + state.requests[state.selectedRequest].headers = state.requests[ + state.selectedRequest + ].headers.toSpliced(action.payload, 1); if (state.requests[state.selectedRequest].headers.length === 0) { requestsSlice.caseReducers.addHeader(state); } @@ -90,7 +100,8 @@ export const requestsSlice = createSlice({ }, }); -export const selectRequest = (state: RootState) => state.requests.requests[state.requests.selectedRequest]; +export const selectRequest = (state: RootState) => + state.requests.requests[state.requests.selectedRequest]; export const selectHeaders = (state: RootState) => selectRequest(state).headers; export const { @@ -107,4 +118,4 @@ export const { clearHeaders, setMetaInfo, clearMetaInfo, -} = requestsSlice.actions; \ No newline at end of file +} = requestsSlice.actions; diff --git a/src/renderer/state/store.ts b/src/renderer/state/store.ts index 6851eaf0..3dab5b68 100644 --- a/src/renderer/state/store.ts +++ b/src/renderer/state/store.ts @@ -16,4 +16,3 @@ export const store = configureStore({ export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; - diff --git a/src/renderer/view/MainWindow.tsx b/src/renderer/view/MainWindow.tsx index 73ad4da2..bb852d98 100644 --- a/src/renderer/view/MainWindow.tsx +++ b/src/renderer/view/MainWindow.tsx @@ -1,16 +1,15 @@ // import { Header } from '@/components/Header'; -import {useState} from "react"; -import {RendererEventService} from "@/services/event/renderer-event-service"; -import {RufusResponse} from "shim/objects/response"; -import {MainTopBar} from "@/components/mainWindow/MainTopBar"; -import {MainBody} from "@/components/mainWindow/MainBody"; -import {HttpHeaders} from "shim/headers"; +import { useState } from 'react'; +import { RendererEventService } from '@/services/event/renderer-event-service'; +import { RufusResponse } from 'shim/objects/response'; +import { MainTopBar } from '@/components/mainWindow/MainTopBar'; +import { MainBody } from '@/components/mainWindow/MainBody'; +import { HttpHeaders } from 'shim/headers'; const eventService = RendererEventService.instance; const textDecoder = new TextDecoder(); export function MainWindow() { - const [body, setBody] = useState(); const [headers, setHeaders] = useState(); @@ -18,16 +17,16 @@ export function MainWindow() { setHeaders(response.headers); if (response.bodyFilePath) { const buffer = await eventService.readFile(response.bodyFilePath); - console.debug("Received response body of", buffer.byteLength, "bytes") + console.debug('Received response body of', buffer.byteLength, 'bytes'); setBody(textDecoder.decode(buffer)); // TODO: decode with encoding from response headers } } return (
- {/*
*/} - - + {/*
*/} + +
); } diff --git a/src/renderer/view/Sidebar.tsx b/src/renderer/view/Sidebar.tsx index 53f578c5..daffba32 100644 --- a/src/renderer/view/Sidebar.tsx +++ b/src/renderer/view/Sidebar.tsx @@ -1,20 +1,20 @@ import React from 'react'; -import {Divider} from '@/components/shared/Divider'; -import {SidebarRequestList} from '@/components/sidebar/SidebarRequestList'; -import {FooterBar} from '@/components/sidebar/FooterBar'; -import {RootState} from "@/state/store"; -import {useSelector} from "react-redux"; -import {SidebarSearch} from "@/components/sidebar/SidebarSearch"; +import { Divider } from '@/components/shared/Divider'; +import { SidebarRequestList } from '@/components/sidebar/SidebarRequestList'; +import { FooterBar } from '@/components/sidebar/FooterBar'; +import { RootState } from '@/state/store'; +import { useSelector } from 'react-redux'; +import { SidebarSearch } from '@/components/sidebar/SidebarSearch'; export const Sidebar = () => { const requests = useSelector((state: RootState) => state.requests.requests); return ( -
- - - - -
+
+ + + + +
); }; diff --git a/src/shim/event-service.ts b/src/shim/event-service.ts index d8deb617..4048dc51 100644 --- a/src/shim/event-service.ts +++ b/src/shim/event-service.ts @@ -4,7 +4,6 @@ import { Collection } from './objects/collection'; import { RufusObject } from './objects'; export interface IEventService { - /** * Load the default collection. */ diff --git a/src/shim/fs.ts b/src/shim/fs.ts index f260d2ef..dd3d1ed1 100644 --- a/src/shim/fs.ts +++ b/src/shim/fs.ts @@ -6,4 +6,4 @@ export type FileInfo = { birthtime: Date; isFile: boolean; isDirectory: boolean; -} +}; diff --git a/src/shim/objects/collection.ts b/src/shim/objects/collection.ts index 4ef0b316..404d6dcc 100644 --- a/src/shim/objects/collection.ts +++ b/src/shim/objects/collection.ts @@ -9,4 +9,4 @@ export type Collection = { dirPath: string; variables: Record; children: (Folder | RufusRequest)[]; -} \ No newline at end of file +}; diff --git a/src/shim/objects/folder.ts b/src/shim/objects/folder.ts index a28bbac0..1f7238be 100644 --- a/src/shim/objects/folder.ts +++ b/src/shim/objects/folder.ts @@ -6,4 +6,4 @@ export type Folder = { type: 'folder'; title: string; children: (Folder | RufusRequest)[]; -} \ No newline at end of file +}; diff --git a/src/shim/objects/headers.ts b/src/shim/objects/headers.ts index 2db9973e..034ab768 100644 --- a/src/shim/objects/headers.ts +++ b/src/shim/objects/headers.ts @@ -2,4 +2,4 @@ export type RufusHeader = { key: string; value: string; isActive: boolean; -}; \ No newline at end of file +}; diff --git a/src/shim/objects/index.ts b/src/shim/objects/index.ts index f37f57fb..8e988310 100644 --- a/src/shim/objects/index.ts +++ b/src/shim/objects/index.ts @@ -14,4 +14,4 @@ export function isFolder(object: RufusObject): object is Folder { export function isCollection(object: RufusObject): object is Collection { return object.type === 'collection'; -} \ No newline at end of file +} diff --git a/src/shim/objects/request.ts b/src/shim/objects/request.ts index c70d026c..6f57214b 100644 --- a/src/shim/objects/request.ts +++ b/src/shim/objects/request.ts @@ -13,13 +13,13 @@ export type RufusRequest = { headers: RufusHeader[]; body: RequestBody; draft?: boolean; -} +}; export type RequestBody = TextBody | FileBody; export enum RequestBodyType { TEXT = 'text', - FILE = 'file' + FILE = 'file', } export type TextBody = { @@ -28,18 +28,18 @@ export type TextBody = { text?: string; /** The mime type of the file content, e.g. "application/json". May include an encoding */ mimeType: string; -} +}; export type FileBody = { type: RequestBodyType.FILE; filePath?: string; /** The mime type of the file content, e.g. "application/json". May include an encoding */ mimeType?: string; -} +}; export function sanitizeTitle(title: string): string { return title - .toLowerCase() - .replace(/\s/g, '-') - .replace(/[^a-z0-9-]/g, ''); + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^a-z0-9-]/g, ''); } diff --git a/src/shim/objects/requestMethod.ts b/src/shim/objects/requestMethod.ts index e5b9c1b6..687f0858 100644 --- a/src/shim/objects/requestMethod.ts +++ b/src/shim/objects/requestMethod.ts @@ -7,5 +7,5 @@ export enum RequestMethod { options = 'OPTIONS', head = 'HEAD', connect = 'CONNECT', - trace = 'TRACE' + trace = 'TRACE', } diff --git a/src/shim/objects/response.ts b/src/shim/objects/response.ts index 732952f7..95be8ce8 100644 --- a/src/shim/objects/response.ts +++ b/src/shim/objects/response.ts @@ -4,16 +4,16 @@ export type RufusResponse = { headers: HttpHeaders; metaInfo: MetaInfo; bodyFilePath?: string; -} +}; export type MetaInfo = { duration: number; size: ResponseSize; status: number; -} +}; export type ResponseSize = { - totalSizeInBytes: number - headersSizeInBytes: number - bodySizeInBytes: number -} \ No newline at end of file + totalSizeInBytes: number; + headersSizeInBytes: number; + bodySizeInBytes: number; +}; diff --git a/src/shim/variables.ts b/src/shim/variables.ts index 35db94ba..96ba4274 100644 --- a/src/shim/variables.ts +++ b/src/shim/variables.ts @@ -4,4 +4,4 @@ export const VARIABLE_NAME_REGEX = /^[a-zA-Z_]+(-?[0-9a-zA-Z_]+)*$/; export type VariableObject = { value: string; enabled: boolean; -} \ No newline at end of file +}; diff --git a/webpack.main.config.ts b/webpack.main.config.ts index fd3c83e2..99d557c8 100644 --- a/webpack.main.config.ts +++ b/webpack.main.config.ts @@ -1,8 +1,8 @@ -import type {Configuration} from 'webpack'; +import type { Configuration } from 'webpack'; -import {rules} from './webpack.rules'; -import {plugins} from './webpack.plugins'; -import {TsconfigPathsPlugin} from "tsconfig-paths-webpack-plugin"; +import { rules } from './webpack.rules'; +import { plugins } from './webpack.plugins'; +import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; export const mainConfig: Configuration = { /** @@ -17,6 +17,6 @@ export const mainConfig: Configuration = { plugins, resolve: { extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'], - plugins: [new TsconfigPathsPlugin()] + plugins: [new TsconfigPathsPlugin()], }, }; diff --git a/webpack.renderer.config.ts b/webpack.renderer.config.ts index 098c55e3..105f4af1 100644 --- a/webpack.renderer.config.ts +++ b/webpack.renderer.config.ts @@ -7,18 +7,21 @@ import MonacoEditorWebpackPlugin from 'monaco-editor-webpack-plugin'; rules.push({ test: /\.css$/, - use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'postcss-loader' }] + use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'postcss-loader' }], }); export const rendererConfig: Configuration = { module: { - rules + rules, }, - plugins: [...plugins, new MonacoEditorWebpackPlugin({ - languages: ['javascript', 'typescript', 'json', 'xml', 'html'] // TODO: also remove features that are not needed - })], + plugins: [ + ...plugins, + new MonacoEditorWebpackPlugin({ + languages: ['javascript', 'typescript', 'json', 'xml', 'html'], // TODO: also remove features that are not needed + }), + ], resolve: { extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], - plugins: [new TsconfigPathsPlugin()] - } + plugins: [new TsconfigPathsPlugin()], + }, };