diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b033398a3..4bd83400b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @helloanoop @maintainer-bruno @lohit-bruno @naman-bruno +* @helloanoop @maintainer-bruno @bijin-bruno @lohit-bruno @naman-bruno diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aaf5d1880..2a829098e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,7 @@ jobs: npm run sandbox:bundle-libraries --workspace=packages/bruno-js npm run build --workspace=packages/bruno-converters npm run build --workspace=packages/bruno-requests + npm run build --workspace=packages/bruno-filestore - name: Lint Check run: npm run lint @@ -80,6 +81,7 @@ jobs: npm run sandbox:bundle-libraries --workspace=packages/bruno-js npm run build --workspace=packages/bruno-converters npm run build --workspace=packages/bruno-requests + npm run build --workspace=packages/bruno-filestore - name: Run tests run: | @@ -125,6 +127,7 @@ jobs: npm run sandbox:bundle-libraries --workspace=packages/bruno-js npm run build:bruno-converters npm run build:bruno-requests + npm run build:bruno-filestore - name: Run Playwright tests run: | diff --git a/assets/images/landing-2.png b/assets/images/landing-2.png index 156d207e5..2d3fa7f12 100644 Binary files a/assets/images/landing-2.png and b/assets/images/landing-2.png differ diff --git a/contributing.md b/contributing.md index b72d71293..0c6a7d4a6 100644 --- a/contributing.md +++ b/contributing.md @@ -69,6 +69,7 @@ npm run build:bruno-query npm run build:bruno-common npm run build:bruno-converters npm run build:bruno-requests +npm run build:bruno-filestore # bundle js sandbox libraries npm run sandbox:bundle-libraries --workspace=packages/bruno-js diff --git a/e2e-tests/001-sanity-tests/001-home-screen.spec.ts b/e2e-tests/001-sanity-tests/001-home-screen.spec.ts index d993fb7bc..326ff895c 100644 --- a/e2e-tests/001-sanity-tests/001-home-screen.spec.ts +++ b/e2e-tests/001-sanity-tests/001-home-screen.spec.ts @@ -2,4 +2,4 @@ import { test, expect } from '../../playwright'; test('Check if the logo on top left is visible', async ({ page }) => { await expect(page.getByRole('button', { name: 'bruno' })).toBeVisible(); -}); \ No newline at end of file +}); diff --git a/e2e-tests/002-cookies-tests/001-cookie-persistence.spec.ts b/e2e-tests/002-cookies-tests/001-cookie-persistence.spec.ts new file mode 100644 index 000000000..beefb3b39 --- /dev/null +++ b/e2e-tests/002-cookies-tests/001-cookie-persistence.spec.ts @@ -0,0 +1,43 @@ +import { test, expect } from '../../playwright'; + +test('should persist cookies across app restarts', async ({ createTmpDir, launchElectronApp }) => { + // Create a temporary user-data directory so we control where the cookies store file is written. + const userDataPath = await createTmpDir('cookie-persistence'); + + const app1 = await launchElectronApp({ userDataPath }); + const page1 = await app1.firstWindow(); + await page1.waitForSelector('[data-trigger="cookies"]'); + + // Open Cookies modal via the status-bar button. + await page1.click('[data-trigger="cookies"]'); + + // When no cookies are present the modal shows a centred "Add Cookie" button. + await page1.getByRole('button', { name: /Add Cookie/i }).click(); + + // Fill out the form. + await page1.fill('input[name="domain"]', 'example.com'); + await page1.fill('input[name="path"]', '/'); + await page1.fill('input[name="key"]', 'session'); + await page1.fill('input[name="value"]', 'abc123'); + await page1.check('input[name="secure"]'); + await page1.check('input[name="httpOnly"]'); + + await page1.getByRole('button', { name: 'Save' }).click(); + + await expect(page1.getByText('example.com')).toBeVisible(); + + await app1.close(); + + // Second launch – verify the cookie was persisted and re-loaded + const app2 = await launchElectronApp({ userDataPath }); + const page2 = await app2.firstWindow(); + + // Open the Cookies modal again. + await page2.waitForSelector('[data-trigger="cookies"]'); + await page2.click('[data-trigger="cookies"]'); + + // The domain we added earlier should still be present. + await expect(page2.getByText('example.com')).toBeVisible(); + + await app2.close(); +}); diff --git a/e2e-tests/002-cookies-tests/002-corrupted-passkey.spec.ts b/e2e-tests/002-cookies-tests/002-corrupted-passkey.spec.ts new file mode 100644 index 000000000..02c2a5010 --- /dev/null +++ b/e2e-tests/002-cookies-tests/002-corrupted-passkey.spec.ts @@ -0,0 +1,47 @@ +import { test, expect } from '../../playwright'; +import * as path from 'path'; +import * as fs from 'fs/promises'; + +test('should handle corrupted passkey and still display saved cookie list', async ({ createTmpDir, launchElectronApp }) => { + const userDataPath = await createTmpDir('corrupted-passkey'); + + const app1 = await launchElectronApp({ userDataPath }); + // 1. First run – add a cookie via the UI so `cookies.json` is created. + const page1 = await app1.firstWindow(); + + await page1.waitForSelector('[data-trigger="cookies"]'); + await page1.click('[data-trigger="cookies"]'); + await page1.getByRole('button', { name: /Add Cookie/i }).click(); + + await page1.fill('input[name="domain"]', 'example.com'); + await page1.fill('input[name="path"]', '/'); + await page1.fill('input[name="key"]', 'session'); + await page1.fill('input[name="value"]', 'abc123'); + await page1.check('input[name="secure"]'); + await page1.check('input[name="httpOnly"]'); + + await page1.getByRole('button', { name: 'Save' }).click(); + + await expect(page1.getByText('example.com')).toBeVisible(); + + await app1.close(); + + // 2. Corrupt the encryptedPasskey in cookies.json + const cookiesFilePath = path.join(userDataPath, 'cookies.json'); + const raw = await fs.readFile(cookiesFilePath, 'utf-8'); + const cookiesJson = JSON.parse(raw); + cookiesJson.encryptedPasskey = 'deadbeef'; // clearly invalid value + await fs.writeFile(cookiesFilePath, JSON.stringify(cookiesJson, null, 2)); + + // 3. Second run – Bruno should recover and still list the cookie domain + const app2 = await launchElectronApp({ userDataPath }); + const page2 = await app2.firstWindow(); + + await page2.waitForSelector('[data-trigger="cookies"]'); + await page2.click('[data-trigger="cookies"]'); + + // The domain row should still be visible (even if cookie values are blank). + await expect(page2.getByText('example.com')).toBeVisible(); + + await app2.close(); +}); diff --git a/e2e-tests/persistent-env-tests/001-persistent-env-test.spec.ts b/e2e-tests/persistent-env-tests/001-persistent-env-test.spec.ts new file mode 100644 index 000000000..e319e178f --- /dev/null +++ b/e2e-tests/persistent-env-tests/001-persistent-env-test.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from '../../playwright'; + +test.describe.serial('Persistent Environment Test', () => { + test.setTimeout(2 * 10 * 1000); + + test('add env using script', async ({ pageWithUserData: page, restartApp }) => { + await page.locator('#sidebar-collection-name').click(); + await page.getByRole('button', { name: 'Save' }).click(); + await page.getByText('ping', { exact: true }).click(); + await page.getByText('No Environment').click(); + await page.getByRole('tooltip').locator('div').filter({ hasText: 'Env' }).nth(3).click(); + await page.locator('#send-request').getByRole('img').nth(2).click(); + await page.waitForTimeout(1000); + await page.locator('div').filter({ hasText: /^Env$/ }).nth(3).click(); + await page.getByText('Configure', { exact: true }).click(); + await expect(page.getByRole('row', { name: 'persistent-env-test' }).getByRole('cell').nth(3)).toBeVisible(); + await page.getByText('×').click(); + + const newApp = await restartApp(); + const newPage = await newApp.firstWindow(); + await newPage.locator('#sidebar-collection-name').click(); + await newPage.getByRole('button', { name: 'Save' }).click(); + await newPage.getByText('ping', { exact: true }).click(); + await newPage.getByText('No Environment').click(); + await newPage.getByRole('tooltip').locator('div').filter({ hasText: 'Env' }).nth(3).click(); + await newPage.locator('div').filter({ hasText: /^Env$/ }).nth(3).click(); + await newPage.getByText('Configure', { exact: true }).click(); + await expect(newPage.getByRole('row', { name: 'persistent-env-test' }).getByRole('cell').nth(3)).toBeVisible(); + await newPage.getByText('×').click(); + await newPage.waitForTimeout(1000); + await newPage.close(); + }); +}); diff --git a/e2e-tests/persistent-env-tests/002-env-test-without-persistence.spec.ts b/e2e-tests/persistent-env-tests/002-env-test-without-persistence.spec.ts new file mode 100644 index 000000000..4be151bb0 --- /dev/null +++ b/e2e-tests/persistent-env-tests/002-env-test-without-persistence.spec.ts @@ -0,0 +1,40 @@ +import { test, expect } from '../../playwright'; + +test.describe.serial('Persistent Environment Test', () => { + test.setTimeout(2 * 10 * 1000); + + test('add env using script', async ({ pageWithUserData: page, restartApp }) => { + await page.locator('#sidebar-collection-name').click(); + await page.getByText('ping2', { exact: true }).click(); + await page.getByText('Env', { exact: true }).click(); + await page.getByText('Stage', { exact: true }).click(); + await page.locator('#send-request').getByRole('img').nth(2).click(); + await page.waitForTimeout(1000); + await page + .locator('div') + .filter({ hasText: /^Stage$/ }) + .nth(3) + .click(); + await page.getByText('Configure', { exact: true }).click(); + await expect(page.getByRole('row', { name: 'persistent-env-test' }).getByRole('cell').nth(3)).toBeVisible(); + await page.getByText('×').click(); + + const newApp = await restartApp(); + const newPage = await newApp.firstWindow(); + await newPage.locator('#sidebar-collection-name').click(); + await newPage.getByRole('button', { name: 'Save' }).click(); + await newPage.getByText('ping2', { exact: true }).click(); + await newPage.getByText('No Environment').click(); + await newPage.getByText('Stage').click(); + await newPage + .locator('div') + .filter({ hasText: /^Stage$/ }) + .nth(3) + .click(); + await newPage.getByText('Configure', { exact: true }).click(); + await expect(newPage.getByRole('row', { name: 'persistent-env-test' }).getByRole('cell').nth(3)).not.toBeVisible(); + await newPage.getByText('×').click(); + await newPage.waitForTimeout(1000); + await newPage.close(); + }); +}); diff --git a/e2e-tests/persistent-env-tests/collection/bruno.json b/e2e-tests/persistent-env-tests/collection/bruno.json new file mode 100644 index 000000000..fa729847c --- /dev/null +++ b/e2e-tests/persistent-env-tests/collection/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "collection", + "type": "collection" +} \ No newline at end of file diff --git a/e2e-tests/persistent-env-tests/collection/collection.bru b/e2e-tests/persistent-env-tests/collection/collection.bru new file mode 100644 index 000000000..e69de29bb diff --git a/e2e-tests/persistent-env-tests/collection/environments/Env.bru b/e2e-tests/persistent-env-tests/collection/environments/Env.bru new file mode 100644 index 000000000..909243fd2 --- /dev/null +++ b/e2e-tests/persistent-env-tests/collection/environments/Env.bru @@ -0,0 +1,4 @@ +vars { + host: https://testbench-sanity.usebruno.com + persistent-env-test: persistent-env-test-value +} diff --git a/e2e-tests/persistent-env-tests/collection/environments/Stage.bru b/e2e-tests/persistent-env-tests/collection/environments/Stage.bru new file mode 100644 index 000000000..0b756aa68 --- /dev/null +++ b/e2e-tests/persistent-env-tests/collection/environments/Stage.bru @@ -0,0 +1,3 @@ +vars { + host: https://testbench-sanity.usebruno.com +} \ No newline at end of file diff --git a/e2e-tests/persistent-env-tests/collection/persist-env-request.bru b/e2e-tests/persistent-env-tests/collection/persist-env-request.bru new file mode 100644 index 000000000..eefb4e827 --- /dev/null +++ b/e2e-tests/persistent-env-tests/collection/persist-env-request.bru @@ -0,0 +1,15 @@ +meta { + name: ping2 + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: none +} + +script:pre-request { + bru.setEnvVar("persistent-env-test", "persistent-env-test-value"); +} \ No newline at end of file diff --git a/e2e-tests/persistent-env-tests/collection/request.bru b/e2e-tests/persistent-env-tests/collection/request.bru new file mode 100644 index 000000000..9ae6899c5 --- /dev/null +++ b/e2e-tests/persistent-env-tests/collection/request.bru @@ -0,0 +1,15 @@ +meta { + name: ping + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: none +} + +script:pre-request { + bru.setEnvVar("persistent-env-test", "persistent-env-test-value", { persist: true }); +} \ No newline at end of file diff --git a/e2e-tests/persistent-env-tests/init-user-data/preferences.json b/e2e-tests/persistent-env-tests/init-user-data/preferences.json new file mode 100644 index 000000000..f9c1fdc7e --- /dev/null +++ b/e2e-tests/persistent-env-tests/init-user-data/preferences.json @@ -0,0 +1,6 @@ +{ + "maximized": true, + "lastOpenedCollections": [ + "{{projectRoot}}/e2e-tests/persistent-env-tests/collection" + ] +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 30930c550..2d136029b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -5,7 +5,7 @@ const globals = require("globals"); module.exports = defineConfig([ { files: ["packages/bruno-app/**/*.{js,jsx,ts}"], - ignores: ["**/*.config.js"], + ignores: ["**/*.config.js", "**/public/**/*"], languageOptions: { globals: { ...globals.browser, @@ -13,7 +13,8 @@ module.exports = defineConfig([ global: false, require: false, Buffer: false, - process: false + process: false, + ipcRenderer: false }, parserOptions: { ecmaFeatures: { @@ -39,8 +40,60 @@ module.exports = defineConfig([ }, }, { - files: ["packages/bruno-electron/**/*.{js}"], + files: ["packages/bruno-cli/**/*.js"], ignores: ["**/*.config.js"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + ecmaVersion: "latest" + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-common/**/*.ts"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parser: require("@typescript-eslint/parser"), + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: "./packages/bruno-common/tsconfig.json", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-converters/**/*.js"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-electron/**/*.js"], + ignores: ["**/*.config.js", "**/web/**/*"], languageOptions: { globals: { ...globals.node, @@ -50,5 +103,98 @@ module.exports = defineConfig([ rules: { "no-undef": "error", }, - } + }, + { + files: ["packages/bruno-filestore/**/*.ts"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parser: require("@typescript-eslint/parser"), + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: "./packages/bruno-filestore/tsconfig.json", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-js/**/*.js"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + window: false, + self: false, + HTMLElement: false, + typeDetectGlobalObject: false + }, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-lang/**/*.js"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-requests/**/*.ts"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parser: require("@typescript-eslint/parser"), + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: "./packages/bruno-requests/tsconfig.json", + }, + }, + rules: { + "no-undef": "error", + }, + }, + { + files: ["packages/bruno-requests/**/*.js"], + ignores: ["**/*.config.js", "**/dist/**/*"], + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + }, + rules: { + "no-undef": "error", + }, + }, ]); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 577b1d843..de4683783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,15 +18,18 @@ "packages/bruno-tests", "packages/bruno-toml", "packages/bruno-graphql-docs", - "packages/bruno-requests" + "packages/bruno-requests", + "packages/bruno-filestore" ], "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", "@playwright/test": "^1.51.1", + "@rollup/plugin-json": "^6.1.0", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", "@types/node": "^22.14.1", + "@typescript-eslint/parser": "^8.39.0", "concurrently": "^8.2.2", "eslint": "^9.26.0", "fs-extra": "^11.1.1", @@ -3515,7 +3518,6 @@ "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.0", @@ -3533,7 +3535,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -3550,7 +3551,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/@discoveryjs/json-ext": { @@ -3567,7 +3567,6 @@ "version": "3.2.17", "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.17.tgz", "integrity": "sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==", - "dev": true, "license": "MIT", "dependencies": { "commander": "^5.0.0", @@ -3585,7 +3584,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -3597,7 +3595,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -3618,7 +3615,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -3650,9 +3646,9 @@ } }, "node_modules/@electron/get/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3709,296 +3705,6 @@ "node": ">= 4.0.0" } }, - "node_modules/@electron/notarize": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", - "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/notarize/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/osx-sign": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@electron/osx-sign/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/@electron/osx-sign/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/rebuild": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", - "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.45.0", - "node-api-version": "^0.2.0", - "node-gyp": "^9.0.0", - "ora": "^5.1.0", - "read-binary-file-arch": "^1.0.6", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" - }, - "bin": { - "electron-rebuild": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/@electron/rebuild/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@electron/rebuild/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@electron/rebuild/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/rebuild/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/rebuild/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/universal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", - "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/asar": "^3.2.7", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "dir-compare": "^4.2.0", - "fs-extra": "^11.1.1", - "minimatch": "^9.0.3", - "plist": "^3.1.0" - }, - "engines": { - "node": ">=16.4" - } - }, - "node_modules/@electron/universal/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@electron/universal/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@electron/universal/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", @@ -4369,13 +4075,6 @@ "integrity": "sha512-weN3E+rq0Xb3Z93VHJ+Rc7WOQX9ETJPTAJ+gDcaMHtjft67L58sfS65rAjC5tZUXQ2FdZ/V1/sSzCwZ6v05kJw==", "license": "OFL-1.1" }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true, - "license": "MIT" - }, "node_modules/@graphiql/react": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/@graphiql/react/-/react-0.26.2.tgz", @@ -4489,6 +4188,37 @@ } } }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", + "integrity": "sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@headlessui/react": { "version": "1.7.19", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", @@ -4582,7 +4312,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -4600,7 +4329,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4613,7 +4341,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4626,14 +4353,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -4651,7 +4376,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -4667,7 +4391,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -5273,6 +4996,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -5297,34 +5030,10 @@ "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@malept/cross-spawn-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 12.13.0" - } - }, "node_modules/@malept/flatpak-bundler": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.1.1", @@ -5340,7 +5049,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5358,7 +5066,6 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", @@ -5374,7 +5081,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/@mapbox/node-pre-gyp": { @@ -5819,124 +5525,6 @@ "node": ">= 8" } }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@npmcli/move-file/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@npmcli/move-file/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/move-file/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -6257,7 +5845,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -6359,6 +5946,70 @@ "node": ">=16.9" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", @@ -7052,6 +6703,27 @@ } } }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", @@ -8254,7 +7926,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -8348,7 +8019,6 @@ "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/ms": "*" @@ -8386,12 +8056,17 @@ "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/google-protobuf": { + "version": "3.15.12", + "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", + "integrity": "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -8542,6 +8217,15 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.set": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.9.tgz", + "integrity": "sha512-KOxyNkZpbaggVmqbpr82N2tDVTx05/3/j0f50Es1prxrWB0XYf9p3QNxqcbWb7P1Q9wlvsUSlCFnwlPCIJ46PQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/markdown-it": { "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", @@ -8564,14 +8248,12 @@ "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "22.15.17", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -8703,6 +8385,253 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/parser": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@usebruno/app": { "resolved": "packages/bruno-app", "link": true @@ -8725,6 +8654,10 @@ "integrity": "sha512-khvEnRF6/UVDw4df06j+6lFWGNDYWlcWnxfmEgU2o/CdsGY291NC1Cexz99ud7sbGBQP2d8JUXZe4zXPkGNJpQ==", "license": "MIT" }, + "node_modules/@usebruno/filestore": { + "resolved": "packages/bruno-filestore", + "link": true + }, "node_modules/@usebruno/graphql-docs": { "resolved": "packages/bruno-graphql-docs", "link": true @@ -9047,7 +8980,6 @@ "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -9071,7 +9003,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true, "license": "MIT" }, "node_modules/abab": { @@ -9086,8 +9017,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/abort-controller": { "version": "3.0.0", @@ -9175,33 +9106,6 @@ "node": ">= 14" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -9253,7 +9157,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" @@ -9340,114 +9243,6 @@ "node": ">= 8" } }, - "node_modules/app-builder-bin": { - "version": "5.0.0-alpha.10", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", - "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", - "dev": true, - "license": "MIT" - }, - "node_modules/app-builder-lib": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.8.tgz", - "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@develar/schema-utils": "~2.6.5", - "@electron/notarize": "2.5.0", - "@electron/osx-sign": "1.3.1", - "@electron/rebuild": "3.6.1", - "@electron/universal": "2.0.1", - "@malept/flatpak-bundler": "^0.4.0", - "@types/fs-extra": "9.0.13", - "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.9", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", - "chromium-pickle-js": "^0.2.0", - "config-file-ts": "0.2.8-rc1", - "debug": "^4.3.4", - "dotenv": "^16.4.5", - "dotenv-expand": "^11.0.6", - "ejs": "^3.1.8", - "electron-publish": "25.1.7", - "form-data": "^4.0.0", - "fs-extra": "^10.1.0", - "hosted-git-info": "^4.1.0", - "is-ci": "^3.0.0", - "isbinaryfile": "^5.0.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.3", - "lazy-val": "^1.0.5", - "minimatch": "^10.0.0", - "resedit": "^1.7.0", - "sanitize-filename": "^1.6.3", - "semver": "^7.3.8", - "tar": "^6.1.12", - "temp-file": "^3.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "dmg-builder": "25.1.8", - "electron-builder-squirrel-windows": "25.1.8" - } - }, - "node_modules/app-builder-lib/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/app-builder-lib/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -9458,8 +9253,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/arcsecond": { "version": "5.0.0", @@ -9467,36 +9262,6 @@ "integrity": "sha512-J/fHdyadnsIencRsM6oUSsraCKG+Ni9Udcgr/eusxjTzX3SEQtCUQSpP0YtImFPfIK6DdT1nqwN0ng4FqNmwgA==", "license": "MIT" }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -9647,14 +9412,12 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -10100,45 +9863,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, "license": "MIT" }, "node_modules/bluebird-lst": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", - "dev": true, "license": "MIT", "dependencies": { "bluebird": "^3.5.5" @@ -10227,7 +9961,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -10501,6 +10234,18 @@ "node": "*" } }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -10520,127 +10265,19 @@ "dev": true, "license": "MIT" }, - "node_modules/builder-util": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-25.1.7.tgz", - "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", + "node_modules/builtin-modules": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", "dev": true, "license": "MIT", - "dependencies": { - "@types/debug": "^4.1.6", - "7zip-bin": "~5.2.0", - "app-builder-bin": "5.0.0-alpha.10", - "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.2.10", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "debug": "^4.3.4", - "fs-extra": "^10.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "is-ci": "^3.0.0", - "js-yaml": "^4.1.0", - "source-map-support": "^0.5.19", - "stat-mode": "^1.0.0", - "temp-file": "^3.4.0" - } - }, - "node_modules/builder-util-runtime": { - "version": "9.2.10", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", - "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/builder-util-runtime/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/builder-util-runtime/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/builder-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "node": ">=18.20" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/builder-util/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/builder-util/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/builder-util/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -10668,122 +10305,6 @@ "node": ">= 0.8" } }, - "node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -11287,7 +10808,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "devOptional": true, "license": "ISC", "engines": { "node": ">=10" @@ -11307,14 +10827,12 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true, "license": "MIT" }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, "funding": [ { "type": "github", @@ -11366,16 +10884,6 @@ "node": ">= 10.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", @@ -11389,32 +10897,6 @@ "node": ">=0.2.5" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", @@ -11495,16 +10977,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -11519,6 +10991,31 @@ "node": ">=6" } }, + "node_modules/clone-regexp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz", + "integrity": "sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==", + "dependencies": { + "is-regexp": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-regexp/node_modules/is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -11602,8 +11099,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "devOptional": true, "license": "ISC", + "optional": true, "bin": { "color-support": "bin.js" } @@ -11645,7 +11142,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -11661,7 +11157,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11838,78 +11333,6 @@ "node": ">=10" } }, - "node_modules/config-file-ts": { - "version": "0.2.8-rc1", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", - "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^10.3.12", - "typescript": "^5.4.3" - } - }, - "node_modules/config-file-ts/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/config-file-ts/node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -11920,8 +11343,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/constants-browserify": { "version": "1.0.0", @@ -11951,6 +11374,17 @@ "node": ">= 0.6" } }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -12193,7 +11627,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -12843,19 +12276,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -12915,8 +12335,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/depd": { "version": "2.0.0", @@ -12960,8 +12380,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "devOptional": true, "license": "Apache-2.0", + "optional": true, "engines": { "node": ">=8" } @@ -13041,41 +12461,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dir-compare": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", - "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5", - "p-limit": "^3.1.0 " - } - }, - "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -13083,39 +12468,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dmg-builder": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.8.tgz", - "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "app-builder-lib": "25.1.8", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", - "fs-extra": "^10.1.0", - "iconv-lite": "^0.6.2", - "js-yaml": "^4.1.0" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" - } - }, - "node_modules/dmg-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/dmg-license": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", @@ -13332,22 +12684,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -13372,7 +12708,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/ecc-jsbn": { @@ -13410,7 +12745,6 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" @@ -13423,15 +12757,15 @@ } }, "node_modules/electron": { - "version": "33.2.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-33.2.1.tgz", - "integrity": "sha512-SG/nmSsK9Qg1p6wAW+ZfqU+AV8cmXMTIklUL18NnOKfZLlum4ZsDoVdmmmlL39ZmeCaq27dr7CgslRPahfoVJg==", + "version": "37.2.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.6.tgz", + "integrity": "sha512-Ns6xyxE+hIK5UlujtRlw7w4e2Ju/ImCWXf1Q/PoOhc0N3/6SN6YW7+ujCarsHbxWnolbW+1RlkHtdklUJpjbPA==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^22.7.7", "extract-zip": "^2.0.1" }, "bin": { @@ -13441,64 +12775,6 @@ "node": ">= 12.20.55" } }, - "node_modules/electron-builder": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz", - "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", - "dev": true, - "license": "MIT", - "dependencies": { - "app-builder-lib": "25.1.8", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", - "chalk": "^4.1.2", - "dmg-builder": "25.1.8", - "fs-extra": "^10.1.0", - "is-ci": "^3.0.0", - "lazy-val": "^1.0.5", - "simple-update-notifier": "2.0.0", - "yargs": "^17.6.2" - }, - "bin": { - "electron-builder": "cli.js", - "install-app-deps": "install-app-deps.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/electron-builder/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/electron-devtools-installer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-4.0.0.tgz", @@ -13570,54 +12846,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/electron-publish": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-25.1.7.tgz", - "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/fs-extra": "^9.0.11", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", - "chalk": "^4.1.2", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" - } - }, - "node_modules/electron-publish/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-publish/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/electron-store": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz", @@ -13656,23 +12884,6 @@ "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", "license": "MIT" }, - "node_modules/electron/node_modules/@types/node": { - "version": "20.17.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", - "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/electron/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, "node_modules/elliptic": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", @@ -13696,15 +12907,6 @@ "dev": true, "license": "MIT" }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -13743,17 +12945,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/encoding-sniffer": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", @@ -13823,7 +13014,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, "license": "MIT" }, "node_modules/error-ex": { @@ -13910,6 +13100,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -14056,9 +13261,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -14497,13 +13702,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -14849,9 +14047,9 @@ } }, "node_modules/extract-zip/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15071,7 +14269,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" @@ -15081,7 +14278,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -15348,7 +14544,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -15365,7 +14560,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -15384,13 +14578,15 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -15518,7 +14714,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -15556,6 +14751,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-0.1.1.tgz", + "integrity": "sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -15566,27 +14772,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/generic-names": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz", @@ -15819,9 +15004,9 @@ } }, "node_modules/global-agent/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "optional": true, @@ -15872,6 +15057,12 @@ "csstype": "^3.0.10" } }, + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -15971,19 +15162,37 @@ } }, "node_modules/graphql-request/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, + "node_modules/grpc-reflection-js": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/grpc-reflection-js/-/grpc-reflection-js-0.3.0.tgz", + "integrity": "sha512-3lhTlQluPxVgbowCXA3tAZC3RJW+GSOUkguLNYl1QffYRiutUB3RDfPkQFTcrCFJgNiIIxx+iJkr8s3uSp3zWA==", + "license": "MIT", + "dependencies": { + "@types/google-protobuf": "^3.7.2", + "@types/lodash.set": "^4.3.6", + "google-protobuf": "^3.12.2", + "lodash.set": "^4.3.2", + "protobufjs": "^7.2.2" + }, + "peerDependencies": { + "@grpc/grpc-js": "^1.0.0" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -16086,7 +15295,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -16102,8 +15310,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/hash-base": { "version": "3.0.5", @@ -16183,7 +15391,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" @@ -16196,7 +15403,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -16209,7 +15415,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/html-encoding-sniffer": { @@ -16598,16 +15803,6 @@ "node": ">=10.17.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/i18next": { "version": "24.1.2", "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.1.2.tgz", @@ -16839,13 +16034,6 @@ "node": ">=8" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true, - "license": "ISC" - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -16901,6 +16089,17 @@ "node": ">= 12" } }, + "node_modules/ip-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", + "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -17013,7 +16212,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, "license": "MIT", "dependencies": { "ci-info": "^3.2.0" @@ -17111,16 +16309,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-invalid-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", @@ -17154,12 +16342,20 @@ "node": ">=0.10.0" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, - "license": "MIT" + "node_modules/is-ip": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-5.0.1.tgz", + "integrity": "sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==", + "dependencies": { + "ip-regex": "^5.0.0", + "super-regex": "^0.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/is-map": { "version": "2.0.3", @@ -17405,19 +16601,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-valid-path": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", @@ -17470,7 +16653,6 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 18.0.0" @@ -17483,7 +16665,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -17614,7 +16795,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -17630,7 +16810,6 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", @@ -17649,7 +16828,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -17660,7 +16838,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -17677,7 +16854,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -19318,7 +18494,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "dev": true, "license": "MIT" }, "node_modules/leven": { @@ -19440,7 +18615,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, "license": "MIT" }, "node_modules/lodash.curry": { @@ -19518,6 +18692,12 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==", + "license": "MIT" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -19525,39 +18705,11 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -19686,126 +18838,6 @@ "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" } }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-fetch-happen/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -20125,7 +19157,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, "license": "MIT", "bin": { "mime": "cli.js" @@ -20280,7 +19311,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -20289,88 +19319,16 @@ "node": ">=8" } }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/minipass/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true, "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -20384,7 +19342,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true, "license": "ISC" }, "node_modules/mkdirp": { @@ -20546,32 +19503,6 @@ "tslib": "^2.0.3" } }, - "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", @@ -20579,29 +19510,6 @@ "license": "MIT", "optional": true }, - "node_modules/node-api-version": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz", - "integrity": "sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - } - }, - "node_modules/node-api-version/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -20622,32 +19530,6 @@ } } }, - "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.13 || ^14.13 || >=16" - } - }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -20659,82 +19541,6 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -20792,22 +19598,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -20853,23 +19643,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -21055,47 +19828,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -21168,22 +19900,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-queue": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", @@ -21227,7 +19943,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pako": { @@ -21463,7 +20178,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21480,7 +20194,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -21497,14 +20210,12 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/path-scurry/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -21596,21 +20307,6 @@ "path2d": "^0.2.0" } }, - "node_modules/pe-library": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", - "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -21817,7 +20513,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", @@ -22838,18 +21533,10 @@ "asap": "~2.0.3" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, - "license": "ISC" - }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, "license": "MIT", "dependencies": { "err-code": "^2.0.2", @@ -22900,6 +21587,30 @@ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "license": "MIT" }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -23558,44 +22269,6 @@ "react-dom": ">=16.14.0" } }, - "node_modules/read-binary-file-arch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", - "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "bin": { - "read-binary-file-arch": "cli.js" - } - }, - "node_modules/read-binary-file-arch/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/read-binary-file-arch/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -23616,6 +22289,105 @@ "node": ">=0.10.0" } }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "license": "MIT", + "dependencies": { + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/read-config-file/node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "license": "MIT", + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/read-config-file/node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/read-config-file/node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "license": "BSD-2-Clause" + }, + "node_modules/read-config-file/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-config-file/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-config-file/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/read-config-file/node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/readable-stream": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", @@ -23983,24 +22755,6 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, - "node_modules/resedit": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", - "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pe-library": "^0.4.1" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" - } - }, "node_modules/reselect": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", @@ -24090,25 +22844,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -24541,7 +23280,6 @@ "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", - "dev": true, "license": "WTFPL OR ISC", "dependencies": { "truncate-utf8-bytes": "^1.0.0" @@ -25192,8 +23930,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/set-function-length": { "version": "1.2.2", @@ -25295,7 +24033,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -25308,7 +24045,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -25469,7 +24205,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" @@ -25482,7 +24217,6 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -25676,19 +24410,6 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "license": "MIT" }, - "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -25731,7 +24452,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -25760,16 +24480,6 @@ "node": ">= 0.4" } }, - "node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emitter-component": "^1.1.1" - } - }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -25916,7 +24626,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -25967,7 +24676,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -26252,9 +24960,9 @@ } }, "node_modules/sumchecker/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -26276,6 +24984,22 @@ "dev": true, "license": "MIT" }, + "node_modules/super-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz", + "integrity": "sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==", + "dependencies": { + "clone-regexp": "^3.0.0", + "function-timeout": "^0.1.0", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -26513,7 +25237,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "devOptional": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -26531,7 +25254,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "devOptional": true, "license": "ISC", "engines": { "node": ">=8" @@ -26541,7 +25263,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -26554,14 +25275,12 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true, "license": "ISC" }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", - "dev": true, "license": "MIT", "dependencies": { "async-exit-hook": "^2.0.1", @@ -26572,7 +25291,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -26808,6 +25526,20 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -26842,6 +25574,22 @@ "@popperjs/core": "^2.9.0" } }, + "node_modules/tldts": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.12.tgz", + "integrity": "sha512-M9ZQBPp6FyqhMcl233vHYyYRkxXOA1SKGlnq13S0mJdUhRSwr2w6I8rlchPL73wBwRlyIZpFvpu2VcdSMWLYXw==", + "dependencies": { + "tldts-core": "^7.0.12" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.12.tgz", + "integrity": "sha512-3K76aXywJFduGRsOYoY5JzINLs/WMlOkeDwPL+8OCPq2Rh39gkSDtWAxdJQlWjpun/xF/LHf29yqCi6VC/rHDA==" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -26855,7 +25603,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dev": true, "license": "MIT", "dependencies": { "tmp": "^0.2.0" @@ -26905,6 +25652,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", @@ -26920,6 +25668,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4.0.0" @@ -26945,12 +25694,24 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", - "dev": true, "license": "WTFPL", "dependencies": { "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -27169,7 +25930,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -27216,32 +25976,6 @@ "node": ">=4" } }, - "node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -27443,7 +26177,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "dev": true, "license": "(WTFPL OR MIT)" }, "node_modules/util": { @@ -27609,16 +26342,6 @@ "node": ">=10.13.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -27790,7 +26513,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -27866,8 +26588,8 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "devOptional": true, "license": "ISC", + "optional": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -27911,7 +26633,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -29922,6 +28643,7 @@ "@aws-sdk/credential-providers": "3.750.0", "@usebruno/common": "0.1.0", "@usebruno/converters": "^0.1.0", + "@usebruno/filestore": "^0.1.0", "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", "@usebruno/requests": "^0.1.0", @@ -29941,7 +28663,6 @@ "lodash": "^4.17.21", "qs": "^6.11.0", "socks-proxy-agent": "^8.0.2", - "tough-cookie": "^4.1.3", "xmlbuilder": "^15.1.1", "yargs": "^17.6.2" }, @@ -31015,6 +29736,7 @@ "@rollup/plugin-typescript": "^12.1.2", "@types/jest": "^29.5.14", "babel-jest": "^29.7.0", + "is-ip": "^5.0.1", "moment": "^2.29.4", "rollup": "3.29.5", "rollup-plugin-dts": "^5.0.0", @@ -31576,7 +30298,7 @@ "@web/rollup-plugin-copy": "^0.5.1", "babel-jest": "^29.7.0", "rimraf": "^5.0.7", - "rollup": "3.2.5", + "rollup": "3.29.5", "rollup-plugin-dts": "^5.0.0", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-terser": "^7.0.2", @@ -31664,30 +30386,16 @@ "url": "https://github.com/sponsors/isaacs" } }, - "packages/bruno-converters/node_modules/rollup": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.2.5.tgz", - "integrity": "sha512-/Ha7HhVVofduy+RKWOQJrxe4Qb3xyZo+chcpYiD8SoQa4AG7llhupUtyfKSSrdBM2mWJjhM8wZwmbY23NmlIYw==", - "dev": true, - "license": "MIT", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "packages/bruno-electron": { "name": "bruno", "version": "2.0.0", "dependencies": { "@aws-sdk/credential-providers": "3.750.0", + "@grpc/grpc-js": "^1.13.2", + "@grpc/proto-loader": "^0.7.13", "@usebruno/common": "0.1.0", "@usebruno/converters": "^0.1.0", + "@usebruno/filestore": "^0.1.0", "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", "@usebruno/node-machine-id": "^2.0.0", @@ -31703,6 +30411,7 @@ "content-disposition": "^0.5.4", "decomment": "^0.9.5", "dotenv": "^16.0.3", + "electron-builder": "^24.13.3", "electron-is-dev": "^2.0.0", "electron-notarize": "^1.2.2", "electron-store": "^8.1.0", @@ -31720,13 +30429,12 @@ "nanoid": "3.3.8", "qs": "^6.11.0", "socks-proxy-agent": "^8.0.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^6.0.0", "uuid": "^9.0.0", "yup": "^0.32.11" }, "devDependencies": { - "electron": "33.2.1", - "electron-builder": "25.1.8", + "electron": "~37.2.6", "electron-devtools-installer": "^4.0.0" }, "optionalDependencies": { @@ -32195,6 +30903,145 @@ } } }, + "packages/bruno-electron/node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "packages/bruno-electron/node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "packages/bruno-electron/node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "packages/bruno-electron/node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "packages/bruno-electron/node_modules/@electron/universal": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, + "packages/bruno-electron/node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "packages/bruno-electron/node_modules/@electron/universal/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "packages/bruno-electron/node_modules/@electron/universal/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "packages/bruno-electron/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, "packages/bruno-electron/node_modules/@smithy/abort-controller": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", @@ -32760,6 +31607,66 @@ "node": ">=18.0.0" } }, + "packages/bruno-electron/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "packages/bruno-electron/node_modules/app-builder-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "license": "MIT" + }, + "packages/bruno-electron/node_modules/app-builder-lib": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" + } + }, "packages/bruno-electron/node_modules/axios": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", @@ -32771,6 +31678,193 @@ "proxy-from-env": "^1.1.0" } }, + "packages/bruno-electron/node_modules/builder-util": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "4.0.0", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "packages/bruno-electron/node_modules/builder-util-runtime": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "packages/bruno-electron/node_modules/builder-util/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "packages/bruno-electron/node_modules/builder-util/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "packages/bruno-electron/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/bruno-electron/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/bruno-electron/node_modules/dir-compare": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" + } + }, + "packages/bruno-electron/node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "packages/bruno-electron/node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "packages/bruno-electron/node_modules/dmg-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "packages/bruno-electron/node_modules/electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "packages/bruno-electron/node_modules/electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, "packages/bruno-electron/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -32785,6 +31879,24 @@ "node": ">=12" } }, + "packages/bruno-electron/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "packages/bruno-electron/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "packages/bruno-electron/node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -32803,6 +31915,136 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "packages/bruno-electron/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "packages/bruno-electron/node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "packages/bruno-filestore": { + "name": "@usebruno/filestore", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@usebruno/lang": "0.12.0", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@babel/preset-env": "^7.22.0", + "@babel/preset-typescript": "^7.22.0", + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-typescript": "^9.0.2", + "@types/jest": "^29.5.11", + "@types/lodash": "^4.14.191", + "@types/node": "^24.1.0", + "babel-jest": "^29.7.0", + "jest": "^29.2.0", + "rimraf": "^3.0.2", + "rollup": "3.29.5", + "rollup-plugin-dts": "^5.0.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-terser": "^7.0.2", + "typescript": "^4.8.4" + } + }, + "packages/bruno-filestore/node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "packages/bruno-filestore/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "packages/bruno-filestore/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/bruno-filestore/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "packages/bruno-filestore/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/bruno-filestore/node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "packages/bruno-graphql-docs": { "name": "@usebruno/graphql-docs", "version": "0.1.0", @@ -32887,9 +32129,7 @@ "@rollup/plugin-commonjs": "^23.0.2", "@rollup/plugin-node-resolve": "^15.0.1", "rollup": "3.29.5", - "rollup-plugin-terser": "^7.0.2", - "stream": "^0.0.2", - "util": "^0.12.5" + "rollup-plugin-terser": "^7.0.2" }, "peerDependencies": { "@usebruno/vm2": "^3.9.13" @@ -32955,17 +32195,26 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "@faker-js/faker": "^9.7.0", + "@grpc/grpc-js": "^1.13.3", + "@grpc/proto-loader": "^0.7.15", "@types/qs": "^6.9.18", - "axios": "^1.9.0" + "axios": "^1.9.0", + "grpc-reflection-js": "^0.3.0", + "is-ip": "^5.0.1", + "tough-cookie": "^6.0.0" }, "devDependencies": { "@babel/preset-env": "^7.22.0", "@babel/preset-typescript": "^7.22.0", + "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-typescript": "^9.0.2", "@types/jest": "^29.5.11", "babel-jest": "^29.7.0", + "builtin-modules": "^5.0.0", "jest": "^29.2.0", "rollup": "3.29.5", "rollup-plugin-dts": "^5.0.0", @@ -32974,6 +32223,22 @@ "typescript": "^4.8.4" } }, + "packages/bruno-requests/node_modules/@faker-js/faker": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", + "integrity": "sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, "packages/bruno-requests/node_modules/axios": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", @@ -32985,6 +32250,17 @@ "proxy-from-env": "^1.1.0" } }, + "packages/bruno-requests/node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, "packages/bruno-schema": { "name": "@usebruno/schema", "version": "0.7.0", diff --git a/package.json b/package.json index 55873fff4..fe8dafdc9 100644 --- a/package.json +++ b/package.json @@ -14,16 +14,19 @@ "packages/bruno-tests", "packages/bruno-toml", "packages/bruno-graphql-docs", - "packages/bruno-requests" + "packages/bruno-requests", + "packages/bruno-filestore" ], "homepage": "https://usebruno.com", "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", "@playwright/test": "^1.51.1", + "@rollup/plugin-json": "^6.1.0", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", "@types/node": "^22.14.1", + "@typescript-eslint/parser": "^8.39.0", "concurrently": "^8.2.2", "eslint": "^9.26.0", "fs-extra": "^11.1.1", @@ -40,6 +43,7 @@ "setup": "node ./scripts/setup.js", "watch:converters": "npm run watch --workspace=packages/bruno-converters", "dev": "concurrently --kill-others \"npm run dev:web\" \"npm run dev:electron\"", + "watch": "npm run dev:watch", "dev:watch": "node ./scripts/dev-hot-reload.js", "dev:web": "npm run dev --workspace=packages/bruno-app", "build:web": "npm run build --workspace=packages/bruno-app", @@ -48,6 +52,7 @@ "dev:electron:debug": "npm run debug --workspace=packages/bruno-electron", "build:bruno-common": "npm run build --workspace=packages/bruno-common", "build:bruno-requests": "npm run build --workspace=packages/bruno-requests", + "build:bruno-filestore": "npm run build --workspace=packages/bruno-filestore", "build:bruno-converters": "npm run build --workspace=packages/bruno-converters", "build:bruno-query": "npm run build --workspace=packages/bruno-query", "build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs", @@ -72,4 +77,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 5e0f6a8ea..aac3dbab7 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -111,5 +111,10 @@ "tailwindcss": "^3.4.1", "webpack": "^5.64.4", "webpack-cli": "^4.9.1" + }, + "overrides": { + "httpsnippet": { + "form-data": "4.0.4" + } } } diff --git a/packages/bruno-app/rsbuild.config.mjs b/packages/bruno-app/rsbuild.config.mjs index b9b84129c..f21f80666 100644 --- a/packages/bruno-app/rsbuild.config.mjs +++ b/packages/bruno-app/rsbuild.config.mjs @@ -18,21 +18,6 @@ export default defineConfig({ } }) ], - dev: { - watchFiles: { - paths: [ - 'src/providers/**', - 'src/utils/**', - 'src/hooks/**', - 'src/themes/**', - 'src/selectors/**' - ], - options: { - usePolling: false, - interval: 1000, - }, - }, - }, source: { tsconfigPath: './jsconfig.json', // Specifies the path to the JavaScript/TypeScript configuration file, exclude: [ diff --git a/packages/bruno-app/src/components/CodeEditor/index.js b/packages/bruno-app/src/components/CodeEditor/index.js index a83535300..4a8f0591a 100644 --- a/packages/bruno-app/src/components/CodeEditor/index.js +++ b/packages/bruno-app/src/components/CodeEditor/index.js @@ -186,6 +186,8 @@ export default class CodeEditor extends React.Component { if (editor) { editor.setOption('lint', this.props.mode && editor.getValue().trim().length > 0 ? this.lintOptions : false); editor.on('change', this._onEdit); + editor.on('scroll', this.onScroll); + editor.scrollTo(null, this.props.initialScroll); this.addOverlay(); const getAllVariablesHandler = () => getAllVariables(this.props.collection, this.props.item); @@ -230,12 +232,18 @@ export default class CodeEditor extends React.Component { if (this.props.theme !== prevProps.theme && this.editor) { this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default'); } + + if (this.props.initialScroll !== prevProps.initialScroll) { + this.editor.scrollTo(null, this.props.initialScroll); + } + this.ignoreChangeEvent = false; } componentWillUnmount() { if (this.editor) { this.editor.off('change', this._onEdit); + this.editor.off('scroll', this.onScroll); this.editor = null; } @@ -271,6 +279,8 @@ export default class CodeEditor extends React.Component { this.editor.setOption('mode', 'brunovariables'); }; + onScroll = (event) => this.props.onScroll?.(event); + _onEdit = () => { if (!this.ignoreChangeEvent && this.editor) { this.editor.setOption('lint', this.editor.getValue().trim().length > 0 ? this.lintOptions : false); diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/AwsV4Auth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/AwsV4Auth/index.js index 0f14a4dfa..12e35cfea 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/AwsV4Auth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/AwsV4Auth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const AwsV4Auth = ({ collection }) => { const { storedTheme } = useTheme(); const awsv4Auth = get(collection, 'root.request.auth.awsv4', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(awsv4Auth?.secretAccessKey); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -131,7 +135,7 @@ const AwsV4Auth = ({ collection }) => { -
+
{ collection={collection} isSecret={true} /> + {showWarning && }
diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/BasicAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/BasicAuth/index.js index 9ea532646..d0cf9d722 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/BasicAuth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/BasicAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const BasicAuth = ({ collection }) => { const { storedTheme } = useTheme(); const basicAuth = get(collection, 'root.request.auth.basic', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(basicAuth?.password); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -55,7 +59,7 @@ const BasicAuth = ({ collection }) => {
-
+
{ collection={collection} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/BearerAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/BearerAuth/index.js index 82f8be12c..788182479 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/BearerAuth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/BearerAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const BearerAuth = ({ collection }) => { const { storedTheme } = useTheme(); const bearerToken = get(collection, 'root.request.auth.bearer.token', ''); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(bearerToken); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -30,7 +34,7 @@ const BearerAuth = ({ collection }) => { return ( -
+
{ collection={collection} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/DigestAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/DigestAuth/index.js index 582b17b82..22981f56b 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/DigestAuth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/DigestAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const DigestAuth = ({ collection }) => { const { storedTheme } = useTheme(); const digestAuth = get(collection, 'root.request.auth.digest', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(digestAuth?.password); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -55,7 +59,7 @@ const DigestAuth = ({ collection }) => {
-
+
{ collection={collection} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js index 173c99a12..38a9c18f0 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/NTLMAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -18,6 +20,8 @@ const NTLMAuth = ({ collection }) => { const { storedTheme } = useTheme(); const ntlmAuth = get(collection, 'root.request.auth.ntlm', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(ntlmAuth?.password); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -82,7 +86,7 @@ const NTLMAuth = ({ collection }) => {
-
+
{ collection={collection} isSecret={true} /> + {showWarning && }
diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/WsseAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/WsseAuth/index.js index 2e1a2c65c..226cedd7b 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/WsseAuth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/WsseAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const WsseAuth = ({ collection }) => { const { storedTheme } = useTheme(); const wsseAuth = get(collection, 'root.request.auth.wsse', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(wsseAuth?.password); const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); @@ -55,14 +59,16 @@ const WsseAuth = ({ collection }) => {
-
+
handlePasswordChange(val)} collection={collection} + isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/StyledWrapper.js index 621b2b86b..affe1f7db 100644 --- a/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/StyledWrapper.js +++ b/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/StyledWrapper.js @@ -38,6 +38,48 @@ const StyledWrapper = styled.div` outline: none !important; } } + + .protocol-placeholder { + height: 100%; + position: relative; + display: inline-block; + width: 60px; + overflow: hidden; + } + + .protocol-https, + .protocol-grpcs { + position: absolute; + right: 8px; + top: 0; + bottom: 0; + transition: transform 0.3s ease-in-out; + display: flex; + align-items: center; + justify-content: center; + } + + .protocol-https { + animation: slideUpDown 6s infinite; + transform: translateY(0); + } + + .protocol-grpcs { + animation: slideUpDown 6s infinite 3s; + transform: translateY(100%); + } + + @keyframes slideUpDown { + 0%, 45% { + transform: translateY(0); + } + 50%, 95% { + transform: translateY(100%); + } + 100% { + transform: translateY(0); + } + } `; export default StyledWrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js b/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js index 7ae9ac85e..3f62fc23e 100644 --- a/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js @@ -132,7 +132,10 @@ const ClientCertSettings = ({ root, clientCertConfig, onUpdate, onRemove }) => {
- https:// + + https:// + grpcs:// +
{ } return ( - +
diff --git a/packages/bruno-app/src/components/CollectionSettings/Grpc/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Grpc/StyledWrapper.js new file mode 100644 index 000000000..21adc36e1 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Grpc/StyledWrapper.js @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .available-certificates { + background-color: ${(props) => props.theme.requestTabPanel.url.bg}; + + button.remove-certificate { + color: ${(props) => props.theme.colors.text.danger}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/CollectionSettings/Grpc/index.js b/packages/bruno-app/src/components/CollectionSettings/Grpc/index.js new file mode 100644 index 000000000..db2313efd --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Grpc/index.js @@ -0,0 +1,263 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { useFormik } from 'formik'; +import { useDispatch } from 'react-redux'; +import StyledWrapper from './StyledWrapper'; +import toast from 'react-hot-toast'; +import { updateBrunoConfig } from 'providers/ReduxStore/slices/collections/actions'; +import cloneDeep from 'lodash/cloneDeep'; +import { IconTrash, IconFile, IconFileImport, IconAlertCircle } from '@tabler/icons'; +import { getRelativePath, getBasename, getDirPath } from 'utils/common/path'; +import { Tooltip } from 'react-tooltip'; +import { existsSync, resolvePath } from '../../../utils/filesystem'; + +const GrpcSettings = ({ collection }) => { + const dispatch = useDispatch(); + const { + brunoConfig: { grpc: grpcConfig = {} } + } = collection; + + const fileInputRef = useRef(null); + const [protoFileValidity, setProtoFileValidity] = useState({}); + + const formik = useFormik({ + enableReinitialize: true, + initialValues: { + protoFiles: grpcConfig.protoFiles || [] + }, + onSubmit: (newGrpcConfig) => { + const brunoConfig = cloneDeep(collection.brunoConfig); + brunoConfig.grpc = newGrpcConfig; + dispatch(updateBrunoConfig(brunoConfig, collection.uid)); + toast.success('gRPC settings updated'); + } + }); + + // Get file path using the ipcRenderer + const getProtoFile = (event) => { + const files = event?.files; + if (files && files.length > 0) { + const newProtoFiles = [...formik.values.protoFiles]; + + for (let i = 0; i < files.length; i++) { + const filePath = window?.ipcRenderer?.getFilePath(files[i]); + if (filePath) { + const relativePath = getRelativePath(filePath, collection.pathname); + const protoFileObj = { + path: relativePath, + type: 'file' + }; + + // Check if this path already exists + const exists = newProtoFiles.some(pf => pf.path === protoFileObj.path); + if (!exists) { + newProtoFiles.push(protoFileObj); + } + } + } + + formik.setFieldValue('protoFiles', newProtoFiles); + // Reset the file input + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + } + }; + + // Handler for removing a proto file + const handleRemoveProtoFile = (index) => { + const updatedProtoFiles = [...formik.values.protoFiles]; + updatedProtoFiles.splice(index, 1); + formik.setFieldValue('protoFiles', updatedProtoFiles); + }; + + // Handle the browse button click + const handleBrowseClick = () => { + if (fileInputRef.current) { + fileInputRef.current.click(); + } + }; + + // Check if a proto file path is valid + const isProtoFileValid = async (protoFile) => { + try { + const absolutePath = await resolvePath(protoFile.path, collection.pathname); + return await existsSync(absolutePath); + } catch (error) { + return false; + } + }; + + // Validate all proto files and update state + useEffect(() => { + const validateProtoFiles = async () => { + const validityMap = {}; + for (const file of formik.values.protoFiles) { + validityMap[file.path] = await isProtoFileValid(file); + } + setProtoFileValidity(validityMap); + }; + + validateProtoFiles(); + }, [formik.values.protoFiles, collection.pathname]); + + // Handle replacing an invalid proto file + const handleReplaceProtoFile = (index) => { + if (fileInputRef.current) { + fileInputRef.current.click(); + // Store the index to replace after file selection + fileInputRef.current.dataset.replaceIndex = index; + } + }; + + // Handle file input change + const handleFileInputChange = (e) => { + const replaceIndex = e.target.dataset.replaceIndex; + if (replaceIndex !== undefined) { + // Handle replacement + const files = e.target.files; + if (files && files.length > 0) { + const filePath = window?.ipcRenderer?.getFilePath(files[0]); + if (filePath) { + const relativePath = getRelativePath(filePath, collection.pathname); + const updatedProtoFiles = [...formik.values.protoFiles]; + updatedProtoFiles[replaceIndex] = { + path: relativePath, + type: 'file' + }; + formik.setFieldValue('protoFiles', updatedProtoFiles); + } + } + delete e.target.dataset.replaceIndex; + } else { + getProtoFile(e.target); + } + }; + + return ( + +
+
+ +
+ {/* Hidden file input for file selection */} + + +
+ {/* File selection options */} +
+
+ +
+
+ + {/* Divider */} +
+ + {/* List of added proto files */} +
+
+ + Added Proto Files ({formik.values.protoFiles.length}) +
+ + {formik.values.protoFiles.length === 0 ? ( +
No proto files added yet
+ ) : ( + <> + {formik.values.protoFiles.some(file => !protoFileValidity[file.path]) && ( +
+ + Some proto files cannot be found at their specified paths. Use the "Replace" option to update their locations. +
+ )} +
    + {formik.values.protoFiles.map((file, index) => { + const isValid = protoFileValidity[file.path]; + return ( +
  • +
    +
    + +
    + {getBasename(file.path)} + + {getDirPath(file.path)} + +
    +
    +
    + {!isValid && ( +
    + + +
    + )} + +
    +
    +
  • + ); + })} +
+ + )} +
+
+
+
+ +
+ +
+
+
+ ); +}; + +export default GrpcSettings; \ No newline at end of file diff --git a/packages/bruno-app/src/components/CollectionSettings/Headers/index.js b/packages/bruno-app/src/components/CollectionSettings/Headers/index.js index 9ae6e1e07..d0968c425 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Headers/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Headers/index.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import get from 'lodash/get'; import cloneDeep from 'lodash/cloneDeep'; import { IconTrash } from '@tabler/icons'; @@ -7,19 +7,30 @@ import { useTheme } from 'providers/Theme'; import { addCollectionHeader, updateCollectionHeader, - deleteCollectionHeader + deleteCollectionHeader, + setCollectionHeaders } from 'providers/ReduxStore/slices/collections'; import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; import SingleLineEditor from 'components/SingleLineEditor'; import StyledWrapper from './StyledWrapper'; import { headers as StandardHTTPHeaders } from 'know-your-http-well'; import { MimeTypes } from 'utils/codemirror/autocompleteConstants'; +import BulkEditor from 'components/BulkEditor/index'; const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header); const Headers = ({ collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const headers = get(collection, 'root.request.headers', []); + const [isBulkEditMode, setIsBulkEditMode] = useState(false); + + const toggleBulkEditMode = () => { + setIsBulkEditMode(!isBulkEditMode); + }; + + const handleBulkHeadersChange = (newHeaders) => { + dispatch(setCollectionHeaders({ collectionUid: collection.uid, headers: newHeaders })); + }; const addHeader = () => { dispatch( @@ -63,6 +74,22 @@ const Headers = ({ collection }) => { ); }; + if (isBulkEditMode) { + return ( + +
+ Add request headers that will be sent with every request in this collection. +
+ +
+ ); + } + return (
@@ -141,9 +168,14 @@ const Headers = ({ collection }) => { : null} - +
+ + +
@@ -74,7 +98,7 @@ const PresetsSettings = ({ collection }) => { id="request-url" type="text" name="requestUrl" - placeholder='Request URL' + placeholder="Request URL" className="block textbox" autoComplete="off" autoCorrect="off" @@ -87,6 +111,7 @@ const PresetsSettings = ({ collection }) => {
+
@@ -239,4 +239,4 @@ const RequestDetailsPanel = () => { ); }; -export default RequestDetailsPanel; \ No newline at end of file +export default RequestDetailsPanel; diff --git a/packages/bruno-app/src/components/Devtools/Console/index.js b/packages/bruno-app/src/components/Devtools/Console/index.js index bde9a8b1d..e87e38d37 100644 --- a/packages/bruno-app/src/components/Devtools/Console/index.js +++ b/packages/bruno-app/src/components/Devtools/Console/index.js @@ -26,7 +26,7 @@ import { } from 'providers/ReduxStore/slices/logs'; import NetworkTab from './NetworkTab'; import RequestDetailsPanel from './RequestDetailsPanel'; -import DebugTab from './DebugTab'; +// import DebugTab from './DebugTab'; import ErrorDetailsPanel from './ErrorDetailsPanel'; import StyledWrapper from './StyledWrapper'; @@ -40,8 +40,8 @@ const LogIcon = ({ type }) => { return ; case 'info': return ; - case 'debug': - return ; + // case 'debug': + // return ; default: return ; } @@ -384,8 +384,8 @@ const Console = () => { ); case 'network': return ; - case 'debug': - return ; + // case 'debug': + // return ; default: return ( {
); - case 'debug': - return ( -
-
- {debugErrors.length > 0 && ( - - )} -
-
- ); + // case 'debug': + // return ( + //
+ //
+ // {debugErrors.length > 0 && ( + // + // )} + //
+ //
+ // ); default: return null; } @@ -484,13 +484,13 @@ const Console = () => { Network - + */}
diff --git a/packages/bruno-app/src/components/Dropdown/StyledWrapper.js b/packages/bruno-app/src/components/Dropdown/StyledWrapper.js index f784cf527..3da7f2d34 100644 --- a/packages/bruno-app/src/components/Dropdown/StyledWrapper.js +++ b/packages/bruno-app/src/components/Dropdown/StyledWrapper.js @@ -16,6 +16,7 @@ const Wrapper = styled.div` border-radius: 3px; max-height: 90vh; overflow-y: auto; + max-width: unset !important; .tippy-content { padding-left: 0; diff --git a/packages/bruno-app/src/components/Dropdown/index.js b/packages/bruno-app/src/components/Dropdown/index.js index 3deb0e849..c4eccce64 100644 --- a/packages/bruno-app/src/components/Dropdown/index.js +++ b/packages/bruno-app/src/components/Dropdown/index.js @@ -2,7 +2,7 @@ import React from 'react'; import Tippy from '@tippyjs/react'; import StyledWrapper from './StyledWrapper'; -const Dropdown = ({ icon, children, onCreate, placement, transparent }) => { +const Dropdown = ({ icon, children, onCreate, placement, transparent, ...props }) => { return ( { interactive={true} trigger="click" appendTo="parent" + {...props} > {icon} diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js new file mode 100644 index 000000000..c7dbe0ef5 --- /dev/null +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js @@ -0,0 +1,11 @@ +const sensitiveFields = [ + 'request.auth.oauth2.clientSecret', + 'request.auth.basic.password', + 'request.auth.digest.password', + 'request.auth.wsse.password', + 'request.auth.ntlm.password', + 'request.auth.awsv4.secretAccessKey', + 'request.auth.bearer.token' +]; + +export { sensitiveFields }; diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index c777aa85f..5ba3b0797 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -1,5 +1,6 @@ -import React, { useRef, useEffect } from 'react'; +import React, { useRef, useEffect, useMemo } from 'react'; import cloneDeep from 'lodash/cloneDeep'; +import { get } from 'lodash'; import { IconTrash, IconAlertCircle, IconDeviceFloppy, IconRefresh, IconCircleCheck } from '@tabler/icons'; import { useTheme } from 'providers/Theme'; import { useDispatch, useSelector } from 'react-redux'; @@ -13,7 +14,9 @@ import { variableNameRegex } from 'utils/common/regex'; import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions'; import toast from 'react-hot-toast'; import { Tooltip } from 'react-tooltip'; -import { getGlobalEnvironmentVariables } from 'utils/collections'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { getGlobalEnvironmentVariables, flattenItems, isItemARequest } from 'utils/collections'; +import { sensitiveFields } from './constants'; const EnvironmentVariables = ({ environment, collection, setIsModified, originalEnvironmentVariables, onClose }) => { const dispatch = useDispatch(); @@ -26,6 +29,50 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original const globalEnvironmentVariables = getGlobalEnvironmentVariables({ globalEnvironments, activeGlobalEnvironmentUid }); _collection.globalEnvironmentVariables = globalEnvironmentVariables; + const nonSecretSensitiveVarUsageMap = useMemo(() => { + const result = {}; + if (!collection || !environment?.variables) { + return result; + } + const nonSecretVars = environment.variables.filter((v) => v.enabled && !v.secret && v.name); + if (!nonSecretVars.length) { + return result; + } + const varNames = new Set(nonSecretVars.map((v) => v.name)); + + const checkSensitiveField = (obj, fieldPath) => { + const value = get(obj, fieldPath); + if (typeof value === 'string') { + varNames.forEach((varName) => { + if (new RegExp(`\{\{\s*${varName}\s*\}\}`).test(value)) { + result[varName] = true; + } + }); + } + }; + + const getObjectToProcess = (item) => { + if (isItemARequest(item)) { + return item.draft || item; + } + return item.root; + }; + + const collectionObj = getObjectToProcess(collection); + sensitiveFields.forEach((fieldPath) => { + checkSensitiveField(collectionObj, fieldPath); + }); + + const items = flattenItems(collection.items || []); + items.forEach((item) => { + const objToProcess = getObjectToProcess(item); + sensitiveFields.forEach((fieldPath) => { + checkSensitiveField(objToProcess, fieldPath); + }); + }); + return result; + }, [collection, environment]); + const formik = useFormik({ enableReinitialize: true, initialValues: environment.variables || [], @@ -61,6 +108,8 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original } }); + const hasSensitiveUsage = (name) => !!nonSecretSensitiveVarUsageMap[name]; + // Effect to track modifications. React.useEffect(() => { setIsModified(formik.dirty); @@ -163,7 +212,7 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original
- +
formik.setFieldValue(`${index}.value`, newValue, true)} />
+ {!variable.secret && hasSensitiveUsage(variable.name) && ( + + )} e.header); const Headers = ({ collection, folder }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const headers = get(folder, 'root.request.headers', []); + const [isBulkEditMode, setIsBulkEditMode] = useState(false); + + const toggleBulkEditMode = () => { + setIsBulkEditMode(!isBulkEditMode); + }; + + const handleBulkHeadersChange = (newHeaders) => { + dispatch(setFolderHeaders({ collectionUid: collection.uid, folderUid: folder.uid, headers: newHeaders })); + }; const addHeader = () => { dispatch( @@ -62,6 +72,22 @@ const Headers = ({ collection, folder }) => { ); }; + if (isBulkEditMode) { + return ( + +
+ Request headers that will be sent with every request inside this folder. +
+ +
+ ); + } + return (
@@ -141,9 +167,14 @@ const Headers = ({ collection, folder }) => { : null} - +
+ + +
+
+ + + ); +}; + +export default Beta; diff --git a/packages/bruno-app/src/components/Preferences/index.js b/packages/bruno-app/src/components/Preferences/index.js index 2319d4c78..2814ed855 100644 --- a/packages/bruno-app/src/components/Preferences/index.js +++ b/packages/bruno-app/src/components/Preferences/index.js @@ -7,6 +7,7 @@ import General from './General'; import Proxy from './ProxySettings'; import Display from './Display'; import Keybindings from './Keybindings'; +import Beta from './Beta'; import StyledWrapper from './StyledWrapper'; @@ -37,6 +38,10 @@ const Preferences = ({ onClose }) => { return ; } + case 'beta': { + return ; + } + case 'support': { return ; } @@ -46,7 +51,7 @@ const Preferences = ({ onClose }) => { return ( -
+
setTab('general')}> General @@ -63,6 +68,9 @@ const Preferences = ({ onClose }) => {
setTab('support')}> Support
+
setTab('beta')}> + Beta +
{getTabPanel(tab)}
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AwsV4Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/AwsV4Auth/index.js index 35ae35d7a..aec7ee3de 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/AwsV4Auth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/AwsV4Auth/index.js @@ -6,13 +6,16 @@ import SingleLineEditor from 'components/SingleLineEditor'; import { updateAuth } from 'providers/ReduxStore/slices/collections'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; -import { update } from 'lodash'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; const AwsV4Auth = ({ item, collection, updateAuth, request, save }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const awsv4Auth = get(request, 'auth.awsv4', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(awsv4Auth?.secretAccessKey); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -144,7 +147,7 @@ const AwsV4Auth = ({ item, collection, updateAuth, request, save }) => {
-
+
{ item={item} isSecret={true} /> + + {showWarning && }
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/BasicAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/BasicAuth/index.js index 752d7ce33..355b2bfd1 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/BasicAuth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/BasicAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const BasicAuth = ({ item, collection, updateAuth, request, save }) => { const { storedTheme } = useTheme(); const basicAuth = get(request, 'auth.basic', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(basicAuth?.password); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -63,7 +67,7 @@ const BasicAuth = ({ item, collection, updateAuth, request, save }) => {
-
+
{ item={item} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/BearerAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/BearerAuth/index.js index c8ba9d1c6..12d65cdbc 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/BearerAuth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/BearerAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -13,6 +15,8 @@ const BearerAuth = ({ item, collection, updateAuth, request, save }) => { // Use the request prop directly like OAuth2ClientCredentials does const bearerToken = get(request, 'auth.bearer.token', ''); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(bearerToken); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -36,7 +40,7 @@ const BearerAuth = ({ item, collection, updateAuth, request, save }) => { return ( -
+
{ item={item} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/DigestAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/DigestAuth/index.js index a4ff3012e..e872894d8 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/DigestAuth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/DigestAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -11,6 +13,8 @@ const DigestAuth = ({ item, collection, updateAuth, request, save }) => { const { storedTheme } = useTheme(); const digestAuth = get(request, 'auth.digest', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(digestAuth?.password); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -62,7 +66,7 @@ const DigestAuth = ({ item, collection, updateAuth, request, save }) => {
-
+
{ item={item} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js index 44f87656e..b1cdf474c 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/NTLMAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const NTLMAuth = ({ item, collection, request, save, updateAuth }) => { const { storedTheme } = useTheme(); const ntlmAuth = get(request, 'auth.ntlm', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(ntlmAuth?.password); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -80,7 +84,7 @@ const NTLMAuth = ({ item, collection, request, save, updateAuth }) => {
-
+
{ item={item} isSecret={true} /> + {showWarning && }
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/StyledWrapper.js new file mode 100644 index 000000000..712367fd7 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/StyledWrapper.js @@ -0,0 +1,66 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .tabs { + .tab { + cursor: pointer; + padding: 4px 8px !important; + font-size: 12px; + border-radius: 4px; + + &:hover { + background-color: ${(props) => props.theme.mode === 'dark' ? 'rgba(99, 102, 241, 0.1)' : 'rgba(99, 102, 241, 0.1)'}; + } + + &.active { + background-color: ${(props) => props.theme.mode === 'dark' ? 'rgba(99, 102, 241, 0.2)' : 'rgba(99, 102, 241, 0.1)'}; + color: ${(props) => props.theme.mode === 'dark' ? '#6366f1' : '#4f46e5'}; + font-weight: 500; + } + } + } + + table { + width: 100%; + border-collapse: collapse; + font-weight: 600; + table-layout: fixed; + + thead, + td { + border: 1px solid ${(props) => props.theme.table.border}; + } + + thead { + color: ${(props) => props.theme.table.thead.color}; + font-size: 0.8125rem; + user-select: none; + } + td { + padding: 6px 10px; + } + } + + .additional-parameter-sends-in-selector { + select { + height: 32px; + width: 100%; + border: 1px solid ${(props) => props.theme.input.border}; + border-radius: 4px; + padding: 0 8px; + + &:focus { + outline: none; + border-color: ${(props) => props.theme.mode === 'dark' ? '#6366f1' : '#4f46e5'}; + } + } + } + + .add-additional-param-actions { + &:hover { + color: ${(props) => props.theme.mode === 'dark' ? '#6366f1' : '#4f46e5'}; + } + } +` + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/index.js new file mode 100644 index 000000000..1d2f81bee --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AdditionalParams/index.js @@ -0,0 +1,306 @@ +import { useDispatch } from "react-redux"; +import React, { useState } from 'react'; +import get from 'lodash/get'; +import { useTheme } from 'providers/Theme'; +import { IconPlus, IconTrash, IconAdjustmentsHorizontal } from '@tabler/icons'; +import { cloneDeep } from "lodash"; +import SingleLineEditor from "components/SingleLineEditor/index"; +import StyledWrapper from "./StyledWrapper"; +import Table from "components/Table/index"; + +const AdditionalParams = ({ item = {}, request, updateAuth, collection, handleSave }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + + const oAuth = get(request, 'auth.oauth2', {}); + const { + grantType, + additionalParameters = {} + } = oAuth; + + const [activeTab, setActiveTab] = useState( + (grantType == 'authorization_code' || grantType == 'implicit') ? 'authorization' : 'token' + ); + + const isEmptyParam = (param) => { + return !param.name.trim() && !param.value.trim(); + }; + + const hasEmptyRow = () => { + const tabParams = additionalParameters[activeTab] || []; + return tabParams.some(isEmptyParam); + }; + + const updateAdditionalParameters = ({ updatedAdditionalParameters }) => { + const filteredParams = cloneDeep(updatedAdditionalParameters); + + Object.keys(filteredParams).forEach(paramType => { + if (filteredParams[paramType]?.length) { + filteredParams[paramType] = filteredParams[paramType].filter(param => + param.name.trim() || param.value.trim() + ); + + if (filteredParams[paramType].length === 0) { + delete filteredParams[paramType]; + } + } else if (Array.isArray(filteredParams[paramType]) && filteredParams[paramType].length === 0) { + // Remove empty arrays + delete filteredParams[paramType]; + } + }); + + dispatch( + updateAuth({ + mode: 'oauth2', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + ...oAuth, + additionalParameters: Object.keys(filteredParams).length > 0 ? filteredParams : undefined + } + }) + ); + } + + const handleUpdateAdditionalParam = ({ paramType, key, paramIndex, value }) => { + const updatedAdditionalParameters = cloneDeep(additionalParameters); + + if (!updatedAdditionalParameters[paramType]) { + updatedAdditionalParameters[paramType] = []; + } + + if (!updatedAdditionalParameters[paramType][paramIndex]) { + updatedAdditionalParameters[paramType][paramIndex] = { + name: '', + value: '', + sendIn: 'headers', + enabled: true + }; + } + + updatedAdditionalParameters[paramType][paramIndex][key] = value; + + // Only filter when updating a parameter + updateAdditionalParameters({ updatedAdditionalParameters }); + } + + const handleDeleteAdditionalParam = ({ paramType, paramIndex }) => { + const updatedAdditionalParameters = cloneDeep(additionalParameters); + + if (updatedAdditionalParameters[paramType]?.length) { + updatedAdditionalParameters[paramType] = updatedAdditionalParameters[paramType].filter((_, index) => index !== paramIndex); + + // If the array is now empty, ensure we're not sending empty arrays + if (updatedAdditionalParameters[paramType].length === 0) { + delete updatedAdditionalParameters[paramType]; + } + } + + updateAdditionalParameters({ updatedAdditionalParameters }); + } + + const handleAddNewAdditionalParam = () => { + // Prevent adding multiple empty rows + if (hasEmptyRow()) { + return; + } + + const paramType = activeTab; + const localAdditionalParameters = cloneDeep(additionalParameters); + + if (!localAdditionalParameters[paramType]) { + localAdditionalParameters[paramType] = []; + } + + localAdditionalParameters[paramType] = [ + ...localAdditionalParameters[paramType], + { + name: '', + value: '', + sendIn: 'headers', + enabled: true + } + ]; + + // Don't filter here to allow the empty row to display in UI + // But don't permanently store it in state until it has values + dispatch( + updateAuth({ + mode: 'oauth2', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + ...oAuth, + additionalParameters: localAdditionalParameters, + } + }) + ); + } + + // Add a class to the Add Parameter button if it's disabled + const addButtonDisabled = hasEmptyRow(); + + // Define available tabs for each grant type + const getAvailableTabs = (grantType) => { + const tabConfig = { + 'authorization_code': ['authorization', 'token', 'refresh'], + 'implicit': ['authorization'], + 'password': ['token', 'refresh'], + 'client_credentials': ['token', 'refresh'] + }; + return tabConfig[grantType] || ['token', 'refresh']; + }; + + const availableTabs = getAvailableTabs(grantType); + + const renderTab = (tabKey, tabLabel) => ( +
setActiveTab(tabKey)} + > + {tabLabel} +
+ ); + + return ( + +
+
+ +
+ + Additional Parameters + +
+ +
+ {availableTabs.includes('authorization') && renderTab('authorization', 'Authorization')} + {availableTabs.includes('token') && renderTab('token', 'Token')} + {availableTabs.includes('refresh') && renderTab('refresh', 'Refresh')} +
+ + + {(additionalParameters?.[activeTab] || []).map((param, index) => + + + + + + + )} + +
+ handleUpdateAdditionalParam({ + paramType: activeTab, + key: 'name', + paramIndex: index, + value + })} + collection={collection} + onSave={handleSave} + /> + + handleUpdateAdditionalParam({ + paramType: activeTab, + key: 'value', + paramIndex: index, + value + })} + collection={collection} + onSave={handleSave} + /> + +
+ +
+
+
+ { + handleUpdateAdditionalParam({ + paramType: activeTab, + key: 'enabled', + paramIndex: index, + value: e.target.checked + }) + }} + /> + +
+
+
+ + Add Parameter +
+
+ ) +} + +export default AdditionalParams; + +const sendInOptionsMap = { + 'authorization_code': { + 'authorization': ['headers', 'queryparams'], + 'token': ['headers', 'queryparams', 'body'], + 'refresh': ['headers', 'queryparams', 'body'] + }, + 'password': { + 'token': ['headers', 'queryparams', 'body'], + 'refresh': ['headers', 'queryparams', 'body'] + }, + 'client_credentials': { + 'token': ['headers', 'queryparams', 'body'], + 'refresh': ['headers', 'queryparams', 'body'] + }, + 'implicit': { + 'authorization': ['headers', 'queryparams'] + } +} \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js index c00964d82..7c890513e 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js @@ -1,4 +1,5 @@ import React, { useRef, forwardRef } from 'react'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -9,13 +10,15 @@ import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; import Oauth2ActionButtons from '../Oauth2ActionButtons/index'; +import AdditionalParams from '../AdditionalParams/index'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAuth, collection, folder }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); - + const { isSensitive } = useDetectSensitiveField(collection); const oAuth = get(request, 'auth.oauth2', {}); const { callbackUrl, @@ -33,7 +36,8 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu tokenQueryKey, refreshTokenUrl, autoRefreshToken, - autoFetchToken + autoFetchToken, + additionalParameters } = oAuth; const refreshTokenUrlAvailable = refreshTokenUrl?.trim() !== ''; @@ -83,6 +87,7 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu refreshTokenUrl, autoRefreshToken, autoFetchToken, + additionalParameters, [key]: value, } }) @@ -110,6 +115,7 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu tokenHeaderPrefix, tokenQueryKey, autoFetchToken, + additionalParameters, pkce: !Boolean(oAuth?.['pkce']) } }) @@ -129,12 +135,15 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu
{inputsConfig.map((input) => { const { key, label, isSecret } = input; + const value = oAuth[key] || ''; + const { showWarning, warningMessage } = isSensitive(value); + return (
-
+
handleChange(key, val)} @@ -143,6 +152,7 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu item={item} isSecret={isSecret} /> + {isSecret && showWarning && }
); @@ -326,6 +336,13 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu
+
); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js index 64ab7c408..253adeac0 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js @@ -1,4 +1,5 @@ import React, { useRef, forwardRef } from 'react'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -9,13 +10,15 @@ import { inputsConfig } from './inputsConfig'; import Dropdown from 'components/Dropdown'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; import Oauth2ActionButtons from '../Oauth2ActionButtons/index'; +import AdditionalParams from '../AdditionalParams/index'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAuth, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); - + const { isSensitive } = useDetectSensitiveField(collection); const oAuth = get(request, 'auth.oauth2', {}); const { @@ -30,7 +33,8 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu tokenQueryKey, refreshTokenUrl, autoRefreshToken, - autoFetchToken + autoFetchToken, + additionalParameters } = oAuth; const refreshTokenUrlAvailable = refreshTokenUrl?.trim() !== ''; @@ -77,6 +81,7 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu refreshTokenUrl, autoRefreshToken, autoFetchToken, + additionalParameters, [key]: value } }) @@ -96,12 +101,15 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
{inputsConfig.map((input) => { const { key, label, isSecret } = input; + const value = oAuth[key] || ''; + const { showWarning, warningMessage } = isSensitive(value); + return (
-
+
handleChange(key, val)} @@ -110,6 +118,7 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu item={item} isSecret={isSecret} /> + {isSecret && showWarning && }
); @@ -284,7 +293,13 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
- +
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js index 3952180b0..9715aa3ce 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js @@ -1,16 +1,15 @@ -import React, { useRef, forwardRef, useState, useMemo } from 'react'; +import React, { useRef, forwardRef, useMemo } from 'react'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; -import { IconCaretDown, IconLoader2, IconSettings, IconKey, IconHelp, IconAdjustmentsHorizontal } from '@tabler/icons'; +import { IconCaretDown, IconSettings, IconKey, IconHelp, IconAdjustmentsHorizontal } from '@tabler/icons'; import Dropdown from 'components/Dropdown'; import SingleLineEditor from 'components/SingleLineEditor'; -import { clearOauth2Cache, fetchOauth2Credentials } from 'providers/ReduxStore/slices/collections/actions'; import Wrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; -import toast from 'react-hot-toast'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; -import { cloneDeep } from 'lodash'; +import Oauth2ActionButtons from '../Oauth2ActionButtons/index'; +import AdditionalParams from '../AdditionalParams/index'; import { getAllVariables } from 'utils/collections/index'; import { interpolate } from '@usebruno/common'; @@ -19,7 +18,6 @@ const OAuth2Implicit = ({ save, item = {}, request, handleRun, updateAuth, colle const { storedTheme } = useTheme(); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); - const [fetchingToken, toggleFetchingToken] = useState(false); const oAuth = get(request, 'auth.oauth2', {}); const { @@ -49,38 +47,6 @@ const OAuth2Implicit = ({ save, item = {}, request, handleRun, updateAuth, colle ); }); - const handleFetchOauth2Credentials = async () => { - let requestCopy = cloneDeep(request); - requestCopy.oauth2 = requestCopy?.auth.oauth2; - requestCopy.headers = {}; - toggleFetchingToken(true); - try { - const result = await dispatch(fetchOauth2Credentials({ - itemUid: item.uid, - request: requestCopy, - collection, - folderUid: folder?.uid || null, - forceGetToken: true - })); - - toggleFetchingToken(false); - - // Check if the result contains error or if access_token is missing - if (result?.error || !result?.access_token) { - const errorMessage = result?.error || 'No access token received from authorization server'; - toast.error(errorMessage); - return; - } - - toast.success('Token fetched successfully!'); - } - catch (error) { - console.error(error); - toggleFetchingToken(false); - toast.error(error?.message || 'An error occurred while fetching token!'); - } - } - const handleSave = () => { save(); }; const handleChange = (key, value) => { @@ -111,16 +77,6 @@ const OAuth2Implicit = ({ save, item = {}, request, handleRun, updateAuth, colle handleChange('autoFetchToken', e.target.checked); }; - const handleClearCache = (e) => { - dispatch(clearOauth2Cache({ collectionUid: collection?.uid, url: interpolatedAuthUrl, credentialsId })) - .then(() => { - toast.success('Cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( @@ -262,18 +218,14 @@ const OAuth2Implicit = ({ save, item = {}, request, handleRun, updateAuth, colle -
- - -
+ +
); }; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2ActionButtons/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2ActionButtons/index.js index de729fdd5..3d3dc697d 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2ActionButtons/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2ActionButtons/index.js @@ -99,12 +99,22 @@ const Oauth2ActionButtons = ({ item, request, collection, url: accessTokenUrl, c return (
- - {creds?.refresh_token ? : null} + {creds?.refresh_token ? + + : null} diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js index 385607848..f048183e6 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js @@ -1,4 +1,5 @@ import React, { useRef, forwardRef } from 'react'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -9,14 +10,16 @@ import { inputsConfig } from './inputsConfig'; import Dropdown from 'components/Dropdown'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; import Oauth2ActionButtons from '../Oauth2ActionButtons/index'; +import AdditionalParams from '../AdditionalParams/index'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning/index'; const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, updateAuth, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); - const oAuth = get(request, 'auth.oauth2', {}); + const { isSensitive } = useDetectSensitiveField(collection); const { accessTokenUrl, @@ -32,7 +35,8 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update tokenQueryKey, refreshTokenUrl, autoRefreshToken, - autoFetchToken + autoFetchToken, + additionalParameters } = oAuth; const refreshTokenUrlAvailable = refreshTokenUrl?.trim() !== ''; @@ -80,6 +84,7 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update refreshTokenUrl, autoRefreshToken, autoFetchToken, + additionalParameters, [key]: value } }) @@ -99,12 +104,15 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update
{inputsConfig.map((input) => { const { key, label, isSecret } = input; + const value = oAuth[key] || ''; + const { showWarning, warningMessage } = isSensitive(value); + return (
-
+
handleChange(key, val)} @@ -113,6 +121,7 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update item={item} isSecret={isSecret} /> + {isSecret && showWarning && }
); @@ -287,6 +296,13 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update
+ ); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/WsseAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/WsseAuth/index.js index 05e9daaf1..fde2310c9 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/WsseAuth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/WsseAuth/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; +import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -12,6 +14,8 @@ const WsseAuth = ({ item, collection, updateAuth, request, save }) => { const { storedTheme } = useTheme(); const wsseAuth = get(request, 'auth.wsse', {}); + const { isSensitive } = useDetectSensitiveField(collection); + const { showWarning, warningMessage } = isSensitive(wsseAuth?.password); const handleRun = () => dispatch(sendRequest(item, collection.uid)); @@ -63,7 +67,7 @@ const WsseAuth = ({ item, collection, updateAuth, request, save }) => { -
+
{ item={item} isSecret={true} /> + {showWarning && }
); diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js index c16f7bb68..7ccbc3e7d 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js @@ -109,7 +109,7 @@ const Auth = ({ item, collection }) => { }; return ( - +
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcBody/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcBody/StyledWrapper.js new file mode 100644 index 000000000..4b1ebcdb1 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcBody/StyledWrapper.js @@ -0,0 +1,59 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + width: 100%; + flex: 1; + /* height: 100%; */ + position: relative; + + .grpc-message-header { + .font-medium { + color: ${(props) => props.theme.text}; + } + + button { + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + + &:hover { + transform: scale(1.1); + } + + &:active { + transform: scale(0.95); + } + } + } + + #grpc-messages-container { + /* height: 100%; */ + position: relative; + } + + .add-message-btn-container { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding-top: 8px; + background: ${(props) => props.theme.bg || '#fff'}; + z-index: 15; + border-top: 1px solid ${(props) => props.theme.border || 'rgba(0, 0, 0, 0.1)'}; + + .add-message-btn { + width: 100%; + } + } + + .CodeMirror { + border-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } +`; + +export default Wrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/GrpcBody/index.js b/packages/bruno-app/src/components/RequestPane/GrpcBody/index.js new file mode 100644 index 000000000..ce7cdebd9 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcBody/index.js @@ -0,0 +1,354 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { get } from 'lodash'; +import { useDispatch, useSelector } from 'react-redux'; +import { useTheme } from 'providers/Theme'; +import { updateRequestBody } from 'providers/ReduxStore/slices/collections'; +import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; +import { sendGrpcMessage, generateGrpcSampleMessage } from 'utils/network/index'; +import useLocalStorage from 'hooks/useLocalStorage'; + +import CodeEditor from 'components/CodeEditor/index'; +import StyledWrapper from './StyledWrapper'; +import { IconSend, IconRefresh, IconWand, IconPlus, IconTrash, IconChevronDown, IconChevronUp } from '@tabler/icons'; +import ToolHint from 'components/ToolHint/index'; +import { toastError } from 'utils/common/error'; +import { format, applyEdits } from 'jsonc-parser'; +import toast from 'react-hot-toast' +import { getAbsoluteFilePath } from 'utils/common/path'; + +const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCollapsed, onToggleCollapse, handleRun, canClientSendMultipleMessages }) => { + const dispatch = useDispatch(); + const { displayedTheme, theme } = useTheme(); + const preferences = useSelector((state) => state.app.preferences); + const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body'); + const isConnectionActive = useSelector((state) => state.collections.activeConnections.includes(item.uid)); + + // Access gRPC method metadata from local storage + const [reflectionCache] = useLocalStorage('bruno.grpc.reflectionCache', {}); + const [protofileCache] = useLocalStorage('bruno.grpc.protofileCache', {}); + + const canClientStream = methodType === 'client-streaming' || methodType === 'bidi-streaming'; + + const { name, content } = message; + + const onEdit = (value) => { + const currentMessages = [...(body.grpc || [])]; + + currentMessages[index] = { + name: name ? name : `message ${index + 1}`, + content: value + }; + + dispatch( + updateRequestBody({ + content: currentMessages, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + }; + + const onSend = async () => { + try { + await sendGrpcMessage(item, collection.uid, content); + } catch (error) { + console.error('Error sending message:', error); + } + } + const onSave = () => dispatch(saveRequest(item.uid, collection.uid)); + + const onRegenerateMessage = async () => { + try { + const methodPath = item.draft?.request?.method || item.request?.method; + + if (!methodPath) { + toastError(new Error('Method path not found in request')); + return; + } + + // Get the URL and protoPath to determine which cache to use + const url = item.draft?.request?.url || item.request?.url; + const protoPath = item.draft?.request?.protoPath || item.request?.protoPath; + + // Find the method metadata from the appropriate cache + let methodMetadata = null; + if (protoPath) { + // Use protofile cache if protoPath is available + const absolutePath = getAbsoluteFilePath(protoPath, collection.pathname); + const cachedMethods = protofileCache[absolutePath]; + if (cachedMethods) { + methodMetadata = cachedMethods.find(method => method.path === methodPath); + } + } else if (url) { + // Use reflection cache if no protoPath (reflection mode) + const cachedMethods = reflectionCache[url]; + if (cachedMethods) { + methodMetadata = cachedMethods.find(method => method.path === methodPath); + } + } + + const result = await generateGrpcSampleMessage( + methodPath, + content, + { + arraySize: 2, + methodMetadata // Pass the method metadata to the function + } + ); + + if (result.success) { + const currentMessages = [...(body.grpc || [])]; + + currentMessages[index] = { + name: name ? name : `message ${index + 1}`, + content: result.message + }; + + dispatch( + updateRequestBody({ + content: currentMessages, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + + toast.success('Sample message generated successfully!'); + } else { + toastError(new Error(result.error || 'Failed to generate sample message')); + } + } catch (error) { + console.error('Error generating sample message:', error); + toastError(error); + } + }; + + const onDeleteMessage = () => { + const currentMessages = [...(body.grpc || [])]; + + currentMessages.splice(index, 1); + + dispatch( + updateRequestBody({ + content: currentMessages, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + }; + + const onPrettify = () => { + try { + const edits = format(content, undefined, { tabSize: 2, insertSpaces: true }); + const prettyBodyJson = applyEdits(content, edits); + + const currentMessages = [...(body.grpc || [])]; + currentMessages[index] = { + name: name ? name : `message ${index + 1}`, + content: prettyBodyJson + }; + dispatch( + updateRequestBody({ + content: currentMessages, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + } catch (e) { + toastError(new Error('Unable to prettify. Invalid JSON format.')); + } + }; + + const getContainerHeight = (canClientSendMultipleMessages && body.grpc.length > 1) ? `${isCollapsed ? "" : "h-80"}` : "h-full" + + return ( +
+
+
+ {isCollapsed ? + : + + } + {`Message ${canClientStream ? index + 1 : ''}`} +
+
e.stopPropagation()}> + + + + + + + + + {canClientStream && ( + + + + )} + + {index > 0 && ( + + + + )} +
+
+ + {!isCollapsed && ( +
+ +
+ )} +
+ ) +} + +const GrpcBody = ({ item, collection, handleRun }) => { + const preferences = useSelector((state) => state.app.preferences); + const isVerticalLayout = preferences?.layout?.responsePaneOrientation === 'vertical'; + const dispatch = useDispatch(); + const [collapsedMessages, setCollapsedMessages] = useState([]); + const messagesContainerRef = useRef(null); + const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body'); + + const methodType = item.draft ? get(item, 'draft.request.methodType') : get(item, 'request.methodType'); + const canClientSendMultipleMessages = methodType === 'client-streaming' || methodType === 'bidi-streaming'; + + // Auto-scroll to the latest message when messages are added + useEffect(() => { + if (messagesContainerRef.current && body?.grpc?.length > 0) { + const container = messagesContainerRef.current; + container.scrollTop = container.scrollHeight; + } + }, [body?.grpc?.length]); + + const toggleMessageCollapse = (index) => { + setCollapsedMessages(prev => { + if (prev.includes(index)) { + return prev.filter(i => i !== index); + } else { + return [...prev, index]; + } + }); + }; + + const addNewMessage = () => { + const currentMessages = Array.isArray(body.grpc) + ? [...body.grpc] + : []; + + currentMessages.push({ + name: `message ${currentMessages.length + 1}`, + content: '{}' + }); + + dispatch( + updateRequestBody({ + content: currentMessages, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + }; + + + if (!body?.grpc || !Array.isArray(body.grpc)) { + return ( + +
+

No gRPC messages available

+ + + +
+
+ ); + } + + return ( + +
+ {body.grpc + .filter((_, index) => canClientSendMultipleMessages || index === 0) + .map((message, index) => ( + toggleMessageCollapse(index)} + handleRun={handleRun} + canClientSendMultipleMessages={canClientSendMultipleMessages} + /> + ))} +
+ + {canClientSendMultipleMessages && ( +
+ + + +
+ )} +
+ ); +}; + +export default GrpcBody; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/StyledWrapper.js new file mode 100644 index 000000000..64c29be51 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/StyledWrapper.js @@ -0,0 +1,119 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + height: 2.3rem; + + .method-selector-container { + background-color: ${(props) => props.theme.requestTabPanel.url.bg}; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + + .input-container { + background-color: ${(props) => props.theme.requestTabPanel.url.bg}; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + + input { + background-color: ${(props) => props.theme.requestTabPanel.url.bg}; + outline: none; + box-shadow: none; + + &:focus { + outline: none !important; + box-shadow: none !important; + } + } + } + + .caret { + color: rgb(140, 140, 140); + fill: rgb(140 140 140); + position: relative; + top: 1px; + } + + .infotip { + position: relative; + display: inline-block; + cursor: pointer; + } + + .infotip:hover .infotip-text { + visibility: visible; + opacity: 1; + } + + .infotip-text { + visibility: hidden; + width: auto; + background-color: ${(props) => props.theme.requestTabs.active.bg}; + color: ${(props) => props.theme.text}; + text-align: center; + border-radius: 4px; + padding: 4px 8px; + position: absolute; + z-index: 1; + bottom: 34px; + left: 50%; + transform: translateX(-50%); + opacity: 0; + transition: opacity 0.3s; + white-space: nowrap; + } + + .infotip-text::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -4px; + border-width: 4px; + border-style: solid; + border-color: ${(props) => props.theme.requestTabs.active.bg} transparent transparent transparent; + } + + .shortcut { + font-size: 0.625rem; + } + + @keyframes pulse { + 0% { + opacity: 0.4; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.4; + } + } + + .connection-status-strip { + animation: pulse 1.5s ease-in-out infinite; + background-color: ${(props) => props.theme.colors.text.green}; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 2px; + } + + /* Method dropdown styling */ + .method-dropdown { + margin-right: 8px; + position: relative; + z-index: 10; + } + + .dropdown-item { + padding: 8px 12px; + cursor: pointer; + + &:hover { + background-color: ${(props) => props.theme.dropdown.hoverBg}; + } + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js new file mode 100644 index 000000000..150736ae3 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js @@ -0,0 +1,1028 @@ +import React, { useState, useEffect, useRef, forwardRef, useCallback, useMemo } from 'react'; +import get from 'lodash/get'; +import { useDispatch, useSelector } from 'react-redux'; +import { requestUrlChanged, updateRequestMethod, updateRequestProtoPath } from 'providers/ReduxStore/slices/collections'; +import { saveRequest, browseFiles, loadGrpcMethodsFromReflection, openCollectionSettings, generateGrpcurlCommand } from 'providers/ReduxStore/slices/collections/actions'; +import { useTheme } from 'providers/Theme'; +import SingleLineEditor from 'components/SingleLineEditor/index'; +import { isMacOS } from 'utils/common/platform'; +import { getRelativePath, getBasename, getAbsoluteFilePath } from 'utils/common/path'; +import useLocalStorage from 'hooks/useLocalStorage/index'; +import StyledWrapper from './StyledWrapper'; +import ToggleSwitch from 'components/ToggleSwitch/index'; +import { + IconX, + IconCheck, + IconRefresh, + IconDeviceFloppy, + IconArrowRight, + IconCode, + IconFile, + IconChevronDown, + IconSettings, + IconAlertCircle, + IconCopy +} from '@tabler/icons'; +import toast from 'react-hot-toast'; +import { + loadGrpcMethodsFromProtoFile, + cancelGrpcConnection, + endGrpcConnection +} from 'utils/network/index'; +import Dropdown from 'components/Dropdown/index'; +import { + IconGrpcUnary, + IconGrpcClientStreaming, + IconGrpcServerStreaming, + IconGrpcBidiStreaming +} from 'components/Icons/Grpc'; +import Modal from 'components/Modal/index'; +import CodeEditor from 'components/CodeEditor'; +import { debounce } from 'lodash'; +import { getPropertyFromDraftOrRequest } from 'utils/collections'; +import { existsSync } from 'utils/filesystem'; + +// Constants for gRPC method types +const STREAMING_METHOD_TYPES = ['client-streaming', 'server-streaming', 'bidi-streaming']; +const CLIENT_STREAMING_METHOD_TYPES = ['client-streaming', 'bidi-streaming']; + +const GrpcurlModal = ({ isOpen, onClose, command }) => { + const { displayedTheme } = useTheme(); + const [copied, setCopied] = useState(false); + const preferences = useSelector((state) => state.app.preferences); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(command); + setCopied(true); + toast.success('Command copied to clipboard'); + setTimeout(() => setCopied(false), 2000); + } catch (error) { + toast.error('Failed to copy command'); + } + }; + + return ( + + Generate gRPCurl Command + BETA +
+ } + size="lg" + hideFooter={true} + > +
+
+
+
+ +
+ +
+
+
+ + ); +}; + +const GrpcQueryUrl = ({ item, collection, handleRun }) => { + const { theme, storedTheme } = useTheme(); + const dispatch = useDispatch(); + const method = getPropertyFromDraftOrRequest(item, 'request.method'); + const type = getPropertyFromDraftOrRequest(item, 'request.type'); + const url = getPropertyFromDraftOrRequest(item, 'request.url', ''); + const protoPath = getPropertyFromDraftOrRequest(item, 'request.protoPath'); + const isMac = isMacOS(); + const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S'; + const editorRef = useRef(null); + const isConnectionActive = useSelector((state) => state.collections.activeConnections.includes(item.uid)); + const [protoFilePath, setProtoFilePath] = useState(protoPath); + const [grpcMethods, setGrpcMethods] = useState([]); + const [isLoadingMethods, setIsLoadingMethods] = useState(false); + const [selectedGrpcMethod, setSelectedGrpcMethod] = useState({ + path: method, + type: type + }); + const methodDropdownRef = useRef(); + const protoDropdownRef = useRef(); + const haveFetchedMethodsRef = useRef(false); + const [showGrpcurlModal, setShowGrpcurlModal] = useState(false); + const [grpcurlCommand, setGrpcurlCommand] = useState(''); + const [isReflectionMode, setIsReflectionMode] = useState(false); + const collectionProtoFiles = get(collection, 'brunoConfig.grpc.protoFiles', []); + const [reflectionCache, setReflectionCache] = useLocalStorage('bruno.grpc.reflectionCache', {}); + const [protofileCache, setProtofileCache] = useLocalStorage('bruno.grpc.protofileCache', {}); + const fileExistsCache = useRef(new Map()); + const [showProtoDropdown, setShowProtoDropdown] = useState(false); + + const fileExists = useCallback(async (filePath) => { + if (!filePath) return false; + + if (fileExistsCache.current.has(filePath)) { + return fileExistsCache.current.get(filePath); + } + + try { + const absolutePath = getAbsoluteFilePath(filePath, collection.pathname); + const exists = await existsSync(absolutePath); + fileExistsCache.current.set(filePath, exists); + return exists; + } catch (error) { + console.error('Error checking if file exists:', error); + return false; + } + }, [collection.pathname]); + + const [collectionProtoFilesExistence, setCollectionProtoFilesExistence] = useState([]); + + useEffect(() => { + const fetchCollectionProtoFilesExistence = async () => { + if (!collectionProtoFiles) return; + const existence = await Promise.all(collectionProtoFiles.map(async (protoFile) => { + const absolutePath = getAbsoluteFilePath(protoFile.path, collection.pathname); + const exists = await fileExists(absolutePath) + return { + path: protoFile.path, + absolutePath, + exists + } + })); + setCollectionProtoFilesExistence(existence); + }; + fetchCollectionProtoFilesExistence(); + }, [fileExists]); + + const invalidProtoFiles = useMemo(() => { + return collectionProtoFilesExistence.filter(file => !file.exists); + }, [collectionProtoFilesExistence]); + + const currentProtoFileExists = useMemo(() => { + return fileExists(protoFilePath); + }, [protoFilePath, fileExists]); + + const onMethodDropdownCreate = (ref) => (methodDropdownRef.current = ref); + const onProtoDropdownCreate = (ref) => (protoDropdownRef.current = ref); + + + const isStreamingMethod = selectedGrpcMethod && selectedGrpcMethod.type && STREAMING_METHOD_TYPES.includes(selectedGrpcMethod.type); + const isClientStreamingMethod = selectedGrpcMethod && selectedGrpcMethod.type && CLIENT_STREAMING_METHOD_TYPES.includes(selectedGrpcMethod.type); + + const onSave = (finalValue) => { + dispatch(saveRequest(item.uid, collection.uid)); + }; + + const onUrlChange = (value) => { + if (!editorRef.current?.editor) return; + const editor = editorRef.current.editor; + const cursor = editor.getCursor(); + + const finalUrl = value?.trim() || value; + + dispatch( + requestUrlChanged({ + itemUid: item.uid, + collectionUid: collection.uid, + url: finalUrl + }) + ); + + // Restore cursor position only if URL was trimmed + if (finalUrl !== value) { + setTimeout(() => { + if (editor) { + editor.setCursor(cursor); + } + }, 0); + } + + if(!protoFilePath && value) { + setIsReflectionMode(true); + handleReflection(finalUrl); + } + }; + + const onMethodSelect = ({ path, type }) => { + if (isConnectionActive) { + cancelGrpcConnection(item.uid) + .then(() => { + toast.success('gRPC connection cancelled'); + }) + .catch((err) => { + console.error('Failed to cancel gRPC connection:', err); + }); + } + + dispatch( + updateRequestMethod({ + method: path, + methodType: type, + itemUid: item.uid, + collectionUid: collection.uid + }) + ); + }; + + const handleReflection = async (url, isManualRefresh = false) => { + if (!url) return; + + const cachedMethods = reflectionCache[url]; + if (!isManualRefresh && cachedMethods && !isLoadingMethods) { + setGrpcMethods(cachedMethods); + setProtoFilePath(''); + setIsReflectionMode(true); + const isDuplicateSave = !item.request.protoPath; + if (!isDuplicateSave) { + dispatch(updateRequestProtoPath({ + protoPath: '', + itemUid: item.uid, + collectionUid: collection.uid + })); + } + + if (cachedMethods && cachedMethods.length > 0) { + const haveSelectedMethod = + selectedGrpcMethod && cachedMethods.some((method) => method.path === selectedGrpcMethod.path); + if (!haveSelectedMethod) { + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + } else if (selectedGrpcMethod) { + // Update the method type for the currently selected method to ensure it matches + const currentMethod = cachedMethods.find((method) => method.path === selectedGrpcMethod.path); + if (currentMethod) { + const methodType = currentMethod.type; + setSelectedGrpcMethod({ + path: selectedGrpcMethod.path, + type: methodType + }); + } + } + return; + } + } + + setIsLoadingMethods(true); + try { + const { methods, error } = await dispatch(loadGrpcMethodsFromReflection(item, collection.uid, url)); + + if (error) { + console.error('Error loading gRPC methods:', error); + toast.error(`Failed to load gRPC methods: ${error.message || 'Unknown error'}`); + return; + } + + // Cache the methods for this URL + setReflectionCache(prevCache => ({ + ...prevCache, + [url]: methods + })); + + setGrpcMethods(methods); + setProtoFilePath(''); + setIsReflectionMode(true); + const isDuplicateSave = !item.request.protoPath; + if (!isDuplicateSave) { + dispatch(updateRequestProtoPath({ + protoPath: '', + itemUid: item.uid, + collectionUid: collection.uid + })); + } + + if (methods && methods.length > 0) { + const haveSelectedMethod = + selectedGrpcMethod && methods.some((method) => method.path === selectedGrpcMethod.path); + if (!haveSelectedMethod) { + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + } else if (selectedGrpcMethod) { + // Update the method type for the currently selected method to ensure it matches + const currentMethod = methods.find((method) => method.path === selectedGrpcMethod.path); + if (currentMethod) { + const methodType = currentMethod.type; + setSelectedGrpcMethod({ + path: selectedGrpcMethod.path, + type: methodType + }); + } + } + toast.success(`Loaded ${methods.length} gRPC methods from reflection`); + } + } catch (error) { + console.error('Error loading gRPC methods:', error); + toast.error('Failed to load gRPC methods from reflection'); + } finally { + setIsLoadingMethods(false); + } + }; + + const handleGrpcurl = async (url) => { + if (!url) { + toast.error('Please enter a valid gRPC server URL'); + return; + } + + if (!selectedGrpcMethod?.path) { + toast.error('Please select a gRPC method'); + return; + } + + try { + const result = await dispatch(generateGrpcurlCommand(item, collection.uid)); + + if (result.success) { + setGrpcurlCommand(result.command); + setShowGrpcurlModal(true); + } else { + toast.error(result.error || 'Failed to generate grpcurl command'); + } + } catch (error) { + console.error('Error generating grpcurl command:', error); + toast.error('Failed to generate grpcurl command'); + } + }; + + // Add a new function to group methods by service + const groupMethodsByService = (methods) => { + if (!methods || !methods.length) return {}; + + const groupedMethods = {}; + + methods.forEach(method => { + // The format is "/service.ServiceName/MethodName" + const pathWithoutLeadingSlash = method.path.startsWith('/') ? method.path.slice(1) : method.path; + const parts = pathWithoutLeadingSlash.split('/'); + + // The service is the part before the last slash + const serviceName = parts[0] || 'Default'; + // The method name is the part after the last slash + const methodName = parts[1] || method.path; + + // Store the extracted method name for easier display + const enhancedMethod = { + ...method, + serviceName, + methodName + }; + + if (!groupedMethods[serviceName]) { + groupedMethods[serviceName] = []; + } + + groupedMethods[serviceName].push(enhancedMethod); + }); + + return groupedMethods; + }; + + const MethodsDropdownIcon = forwardRef((props, ref) => { + return ( +
+ {selectedGrpcMethod &&
{getIconForMethodType(selectedGrpcMethod.type)}
} + + {selectedGrpcMethod ? ( + + {selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path} + + ) : ( + Select Method + )} + + +
+ ); + }); + + const ProtoFileDropdownIcon = forwardRef((props, ref) => { + return ( +
setShowProtoDropdown(prev => !prev)}> + {isReflectionMode ? (<> + ) : ( + + )} + + {isReflectionMode ? 'Using Reflection' : (protoFilePath ? getBasename(protoFilePath) : 'Select Proto File')} + + +
+ ); + }); + + const handleGrpcMethodSelect = (method) => { + const methodType = method.type + setSelectedGrpcMethod({ + path: method.path, + type: methodType + }); + onMethodSelect({ path: method.path, type: methodType }); + }; + + const getIconForMethodType = (type) => { + switch (type) { + case 'unary': + return ; + case 'client-streaming': + return ; + case 'server-streaming': + return ; + case 'bidi-streaming': + return ; + default: + return ; + } + }; + + const handleCancelConnection = (e) => { + e.stopPropagation(); + + cancelGrpcConnection(item.uid) + .then(() => { + toast.success('gRPC connection cancelled'); + }) + .catch((err) => { + console.error('Failed to cancel gRPC connection:', err); + toast.error('Failed to cancel gRPC connection'); + }); + }; + + const handleEndConnection = (e) => { + e.stopPropagation(); + + endGrpcConnection(item.uid) + .then(() => { + toast.success('gRPC stream ended'); + }) + .catch((err) => { + console.error('Failed to end gRPC stream:', err); + toast.error('Failed to end gRPC stream'); + }); + }; + + const handleSelectCollectionProtoFile = (protoFile) => { + try { + if (!protoFile) { + toast.error('No proto file selected'); + return; + } + + // Get the absolute path from the relative path + const absolutePath = protoFile.absolutePath; + + if (!protoFile.exists) { + toast.error(`Proto file not found: ${protoFile.path}`); + return; + } + + setProtoFilePath(protoFile.path); + setIsReflectionMode(false); + + dispatch(updateRequestProtoPath({ + protoPath: protoFile.path, + itemUid: item.uid, + collectionUid: collection.uid + })); + + loadMethodsFromProtoFile(absolutePath); + } catch (error) { + console.error('Error selecting collection proto file:', error); + toast.error('Failed to select collection proto file'); + } + }; + + const handleResetProtoFile = () => { + setProtoFilePath(''); + setIsReflectionMode(true); + const isDuplicateSave = !item.request.protoPath; + if (!isDuplicateSave) { + dispatch(updateRequestProtoPath({ + protoPath: '', + itemUid: item.uid, + collectionUid: collection.uid + })); + } + setGrpcMethods([]); + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + toast.success('Proto file reset'); + }; + + const loadMethodsFromProtoFile = async (filePath, isManualRefresh = false) => { + if (!filePath) { + toast.error('No proto file selected'); + return; + }; + const absolutePath = getAbsoluteFilePath(filePath, collection.pathname); + + // Check if we have cached methods for this proto file + const cachedMethods = protofileCache[absolutePath]; + if (cachedMethods && !isLoadingMethods && !isManualRefresh) { + setGrpcMethods(cachedMethods); + + if (cachedMethods && cachedMethods.length > 0) { + // Check if currently selected method is still valid + const haveSelectedMethod = + selectedGrpcMethod && cachedMethods.some((method) => method.path === selectedGrpcMethod.path); + if (!haveSelectedMethod) { + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + } else { + // Update the method type for the currently selected method to ensure it matches + const currentMethod = cachedMethods.find((method) => method.path === selectedGrpcMethod.path); + if (currentMethod) { + const methodType = currentMethod.type; + setSelectedGrpcMethod({ + path: selectedGrpcMethod.path, + type: methodType + }); + } + } + } + return; + } + + setIsLoadingMethods(true); + try { + const { methods, error } = await loadGrpcMethodsFromProtoFile(absolutePath); + + if (error) { + console.error('Error loading gRPC methods:', error); + toast.error(`Failed to load gRPC methods: ${error.message || 'Unknown error'}`); + return; + } + + // Cache the methods for this proto file + setProtofileCache(prevCache => ({ + ...prevCache, + [absolutePath]: methods + })); + + setGrpcMethods(methods); + + if (methods && methods.length > 0) { + toast.success(`Loaded ${methods.length} gRPC methods from proto file`); + + // Check if currently selected method is still valid + const haveSelectedMethod = + selectedGrpcMethod && methods.some((method) => method.path === selectedGrpcMethod.path); + if (!haveSelectedMethod) { + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + } else { + // Update the method type for the currently selected method to ensure it matches + const currentMethod = methods.find((method) => method.path === selectedGrpcMethod.path); + if (currentMethod) { + const methodType = currentMethod.type; + setSelectedGrpcMethod({ + path: selectedGrpcMethod.path, + type: methodType + }); + } + } + } else { + toast.warning('No gRPC methods found in proto file'); + } + } catch (err) { + console.error('Error loading gRPC methods:', err); + toast.error('Failed to load gRPC methods from proto file'); + } finally { + setIsLoadingMethods(false); + } + }; + + const handleSelectProtoFile = (e) => { + e.stopPropagation(); + const filters = [{ name: 'Proto Files', extensions: ['proto'] }]; + + dispatch(browseFiles(filters, [''])) + .then((filePaths) => { + if (filePaths && filePaths.length > 0) { + const filePath = filePaths[0]; + const relativePath = getRelativePath(filePath, collection.pathname); + setProtoFilePath(relativePath); + setIsReflectionMode(false); + + dispatch(updateRequestProtoPath({ + protoPath: relativePath, + itemUid: item.uid, + collectionUid: collection.uid + })); + + // Load methods from the newly selected proto file + const absolutePath = getAbsoluteFilePath(relativePath, collection.pathname); + loadMethodsFromProtoFile(absolutePath); + } + }) + .catch((err) => { + console.error('Error selecting proto file:', err); + toast.error('Failed to select proto file'); + }); + }; + + const handleOpenCollectionGrpc = () => { + dispatch(openCollectionSettings(collection.uid, 'grpc')); + }; + + const debouncedOnUrlChange = debounce(onUrlChange, 1000); + + useEffect(() => { + fileExistsCache.current.clear(); + }, [collection.pathname]); + + useEffect(() => { + if(haveFetchedMethodsRef.current) { + return; + } + haveFetchedMethodsRef.current = true; + + if(protoFilePath) { + setIsReflectionMode(false); + loadMethodsFromProtoFile(protoFilePath); + return; + } + if (!url) return; + setIsReflectionMode(true); + handleReflection(url); + + }, []); + + return ( + +
+
+ gRPC +
+
+
+ onSave(finalValue)} + theme={storedTheme} + onChange={(newValue) => debouncedOnUrlChange(newValue)} + onRun={handleRun} + collection={collection} + highlightPathParams={true} + item={item} + /> + + {grpcMethods && grpcMethods.length > 0 && ( +
+ } placement="bottom-end" style={{ maxWidth: "unset" }}> +
+ {Object.entries(groupMethodsByService(grpcMethods)).map(([serviceName, methods], serviceIndex) => ( +
+
+ {serviceName || 'Default Service'} +
+
+ {methods.map((method, methodIndex) => ( +
handleGrpcMethodSelect(method)} + > +
+
{getIconForMethodType(method.type)}
+
+
{method.methodName}
+
{method.type}
+
+
+
+ ))} +
+
+ ))} +
+
+
+ )} +
+
+ } + placement="bottom-end" + visible={showProtoDropdown} + onClickOutside={() => setShowProtoDropdown(false)} + > +
+
+

{isReflectionMode ? "Using Reflection" : "Select Proto File"}

+
+ + {/* Mode Toggle */} +
+
+ Mode +
+ + Proto File + + { + e.stopPropagation(); + e.preventDefault(); + setIsReflectionMode(!isReflectionMode); + if (!isReflectionMode) { + // Switching to reflection mode + setProtoFilePath(''); + dispatch(updateRequestProtoPath({ + protoPath: '', + itemUid: item.uid, + collectionUid: collection.uid + })); + if (url) { + handleReflection(url); + } + } else { + // Switching to proto file mode + setGrpcMethods([]); + setSelectedGrpcMethod(null); + onMethodSelect({ path: '', type: '' }); + } + }} + size="2xs" + /> + + Reflection + +
+
+
+ + {!isReflectionMode && ( + <> + {collectionProtoFiles && collectionProtoFiles.length > 0 && ( +
+
+
From Collection Settings
+ +
+ + {invalidProtoFiles.length > 0 && ( +
+

+ + Some proto files could not be found. +

+
+ )} + +
+ {collectionProtoFilesExistence.map((protoFile, index) => { + const isSelected = protoFilePath === protoFile.absolutePath; + const isInvalid = !protoFile.exists; + + return ( +
{ + if (!isInvalid) { + setShowProtoDropdown(false); + handleSelectCollectionProtoFile(protoFile); + } + }} + > +
+
+ +
+
+ {getBasename(protoFile.absolutePath)} + {isInvalid && ( + + + + )} +
+
{protoFile.path}
+
+
+
+
+ ); + })} +
+
+ )} + + {collectionProtoFiles && collectionProtoFiles.length > 0 && ( +
+ )} + + {protoFilePath && !collectionProtoFilesExistence.some(pf => + pf.absolutePath === protoFilePath + ) && ( +
+
Current Proto File
+ {!currentProtoFileExists && ( +
+

+ + Selected proto file not found. Please select a valid proto file from collection settings or browse for a new one. +

+
+ )} +
+
+
+ +
+
+ {getBasename(protoFilePath)} + {!currentProtoFileExists && ( + + + + )} +
+
{protoFilePath}
+
+
+
+ +
+
+
+ +
+ )} + +
+ +
+ + )} + + {isReflectionMode && ( +
+
+ Using server reflection to discover gRPC methods. +
+
+ )} +
+
+
+ +
{ + e.stopPropagation(); + if (isReflectionMode) { + handleReflection(url, true); + } else if (protoFilePath) { + loadMethodsFromProtoFile(protoFilePath, true); + } else { + toast.error('No proto file selected'); + } + }} + > + + + {isReflectionMode ? 'Refresh server reflection' : 'Refresh proto file methods'} + +
+ +
{ + e.stopPropagation(); + handleGrpcurl(url); + }} + > + + Generate grpcurl command +
+ +
{ + e.stopPropagation(); + if (!item.draft) return; + onSave(); + }} + > + + + Save ({saveShortcut}) + +
+ + {isConnectionActive && isStreamingMethod && ( +
+
+ + Cancel +
+ + {isClientStreamingMethod &&
+ +
} +
+ )} + + {(!isConnectionActive || !isStreamingMethod) && ( +
{ + e.stopPropagation(); + handleRun(e); + }} + > + +
+ )} +
+
+ {isConnectionActive && isStreamingMethod && ( +
+ )} + + {showGrpcurlModal && ( + setShowGrpcurlModal(false)} + command={grpcurlCommand} + /> + )} +
+ ); +}; + +export default GrpcQueryUrl; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/GrpcAuthMode/index.js b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/GrpcAuthMode/index.js new file mode 100644 index 000000000..3b95b708f --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/GrpcAuthMode/index.js @@ -0,0 +1,85 @@ +import React, { useRef, forwardRef } from 'react'; +import get from 'lodash/get'; +import { IconCaretDown } from '@tabler/icons'; +import Dropdown from 'components/Dropdown'; +import { useDispatch } from 'react-redux'; +import { updateRequestAuthMode } from 'providers/ReduxStore/slices/collections'; +import { humanizeRequestAuthMode } from 'utils/collections'; +import StyledWrapper from '../../../Auth/AuthMode/StyledWrapper'; + +const GrpcAuthMode = ({ item, collection }) => { + const dispatch = useDispatch(); + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); + + const authModes = [ + { + name: 'Basic Auth', + mode: 'basic' + }, + { + name: 'Bearer Token', + mode: 'bearer' + }, + { + name: 'API Key', + mode: 'apikey' + }, + { + name: 'OAuth2', + mode: 'oauth2' + }, + { + name: 'Inherit', + mode: 'inherit' + }, + { + name: 'No Auth', + mode: 'none' + } + ]; + + const Icon = forwardRef((props, ref) => { + return ( +
+ {humanizeRequestAuthMode(authMode)} +
+ ); + }); + + const onModeChange = (value) => { + dispatch( + updateRequestAuthMode({ + itemUid: item.uid, + collectionUid: collection.uid, + mode: value + }) + ); + }; + + const onClickHandler = (mode) => { + dropdownTippyRef?.current?.hide(); + onModeChange(mode); + }; + + return ( + +
+ } placement="bottom-end"> + {authModes.map((authMode) => ( +
onClickHandler(authMode.mode)} + > + {authMode.name} +
+ ))} +
+
+
+ ); +}; + +export default GrpcAuthMode; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/StyledWrapper.js new file mode 100644 index 000000000..f76b0d9a4 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/StyledWrapper.js @@ -0,0 +1,9 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + .inherit-mode-text { + color: ${(props) => props.theme.colors.text.yellow}; + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/index.js b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/index.js new file mode 100644 index 000000000..b30be89e5 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/GrpcAuth/index.js @@ -0,0 +1,125 @@ +import React, { useEffect } from 'react'; +import get from 'lodash/get'; +import { useDispatch } from 'react-redux'; +import GrpcAuthMode from './GrpcAuthMode'; +import BearerAuth from '../../Auth/BearerAuth'; +import BasicAuth from '../../Auth/BasicAuth'; +import ApiKeyAuth from '../../Auth/ApiKeyAuth'; +import OAuth2 from '../../Auth/OAuth2/index'; +import StyledWrapper from './StyledWrapper'; +import { humanizeRequestAuthMode } from 'utils/collections'; +import { getTreePathFromCollectionToItem } from 'utils/collections/index'; +import { updateRequestAuthMode, updateAuth } from 'providers/ReduxStore/slices/collections'; +import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; + +// List of auth modes supported by gRPC +const supportedGrpcAuthModes = ['basic', 'bearer', 'apikey', 'oauth2', 'none', 'inherit']; + +const GrpcAuth = ({ item, collection }) => { + const dispatch = useDispatch(); + const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); + const requestTreePath = getTreePathFromCollectionToItem(collection, item); + + const request = item.draft + ? get(item, 'draft.request', {}) + : get(item, 'request', {}); + + const save = () => { + return saveRequest(item.uid, collection.uid); + }; + + // Reset to 'none' if current auth mode is not supported by gRPC + useEffect(() => { + if (authMode && !supportedGrpcAuthModes.includes(authMode)) { + dispatch( + updateRequestAuthMode({ + itemUid: item.uid, + collectionUid: collection.uid, + mode: 'none' + }) + ); + } + }, [authMode, collection.uid, dispatch, item.uid]); + + const getEffectiveAuthSource = () => { + if (authMode !== 'inherit') return null; + + const collectionAuth = get(collection, 'root.request.auth'); + let effectiveSource = { + type: 'collection', + name: 'Collection', + auth: collectionAuth + }; + + // Check folders in reverse to find the closest auth configuration + for (let i of [...requestTreePath].reverse()) { + if (i.type === 'folder') { + const folderAuth = get(i, 'root.request.auth'); + if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') { + effectiveSource = { + type: 'folder', + name: i.name, + auth: folderAuth + }; + break; + } + } + } + + return effectiveSource; + }; + + const getAuthView = () => { + switch (authMode) { + case 'basic': { + return ; + } + case 'bearer': { + return ; + } + case 'apikey': { + return ; + } + case 'oauth2': { + return ; + } + case 'inherit': { + const source = getEffectiveAuthSource(); + + // Only show inherited auth if it's one of the supported types + if (source && supportedGrpcAuthModes.includes(source.auth?.mode)) { + return ( + <> +
+
Auth inherited from {source.name}:
+
{humanizeRequestAuthMode(source.auth?.mode)}
+
+ + ); + } else { + return ( + <> +
+
Inherited auth not supported by gRPC. Using no auth instead.
+
+ + ); + } + } + default: { + return null; + } + } + }; + + return ( + +
+ +
+ {getAuthView()} +
+ ); +}; + +export default GrpcAuth; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/StyledWrapper.js new file mode 100644 index 000000000..e6a766672 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/StyledWrapper.js @@ -0,0 +1,34 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + div.tabs { + div.tab { + padding: 6px 0px; + border: none; + border-bottom: solid 2px transparent; + margin-right: 1.25rem; + color: var(--color-tab-inactive); + cursor: pointer; + + &:focus, + &:active, + &:focus-within, + &:focus-visible, + &:target { + outline: none !important; + box-shadow: none !important; + } + + &.active { + color: ${(props) => props.theme.tabs.active.color} !important; + border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important; + } + + .content-indicator { + color: ${(props) => props.theme.text} + } + } + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/index.js new file mode 100644 index 000000000..ef961b79d --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcRequestPane/index.js @@ -0,0 +1,124 @@ +import React from 'react'; +import classnames from 'classnames'; +import { useSelector, useDispatch } from 'react-redux'; +import { updateRequestPaneTab } from 'providers/ReduxStore/slices/tabs'; +import RequestHeaders from 'components/RequestPane/RequestHeaders'; +import GrpcBody from 'components/RequestPane/GrpcBody'; +import GrpcAuth from './GrpcAuth/index'; +import StatusDot from 'components/StatusDot/index'; +import HeightBoundContainer from 'ui/HeightBoundContainer'; +import StyledWrapper from './StyledWrapper'; +import { find, get } from 'lodash'; +import Documentation from 'components/Documentation/index'; +import { useEffect } from 'react'; +import { getPropertyFromDraftOrRequest } from 'utils/collections/index'; + +const GrpcRequestPane = ({ item, collection, handleRun }) => { + const dispatch = useDispatch(); + const tabs = useSelector((state) => state.tabs.tabs); + const activeTabUid = useSelector((state) => state.tabs.activeTabUid); + + const selectTab = (tab) => { + dispatch( + updateRequestPaneTab({ + uid: item.uid, + requestPaneTab: tab + }) + ); + }; + + const getTabPanel = (tab) => { + switch (tab) { + case 'body': { + return ; + } + case 'headers': { + return ; + } + case 'auth': { + return ; + } + case 'docs': { + return ; + } + default: { + return
404 | Not found
; + } + } + }; + + if (!activeTabUid) { + return
Something went wrong
; + } + + const focusedTab = find(tabs, (t) => t.uid === activeTabUid); + if (!focusedTab || !focusedTab.uid || !focusedTab.requestPaneTab) { + return
An error occurred!
; + } + + const getTabClassname = (tabName) => { + return classnames(`tab select-none ${tabName}`, { + active: tabName === focusedTab.requestPaneTab + }); + }; + + const isMultipleContentTab = ['script', 'vars', 'auth', 'docs'].includes(focusedTab.requestPaneTab); + const body = getPropertyFromDraftOrRequest(item, 'request.body'); + const headers = getPropertyFromDraftOrRequest(item, 'request.headers'); + const docs = getPropertyFromDraftOrRequest(item, 'request.docs'); + const auth = getPropertyFromDraftOrRequest(item, 'request.auth'); + + const activeHeadersLength = headers.filter((header) => header.enabled).length; + const grpcMessagesCount = body?.grpc?.length || 0; + + // Determine if this is a client streaming request + const request = item.draft ? item.draft.request : item.request; + const isClientStreaming = request.methodType === 'client-streaming' || request.methodType === 'bidi-streaming'; + + useEffect(() => { + // Only set the tab to 'body' if no tab is currently set + if (!focusedTab?.requestPaneTab) { + selectTab('body'); + } + }, []); + + return ( + +
+
selectTab('body')}> + Message + {grpcMessagesCount > 0 && ( + isClientStreaming ? ( + {grpcMessagesCount} + ) : ( + + ) + )} +
+
selectTab('headers')}> + Metadata + {activeHeadersLength > 0 && {activeHeadersLength}} +
+
selectTab('auth')}> + Auth + {auth.mode !== 'none' && } +
+
selectTab('docs')}> + Docs + {docs && docs.length > 0 && } +
+
+
+ + {getTabPanel(focusedTab.requestPaneTab)} + +
+
+ ); +}; + +export default GrpcRequestPane; diff --git a/packages/bruno-app/src/components/RequestPane/QueryParams/index.js b/packages/bruno-app/src/components/RequestPane/QueryParams/index.js index 0b1b9df9c..b5c2c69a7 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryParams/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryParams/index.js @@ -147,7 +147,7 @@ const QueryParams = ({ item, collection }) => { diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index 2035ba00c..3fe47b041 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -20,6 +20,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { const isMac = isMacOS(); const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S'; const editorRef = useRef(null); + const isGrpc = item.type === 'grpc-request'; const [methodSelectorWidth, setMethodSelectorWidth] = useState(90); const [generateCodeItemModalOpen, setGenerateCodeItemModalOpen] = useState(false); @@ -80,7 +81,14 @@ const QueryUrl = ({ item, collection, handleRun }) => { return (
- + {isGrpc ? ( +
+ gRPC +
+ + ) : ( + + )}
{
} placement="bottom-end"> -
Form
-
{ - dropdownTippyRef.current.hide(); - onModeChange('multipartForm'); - }} - > - Multipart Form -
-
{ - dropdownTippyRef.current.hide(); - onModeChange('formUrlEncoded'); - }} - > - Form URL Encoded -
-
Raw
-
{ - dropdownTippyRef.current.hide(); - onModeChange('json'); - }} - > - JSON -
-
{ - dropdownTippyRef.current.hide(); - onModeChange('xml'); - }} - > - XML -
-
{ - dropdownTippyRef.current.hide(); - onModeChange('text'); - }} - > - TEXT -
-
{ - dropdownTippyRef.current.hide(); - onModeChange('sparql'); - }} - > - SPARQL -
-
Other
-
{ - dropdownTippyRef.current.hide(); - onModeChange('file'); - }} - > - File / Binary -
-
{ - dropdownTippyRef.current.hide(); - onModeChange('none'); - }} - > - No Body -
+
Form
+
{ + dropdownTippyRef.current.hide(); + onModeChange('multipartForm'); + }} + > + Multipart Form +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('formUrlEncoded'); + }} + > + Form URL Encoded +
+
Raw
+
{ + dropdownTippyRef.current.hide(); + onModeChange('json'); + }} + > + JSON +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('xml'); + }} + > + XML +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('text'); + }} + > + TEXT +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('sparql'); + }} + > + SPARQL +
+
Other
+
{ + dropdownTippyRef.current.hide(); + onModeChange('file'); + }} + > + File / Binary +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('none'); + }} + > + No Body +
{(bodyMode === 'json' || bodyMode === 'xml') && ( diff --git a/packages/bruno-app/src/components/RequestPane/RequestBody/index.js b/packages/bruno-app/src/components/RequestPane/RequestBody/index.js index d562684e5..fdc674ae6 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestBody/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestBody/index.js @@ -79,4 +79,4 @@ const RequestBody = ({ item, collection }) => { return No Body; }; -export default RequestBody; +export default RequestBody; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js index ddcc62af2..930a056f9 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js @@ -16,7 +16,7 @@ import BulkEditor from '../../BulkEditor'; const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header); -const RequestHeaders = ({ item, collection }) => { +const RequestHeaders = ({ item, collection, addHeaderText }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const headers = item.draft ? get(item, 'draft.request.headers') : get(item, 'request.headers'); @@ -181,7 +181,7 @@ const RequestHeaders = ({ item, collection }) => {
-
- -

(Runs in background)

-
-

(May cause the app to freeze temporarily while it runs)

+

(Uses a regex based parsing approach)

)} diff --git a/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js b/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js index 3bee80c40..5349f2410 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js @@ -18,6 +18,7 @@ const StyledWrapper = styled.div` padding: 0; cursor: col-resize; background: transparent; + position: relative; div.dragbar-handle { display: flex; @@ -45,6 +46,7 @@ const StyledWrapper = styled.div` height: 10px; cursor: row-resize; padding: 0 1rem; + position: relative; div.dragbar-handle { width: 100%; diff --git a/packages/bruno-app/src/components/RequestTabPanel/index.js b/packages/bruno-app/src/components/RequestTabPanel/index.js index 6863b30b2..72cd76701 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/index.js +++ b/packages/bruno-app/src/components/RequestTabPanel/index.js @@ -4,13 +4,16 @@ import toast from 'react-hot-toast'; import { useSelector, useDispatch } from 'react-redux'; import GraphQLRequestPane from 'components/RequestPane/GraphQLRequestPane'; import HttpRequestPane from 'components/RequestPane/HttpRequestPane'; +import GrpcRequestPane from 'components/RequestPane/GrpcRequestPane/index'; import ResponsePane from 'components/ResponsePane'; +import GrpcResponsePane from 'components/ResponsePane/GrpcResponsePane'; import Welcome from 'components/Welcome'; import { findItemInCollection } from 'utils/collections'; import { updateRequestPaneTabWidth } from 'providers/ReduxStore/slices/tabs'; import { sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import RequestNotFound from './RequestNotFound'; -import QueryUrl from 'components/RequestPane/QueryUrl'; +import QueryUrl from 'components/RequestPane/QueryUrl/index'; +import GrpcQueryUrl from 'components/RequestPane/GrpcQueryUrl/index'; import NetworkError from 'components/ResponsePane/NetworkError'; import RunnerResults from 'components/RunnerResults'; import VariablesEditor from 'components/VariablesEditor'; @@ -180,6 +183,9 @@ const RequestTabPanel = () => { return
Collection not found!
; } + const item = findItemInCollection(collection, activeTabUid); + const isGrpcRequest = item?.type === 'grpc-request'; + if (focusedTab.type === 'collection-runner') { return ; } @@ -201,7 +207,7 @@ const RequestTabPanel = () => { if (!folder) { return ; } - + return ; } @@ -209,20 +215,32 @@ const RequestTabPanel = () => { return ; } - const item = findItemInCollection(collection, activeTabUid); if (!item || !item.uid) { return ; } if (item?.partial) { - return + return ; } if (item?.loading) { - return + return ; } const handleRun = async () => { + const isGrpcRequest = item?.type === 'grpc-request'; + const request = item.draft ? item.draft.request : item.request; + + if (isGrpcRequest && !request.url) { + toast.error('Please enter a valid gRPC server URL'); + return; + } + + if (isGrpcRequest && !request.method) { + toast.error('Please select a gRPC method'); + return; + } + dispatch(sendRequest(item, collection.uid)).catch((err) => toast.custom((t) => toast.dismiss(t.id)} />, { duration: 5000 @@ -233,9 +251,13 @@ const RequestTabPanel = () => { return (
- + {isGrpcRequest ? ( + + ) : ( + + )}
-
+
{ {item.type === 'http-request' ? ( ) : null} + + {isGrpcRequest ? ( + + ) : null}
@@ -268,7 +294,20 @@ const RequestTabPanel = () => {
- + {item.type === 'grpc-request' ? ( + + ) : ( + + )}
diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js index 816b00e25..fe574fdaa 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js @@ -22,6 +22,7 @@ import { flattenItems } from 'utils/collections/index'; const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUid }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); + const theme = storedTheme === 'dark' ? darkTheme : lightTheme; const [showConfirmClose, setShowConfirmClose] = useState(false); const dropdownTippyRef = useRef(); @@ -65,10 +66,10 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi }; const getMethodColor = (method = '') => { - const theme = storedTheme === 'dark' ? darkTheme : lightTheme; return theme.request.methods[method.toLocaleLowerCase()]; }; + const folder = folderUid ? findItemInCollection(collection, folderUid) : null; if (['collection-settings', 'collection-overview', 'folder-settings', 'variables', 'collection-runner', 'security-settings'].includes(tab.type)) { return ( @@ -107,6 +108,7 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi ); } + const isGrpc = item.type === 'grpc-request'; const method = item.draft ? get(item, 'draft.request.method') : get(item, 'request.method'); return ( @@ -159,8 +161,8 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi } }} > - - {method} + + {isGrpc ? 'gRPC' : method} {item.name} diff --git a/packages/bruno-app/src/components/RequestTabs/index.js b/packages/bruno-app/src/components/RequestTabs/index.js index d50d528b3..fcba790a6 100644 --- a/packages/bruno-app/src/components/RequestTabs/index.js +++ b/packages/bruno-app/src/components/RequestTabs/index.js @@ -18,6 +18,7 @@ const RequestTabs = () => { const activeTabUid = useSelector((state) => state.tabs.activeTabUid); const collections = useSelector((state) => state.collections.collections); const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth); + const sidebarCollapsed = useSelector((state) => state.app.sidebarCollapsed); const screenWidth = useSelector((state) => state.app.screenWidth); const getTabClassname = (tab, index) => { @@ -49,7 +50,8 @@ const RequestTabs = () => { const activeCollection = find(collections, (c) => c.uid === activeTab.collectionUid); const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab.collectionUid); - const maxTablistWidth = screenWidth - leftSidebarWidth - 150; + const effectiveSidebarWidth = sidebarCollapsed ? 0 : leftSidebarWidth; + const maxTablistWidth = screenWidth - effectiveSidebarWidth - 150; const tabsWidth = collectionRequestTabs.length * 150 + 34; // 34: (+)icon const showChevrons = maxTablistWidth < tabsWidth; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/StyledWrapper.js new file mode 100644 index 000000000..f302b86dd --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/StyledWrapper.js @@ -0,0 +1,44 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + border-left: 4px solid ${(props) => props.theme.colors.text.danger}; + border-top: 1px solid transparent; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + border-radius: 0.375rem; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + max-height: 200px; + min-height: 70px; + overflow-y: auto; + background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')}; + + .close-button { + opacity: 0.7; + transition: opacity 0.2s; + + &:hover { + opacity: 1; + } + + svg { + color: ${(props) => props.theme.text}; + } + } + + .error-title { + font-weight: 600; + margin-bottom: 0.375rem; + color: ${(props) => props.theme.colors.text.danger}; + } + + .error-message { + font-family: monospace; + font-size: 0.6875rem; + line-height: 1.25rem; + white-space: pre-wrap; + word-break: break-all; + color: ${(props) => props.theme.text}; + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/index.js new file mode 100644 index 000000000..d2376401d --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcError/index.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { IconX } from '@tabler/icons'; +import StyledWrapper from './StyledWrapper'; + +const GrpcError = ({ error, onClose }) => { + if (!error) return null; + + return ( + +
+
+
gRPC Server Error
+
{typeof error === 'string' ? error : JSON.stringify(error, null, 2)}
+
+
+ +
+
+
+ ); +}; + +export default GrpcError; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/StyledWrapper.js new file mode 100644 index 000000000..81b4c33b1 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/StyledWrapper.js @@ -0,0 +1,96 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + height: 100%; + overflow: hidden; + background: ${(props) => props.theme.bg}; + border-radius: 4px; + + .CodeMirror { + height: 100%; + font-family: ${(props) => (props.font === 'default' ? 'monospace' : props.font)}; + font-size: ${(props) => (props.fontSize ? props.fontSize : '13px')}; + } + + .accordion-header { + background-color: ${(props) => props.theme.requestTabPanel.card.bg}; + + &:hover { + background-color: ${(props) => props.theme.plainGrid.hoverBg}; + } + + &.open { + background-color: ${(props) => props.theme.plainGrid.hoverBg}; + } + } + + .error-header { + background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(185, 28, 28, 0.1)' : '#fee2e2')}; + } + + .error-text { + color: ${(props) => props.theme.colors.text.danger}; + } + + div.tabs { + div.tab { + padding: 6px 0px; + border: none; + border-bottom: solid 2px transparent; + margin-right: 1.25rem; + color: var(--color-tab-inactive); + cursor: pointer; + + &:focus, + &:active, + &:focus-within, + &:focus-visible, + &:target { + outline: none !important; + box-shadow: none !important; + } + + &.active { + color: ${(props) => props.theme.tabs.active.color} !important; + border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important; + } + } + } + + .stream-status { + display: inline-flex; + align-items: center; + + &.complete { + color: ${(props) => props.theme.colors.text.green}; + } + + &.cancelled { + color: ${(props) => props.theme.colors.text.danger}; + } + + &.streaming { + color: ${(props) => props.theme.colors.text.blue}; + } + } + + .message-counter { + display: inline-flex; + align-items: center; + margin-left: 10px; + } + + .response-list { + max-height: 500px; + overflow-y: auto; + } + + .response-message { + margin-bottom: 8px; + padding: 8px; + border-radius: 4px; + background-color: var(--color-panel-background); + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/index.js new file mode 100644 index 000000000..c6ceeff61 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcQueryResult/index.js @@ -0,0 +1,126 @@ +import React, { useState, useEffect } from 'react'; +import Accordion from 'components/Accordion'; +import CodeEditor from 'components/CodeEditor'; +import { get } from 'lodash'; +import { useSelector } from 'react-redux'; +import { useTheme } from 'providers/Theme/index'; +import StyledWrapper from './StyledWrapper'; +import { formatISO9075 } from 'date-fns'; +import GrpcError from '../GrpcError'; + +const GrpcQueryResult = ({ item, collection }) => { + const { displayedTheme } = useTheme(); + const preferences = useSelector((state) => state.app.preferences); + const [showErrorMessage, setShowErrorMessage] = useState(true); + + const response = item.response || {}; + const responsesList = response?.responses || []; + // Reverse the responses list to show the most recent at the top + const reversedResponsesList = [...responsesList].reverse(); + const hasError = response.isError; + const hasResponses = responsesList.length > 0; + const errorMessage = response.error; + + // Reset error visibility when a new response is received + useEffect(() => { + if (hasError) { + setShowErrorMessage(true); + } + }, [response, hasError]); + + // Format a timestamp to a human-readable format + const formatTimestamp = (timestamp) => { + if (!timestamp) return 'Unknown time'; + + try { + const date = new Date(timestamp); + return formatISO9075(date); + } catch (e) { + return 'Invalid time'; + } + }; + + // Format JSON for display + const formatJSON = (data) => { + try { + if (typeof data === 'string') { + return JSON.stringify(JSON.parse(data), null, 2); + } + return JSON.stringify(data, null, 2); + } catch (e) { + return typeof data === 'string' ? data : JSON.stringify(data); + } + }; + + if (!hasResponses && !hasError) { + return ( + +
No messages received
+
+ ); + } + + return ( + + {hasError && showErrorMessage && setShowErrorMessage(false)} />} + {hasResponses && ( +
+ {responsesList.length === 1 ? ( + // Single message - render directly without accordion +
+ +
+ ) : ( + // Multiple messages - use accordion + + {reversedResponsesList.map((response, index) => { + // Calculate the original response number (for display purposes) + const originalIndex = responsesList.length - index - 1; + + return ( + + +
+
+ Response {originalIndex + 1} {index === 0 ? '(Latest)' : ''} +
+
+
+ +
+ +
+
+
+ ); + })} +
+ )} +
+ )} + {hasError && !hasResponses && !showErrorMessage && ( +
+ No messages received. A server error occurred but has been dismissed. +
+ )} +
+ ); +}; + +export default GrpcQueryResult; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/StyledWrapper.js new file mode 100644 index 000000000..d42e77f7f --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/StyledWrapper.js @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + table { + width: 100%; + border-collapse: collapse; + + thead { + color: #777777; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + } + + td { + padding: 6px 10px; + + &.value { + word-break: break-all; + } + } + + tbody { + tr:nth-child(odd) { + background-color: ${(props) => props.theme.table.striped}; + } + } + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/index.js new file mode 100644 index 000000000..a7390558d --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcResponseHeaders/index.js @@ -0,0 +1,38 @@ +import React from 'react'; +import StyledWrapper from './StyledWrapper'; + +const GrpcResponseHeaders = ({ metadata }) => { + // Ensure headers is an array + const metadataArray = Array.isArray(metadata) ? metadata : []; + + return ( + + + + + + + + + + {metadataArray && metadataArray.length ? ( + metadataArray.map((metadata, index) => ( + + + + + )) + ) : ( + + + + )} + +
NameValue
{metadata.name}{metadata.value}
+ No metadata received +
+
+ ); +}; + +export default GrpcResponseHeaders; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/StyledWrapper.js new file mode 100644 index 000000000..bed367559 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/StyledWrapper.js @@ -0,0 +1,22 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + font-size: 0.75rem; + font-weight: 600; + display: flex; + align-items: center; + + &.text-ok { + color: ${(props) => props.theme.requestTabPanel.responseOk}; + } + + &.text-pending { + color: ${(props) => props.theme.requestTabPanel.responsePending}; + } + + &.text-error { + color: ${(props) => props.theme.requestTabPanel.responseError}; + } +`; + +export default Wrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/get-grpc-status-code-phrase.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/get-grpc-status-code-phrase.js new file mode 100644 index 000000000..a8c96e645 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/get-grpc-status-code-phrase.js @@ -0,0 +1,22 @@ +// https://grpc.github.io/grpc/core/md_doc_statuscodes.html +const grpcStatusCodePhraseMap = { + 0: 'OK', + 1: 'Cancelled', + 2: 'Unknown', + 3: 'Invalid Argument', + 4: 'Deadline Exceeded', + 5: 'Not Found', + 6: 'Already Exists', + 7: 'Permission Denied', + 8: 'Resource Exhausted', + 9: 'Failed Precondition', + 10: 'Aborted', + 11: 'Out of Range', + 12: 'Unimplemented', + 13: 'Internal', + 14: 'Unavailable', + 15: 'Data Loss', + 16: 'Unauthenticated' +}; + +export default grpcStatusCodePhraseMap; \ No newline at end of file diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/index.js new file mode 100644 index 000000000..af4f10db6 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/GrpcStatusCode/index.js @@ -0,0 +1,27 @@ +import React from 'react'; +import classnames from 'classnames'; +import grpcStatusCodePhraseMap from './get-grpc-status-code-phrase'; +import StyledWrapper from './StyledWrapper'; + +const GrpcStatusCode = ({ status, text }) => { + // gRPC status codes: 0 is success, anything else is an error + const getTabClassname = (status) => { + const isPending = text === 'PENDING' || text === 'STREAMING'; + return classnames('ml-2', { + 'text-ok': parseInt(status) === 0, + 'text-pending': isPending, + 'text-error': parseInt(status) > 0 && !isPending + }); + }; + + const statusText = text || grpcStatusCodePhraseMap[status] + + return ( + + {Number.isInteger(status) ?
{status}
: null} + {statusText &&
{statusText}
} +
+ ); +}; + +export default GrpcStatusCode; \ No newline at end of file diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/StyledWrapper.js new file mode 100644 index 000000000..d42e77f7f --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/StyledWrapper.js @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + table { + width: 100%; + border-collapse: collapse; + + thead { + color: #777777; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + } + + td { + padding: 6px 10px; + + &.value { + word-break: break-all; + } + } + + tbody { + tr:nth-child(odd) { + background-color: ${(props) => props.theme.table.striped}; + } + } + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/index.js new file mode 100644 index 000000000..7df1f5b45 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/ResponseTrailers/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import StyledWrapper from './StyledWrapper'; + +const ResponseTrailers = ({ trailers }) => { + const trailersArray = Array.isArray(trailers) ? trailers : []; + + return ( + + + + + + + + + + {trailersArray && trailersArray.length ? ( + trailersArray.map((trailer, index) => ( + + + + + )) + ) : ( + + + + )} + +
NameValue
{trailer.name}{trailer.value}
+ No trailers received +
+
+ ); +}; + +export default ResponseTrailers; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/StyledWrapper.js new file mode 100644 index 000000000..e4e358af4 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/StyledWrapper.js @@ -0,0 +1,58 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + height: 100%; + overflow: hidden; + background: ${(props) => props.theme.bg}; + border-radius: 4px; + + div.tabs { + div.tab { + padding: 6px 0px; + border: none; + border-bottom: solid 2px transparent; + margin-right: 1.25rem; + color: var(--color-tab-inactive); + cursor: pointer; + + &:focus, + &:active, + &:focus-within, + &:focus-visible, + &:target { + outline: none !important; + box-shadow: none !important; + } + + &.active { + color: ${(props) => props.theme.tabs.active.color} !important; + border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important; + } + } + } + + .stream-status { + display: inline-flex; + align-items: center; + + &.complete { + color: ${(props) => props.theme.colors.text.green}; + } + + &.cancelled { + color: ${(props) => props.theme.colors.text.danger}; + } + + &.streaming { + color: ${(props) => props.theme.colors.text.blue}; + } + } + + .message-counter { + display: inline-flex; + align-items: center; + margin-left: 10px; + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js new file mode 100644 index 000000000..81ab5bc1e --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js @@ -0,0 +1,160 @@ +import React, { useState, useEffect } from 'react'; +import find from 'lodash/find'; +import classnames from 'classnames'; +import { useDispatch, useSelector } from 'react-redux'; +import { updateResponsePaneTab } from 'providers/ReduxStore/slices/tabs'; +import Overlay from '../Overlay'; +import Placeholder from '../Placeholder'; +import GrpcResponseHeaders from './GrpcResponseHeaders'; +import GrpcStatusCode from './GrpcStatusCode'; +import ResponseTime from '../ResponseTime/index'; +import Timeline from '../Timeline'; +import ClearTimeline from '../ClearTimeline'; +import ResponseSave from '../ResponseSave'; +import ResponseClear from '../ResponseClear'; +import StyledWrapper from './StyledWrapper'; +import ResponseTrailers from './ResponseTrailers'; +import GrpcQueryResult from './GrpcQueryResult'; +import ResponseLayoutToggle from '../ResponseLayoutToggle'; +import Tab from 'components/Tab'; + +const GrpcResponsePane = ({ item, collection }) => { + const dispatch = useDispatch(); + const tabs = useSelector((state) => state.tabs.tabs); + const activeTabUid = useSelector((state) => state.tabs.activeTabUid); + const isLoading = ['queued', 'sending'].includes(item.requestState); + + const requestTimeline = [...(collection?.timeline || [])].filter((obj) => { + if (obj.itemUid === item.uid) return true; + }); + + const selectTab = (tab) => { + dispatch( + updateResponsePaneTab({ + uid: item.uid, + responsePaneTab: tab + }) + ); + }; + + const response = item.response || {}; + + const getTabPanel = (tab) => { + switch (tab) { + case 'response': { + return ; + } + case 'headers': { + return ; + } + case 'trailers': { + return ; + } + case 'timeline': { + return ; + } + default: { + return
404 | Not found
; + } + } + }; + + if (isLoading && !item.response) { + return ( + + + + ); + } + + if (!item.response && !requestTimeline?.length) { + return ( + + + + ); + } + + if (!activeTabUid) { + return
Something went wrong
; + } + + const focusedTab = find(tabs, (t) => t.uid === activeTabUid); + if (!focusedTab || !focusedTab.uid || !focusedTab.responsePaneTab) { + return
An error occurred!
; + } + + const tabConfig = [ + { + name: 'response', + label: 'Response', + count: Array.isArray(response.responses) ? response.responses.length : 0 + }, + { + name: 'headers', + label: 'Metadata', + count: Array.isArray(response.metadata) ? response.metadata.length : 0 + }, + { + name: 'trailers', + label: 'Trailers', + count: Array.isArray(response.trailers) ? response.trailers.length : 0 + }, + { + name: 'timeline', + label: 'Timeline' + } + ]; + + return ( + +
+ {tabConfig.map((tab) => ( + + ))} + {!isLoading ? ( +
+ {focusedTab?.responsePaneTab === 'timeline' ? ( + <> + + + + ) : item?.response ? ( + <> + + + + + + ) : null} +
+ ) : null} +
+
+ {isLoading ? : null} + {!item?.response ? ( + focusedTab?.responsePaneTab === 'timeline' && requestTimeline?.length ? ( + + ) : null + ) : ( + <>{getTabPanel(focusedTab.responsePaneTab)} + )} +
+
+ ); +}; + +export default GrpcResponsePane; diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js index d4fbee07e..595c170e2 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js @@ -1,7 +1,9 @@ import React, { useState, useEffect } from 'react'; import CodeEditor from 'components/CodeEditor/index'; import { get } from 'lodash'; +import find from 'lodash/find'; import { useDispatch, useSelector } from 'react-redux'; +import { updateResponsePaneScrollPosition } from 'providers/ReduxStore/slices/tabs'; import { sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import { Document, Page } from 'react-pdf'; import 'pdfjs-dist/build/pdf.worker'; @@ -51,6 +53,10 @@ const QueryResultPreview = ({ displayedTheme }) => { const preferences = useSelector((state) => state.app.preferences); + const tabs = useSelector((state) => state.tabs.tabs); + const activeTabUid = useSelector((state) => state.tabs.activeTabUid); + const focusedTab = find(tabs, (t) => t.uid === activeTabUid); + const dispatch = useDispatch(); const [numPages, setNumPages] = useState(null); @@ -66,9 +72,19 @@ const QueryResultPreview = ({ if (disableRunEventListener) { return; } + dispatch(sendRequest(item, collection.uid)); }; + const onScroll = (event) => { + dispatch( + updateResponsePaneScrollPosition({ + uid: focusedTab.uid, + scrollY: event.doc.scrollTop + }) + ); + }; + switch (previewTab?.mode) { case 'preview-web': { const webViewSrc = data.replace('', ``); @@ -111,8 +127,10 @@ const QueryResultPreview = ({ fontSize={get(preferences, 'font.codeFontSize')} theme={displayedTheme} onRun={onRun} + onScroll={onScroll} value={formattedData} mode={mode} + initialScroll={focusedTab.responsePaneScrollPosition || 0} readOnly /> ); diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index a52d1499b..c3e43fd12 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -80,10 +80,6 @@ const QueryResult = ({ item, collection, data, dataBuffer, disableRunEventListen const [filter, setFilter] = useState(null); const [showLargeResponse, setShowLargeResponse] = useState(false); const responseEncoding = getEncoding(headers); - const formattedData = useMemo( - () => formatResponse(data, dataBuffer, responseEncoding, mode, filter), - [data, dataBuffer, responseEncoding, mode, filter] - ); const { displayedTheme } = useTheme(); const responseSize = useMemo(() => { @@ -105,6 +101,16 @@ const QueryResult = ({ item, collection, data, dataBuffer, disableRunEventListen const isLargeResponse = responseSize > 10 * 1024 * 1024; // 10 MB + const formattedData = useMemo( + () => { + if (isLargeResponse && !showLargeResponse) { + return ''; + } + return formatResponse(data, dataBuffer, responseEncoding, mode, filter); + }, + [data, dataBuffer, responseEncoding, mode, filter, isLargeResponse, showLargeResponse] + ); + const debouncedResultFilterOnChange = debounce((e) => { setFilter(e.target.value); }, 250); diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js index b1cff2157..fcdeaaca3 100644 --- a/packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js +++ b/packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js @@ -20,7 +20,7 @@ const ResponseSize = ({ size }) => { } return ( - + {sizeToDisplay} ); diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js index ed05e944c..52b8b84a3 100644 --- a/packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js +++ b/packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js @@ -1,9 +1,9 @@ import React from 'react'; import StyledWrapper from './StyledWrapper'; +import isNumber from 'lodash/isNumber'; const ResponseTime = ({ duration }) => { let durationToDisplay = ''; - if (duration > 1000) { // duration greater than a second let seconds = Math.floor(duration / 1000); @@ -13,6 +13,10 @@ const ResponseTime = ({ duration }) => { durationToDisplay = duration + 'ms'; } + if (!isNumber(duration)) { + return null; + } + return {durationToDisplay}; }; export default ResponseTime; diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/GrpcTimelineItem/index.js b/packages/bruno-app/src/components/ResponsePane/Timeline/GrpcTimelineItem/index.js new file mode 100644 index 000000000..5e049118e --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/GrpcTimelineItem/index.js @@ -0,0 +1,274 @@ +import { useState } from "react"; +import { RelativeTime } from "../TimelineItem/Common/Time/index"; +import Status from "../TimelineItem/Common/Status/index"; +import { + IconChevronDown, + IconChevronRight, + IconServer, + IconDatabase, + IconAlertCircle, + IconCircleCheck, + IconCircleX, + IconX, + IconSend +} from '@tabler/icons'; + +// Icons for different event types +const EventTypeIcons = { + metadata: , + response: , + request: , + message: , + status: , + error: , + end: , + cancel: +}; + +// Event type display names +const EventTypeNames = { + metadata: "Metadata", + response: "Response Message", + request: "Request", + message: "Message", + status: "Status", + error: "Error", + end: "Stream Ended", + cancel: "Cancelled" +}; + +// Colors for different event types +const EventTypeColors = { + metadata: "border-blue-500/20", + response: "border-green-500/20", + request: "border-orange-500/20", + message: "border-orange-500/20", + status: "border-purple-500/20", + error: "border-red-500/20", + end: "border-gray-500/20", + cancel: "border-amber-500/20" +}; + +const GrpcTimelineItem = ({ timestamp, request, response, eventType, eventData, item, collection, width }) => { + const [isCollapsed, setIsCollapsed] = useState(true); + const toggleCollapse = () => setIsCollapsed(prev => !prev); + + // Use requestSent if available, otherwise fall back to request + const effectiveRequest = item.requestSent || request || item.request || {}; + + // Extract relevant data from request and response + const { method, url = '' } = effectiveRequest; + const { statusCode, statusText, duration } = response || {}; + + // Get event-specific icon and color + const eventIcon = EventTypeIcons[eventType] || ; + const eventColor = EventTypeColors[eventType] || "border-gray-500/50"; + const eventName = EventTypeNames[eventType] || "Event"; + + + // Render appropriate content based on event type + const renderEventContent = () => { + + const isClientStreaming = effectiveRequest.methodType === 'client-streaming' || effectiveRequest.methodType === 'bidi-streaming'; + + switch(eventType) { + case 'request': + return ( +
+ + {effectiveRequest.headers && Object.keys(effectiveRequest.headers).length > 0 && ( +
+
Metadata
+
+ {Object.entries(effectiveRequest.headers).map(([key, value], idx) => ( +
+
{key}:
+
{value}
+
+ ))} +
+
+ )} + + {/* gRPC Messages section */} + {!isClientStreaming && effectiveRequest.body?.mode === 'grpc' && effectiveRequest.body?.grpc?.length > 0 && ( +
+
+ Message +
+
+ {effectiveRequest.body.grpc.filter((_, index) => index === 0).map((message, idx) => ( +
+
+                        {typeof message.content === 'string' 
+                          ? message.content 
+                          : JSON.stringify(message.content, null, 2)}
+                      
+
+ ))} +
+
+ )} +
+ ); + + case 'message': + return ( +
+
Message
+
+                {typeof eventData === 'string' 
+                  ? eventData 
+                  : JSON.stringify(eventData, null, 2)}
+              
+
+ ); + + case 'metadata': + return ( +
+
Metadata Headers
+ {response.metadata && response.metadata.length > 0 ? ( +
+ {response.metadata.map((header, idx) => ( +
+
{header.name}:
+
{header.value}
+
+ ))} +
+ ) : ( +
No metadata headers
+ )} +
+ ); + + case 'response': + // For message responses, show the response data + return ( +
+
+ Response Message #{(response.responses.length || 0)} +
+ {response.responses && response.responses.length > 0 ? ( +
+                {JSON.stringify(response.responses[response.responses.length - 1], null, 2)}
+              
+ ) : ( +
Empty message
+ )} +
+ ); + + case 'status': + // For status events, show status and trailers + return ( +
+
+ +
+ + {response.statusDescription && ( +
{response.statusDescription}
+ )} + + {response.trailers && response.trailers.length > 0 && ( + <> +
Trailers
+
+ {response.trailers.map((trailer, idx) => ( +
+
{trailer.name}:
+
{trailer.value || ''}
+
+ ))} +
+ + )} +
+ ); + + case 'error': + // For error events, show error details + return ( +
+
Error
+
{response.error || "Unknown error"}
+ +
+ +
+ + {response.trailers && response.trailers.length > 0 && ( + <> +
Error Metadata
+
+ {response.trailers.map((trailer, idx) => ( +
+
{trailer.name}:
+
{trailer.value}
+
+ ))} +
+ + )} +
+ ); + + case 'end': + // For end events, show summary + return ( +
+
Stream Ended
+
+ Total messages: {response.responses.length || 0} +
+
+ ); + + case 'cancel': + // For cancel events, show cancellation info + return ( +
+
Stream Cancelled
+
{response.statusDescription || "The gRPC stream was cancelled"}
+
+ ); + + default: + return null; + } + }; + + return ( +
+
+ {isCollapsed ? : } + {eventIcon} + {eventName} + {eventType === 'request' && effectiveRequest.methodType && ( + + {effectiveRequest.methodType} + + )} + {eventType === 'status' && ( +
+ +
+ )} +
[{new Date(timestamp).toISOString()}]
+ + + +
+ + {/* Always show the URL */} +
{url}
+ + {/* Expanded content - only show for non-status items */} + {!isCollapsed && renderEventContent()} +
+ ); +}; + +export default GrpcTimelineItem; \ No newline at end of file diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js index 4b7cb28a7..263d45245 100644 --- a/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js @@ -1,6 +1,15 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` + position: relative; + overflow-y: auto; + height: 100%; + flex: 1; + + .timeline-container { + flex: 1; + } + .timeline-event { padding: 8px 0 0 0; cursor: pointer; diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Request/index.js b/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Request/index.js index 400f3ffe5..053be2916 100644 --- a/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Request/index.js +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Request/index.js @@ -26,7 +26,7 @@ const Request = ({ collection, request, item }) => {
{/* Method and URL */}
-
{url}
+
{url}
{/* Headers */} diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/index.js b/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/index.js index ff33e41ec..c5fc91295 100644 --- a/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/index.js +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/index.js @@ -16,7 +16,7 @@ const TimelineItem = ({ timestamp, request, response, item, collection, isOauth2 return (
-
+
diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/index.js b/packages/bruno-app/src/components/ResponsePane/Timeline/index.js index 98fe1479a..79bf5725b 100644 --- a/packages/bruno-app/src/components/ResponsePane/Timeline/index.js +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/index.js @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; +import React from 'react'; import StyledWrapper from './StyledWrapper'; import { findItemInCollection, findParentItemInCollection } from 'utils/collections/index'; import { get } from 'lodash'; import TimelineItem from './TimelineItem/index'; +import GrpcTimelineItem from './GrpcTimelineItem/index'; const getEffectiveAuthSource = (collection, item) => { const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); @@ -44,9 +45,10 @@ const getEffectiveAuthSource = (collection, item) => { const Timeline = ({ collection, item }) => { // Get the effective auth source if auth mode is inherit const authSource = getEffectiveAuthSource(collection, item); - + const isGrpcRequest = item.type === 'grpc-request'; + // Filter timeline entries based on new rules - const combinedTimeline = ([...(collection.timeline || [])]).filter(obj => { + const combinedTimeline = ([...(collection?.timeline || [])]).filter(obj => { // Always show entries for this item if (obj.itemUid === item.uid) return true; @@ -57,43 +59,68 @@ const Timeline = ({ collection, item }) => { } return false; - }).sort((a, b) => b.timestamp - a.timestamp); + }).sort((a, b) => b.timestamp - a.timestamp) return ( - {combinedTimeline.map((event, index) => { - if (event.type === 'request') { - const { data, timestamp } = event; - const { request, response } = data; - return ( -
- -
- ); - } else if (event.type === 'oauth2') { - const { data, timestamp } = event; - const { debugInfo } = data; - return ( -
-
-
- OAuth2.0 Calls + {/* Timeline container with scrollbar */} +
+ {combinedTimeline.map((event, index) => { + // Handle regular requests + if (event.type === 'request') { + + const { data, timestamp, eventType } = event; + const { request, response, eventData = {}, timestamp: eventTimestamp = timestamp } = data; + + if (isGrpcRequest) { + return ( +
+
+ ); + } + + // Regular HTTP request + return ( +
+
+ ); + } + // Handle OAuth2 events + else if (event.type === 'oauth2') { + const { data, timestamp } = event; + const { debugInfo } = data; + return ( +
+
+
+ OAuth2.0 Calls +
+
{debugInfo && debugInfo.length > 0 ? ( debugInfo.map((data, idx) => ( -
+
{
No debug information available.
)}
-
- ); - } - - return null; - })} +
+ ); + } + + return null; + })} +
); }; diff --git a/packages/bruno-app/src/components/ResponsePane/index.js b/packages/bruno-app/src/components/ResponsePane/index.js index 22955ac2d..d9147632a 100644 --- a/packages/bruno-app/src/components/ResponsePane/index.js +++ b/packages/bruno-app/src/components/ResponsePane/index.js @@ -176,7 +176,7 @@ const ResponsePane = ({ item, collection }) => { ) : null}
{ } return ( - +
selectTab('response')}> Response @@ -128,7 +127,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
-
+
{hasScriptError && showScriptErrorCard && ( props.theme.sidebar.bg}; + height: 100%; + display: flex; + flex-direction: column; + width: 100%; + overflow: hidden; + + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + border-bottom: 1px solid ${props => props.theme.sidebar.dragbar}; + margin-bottom: 0.5rem; + + .counter { + font-size: 0.875rem; + font-weight: 500; + } + + .actions { + display: flex; + align-items: center; + gap: 0.75rem; + } + + .btn-select-all, + .btn-reset { + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + color: ${props => props.theme.textLink}; + background: none; + border: none; + padding: 0.25rem 0.5rem; + cursor: pointer; + + &:hover { + text-decoration: underline; + } + } + } + + .request-list { + flex: 1; + overflow-y: auto; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: ${props => props.theme.console.scrollbarThumb}; + border-radius: 3px; + } + + .loading-message, + .empty-message { + padding: 0.75rem; + color: ${props => props.theme.colors.text.muted}; + font-size: 0.875rem; + } + + .requests-container { + padding: 0.5rem; + position: relative; + } + } + + .request-item { + display: flex; + align-items: center; + padding: 0.5rem; + border-radius: 4px; + margin-bottom: 0.25rem; + position: relative; + height: 2.5rem; + border: 1px solid transparent; + background-color: ${props => props.theme.sidebar.bg}; + transition: transform 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease; + + &.is-selected { + background-color: ${props => props.theme.requestTabs.active.bg}; + } + + &.is-dragging { + opacity: 0.5; + background-color: ${props => props.theme.sidebar.bg}; + border: 1px dashed ${props => props.theme.sidebar.dragbar}; + transform: scale(0.98); + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.12); + z-index: 5; + } + + &::before, + &::after { + content: ''; + position: absolute; + left: 0; + right: 0; + height: 2px; + background: ${props => props.theme.dragAndDrop?.border || props.theme.textLink}; + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease; + } + + &::before { + top: -1px; + } + + &::after { + bottom: -1px; + } + + &.drop-target-above { + &::before { + opacity: 1; + height: 2px; + background: ${props => props.theme.dragAndDrop?.border || props.theme.textLink}; + } + } + + &.drop-target-below { + &::after { + opacity: 1; + height: 2px; + background: ${props => props.theme.dragAndDrop?.border || props.theme.textLink}; + } + } + + .drag-handle { + cursor: grab; + margin-right: 0.25rem; + color: ${props => props.theme.sidebar.muted}; + display: flex; + align-items: center; + transition: color 0.15s ease; + + &:hover { + color: ${props => props.theme.text}; + } + + &:active { + cursor: grabbing; + color: ${props => props.theme.textLink}; + } + } + + .checkbox-container { + cursor: pointer; + margin-right: 0.5rem; + + .checkbox { + width: 1rem; + height: 1rem; + border: 1px solid ${props => props.theme.sidebar.dragbar}; + border-radius: 3px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.1s ease; + + &:hover { + border-color: ${props => props.theme.textLink}; + } + } + } + + .method { + font-family: monospace; + font-size: 0.75rem; + font-weight: 500; + margin-right: 0.5rem; + min-width: 3rem; + color: ${props => props.theme.sidebar.muted}; // Default color for unknown methods + + &.method-get { + color: ${props => props.theme.request.methods.get}; + } + + &.method-post { + color: ${props => props.theme.request.methods.post}; + } + + &.method-put { + color: ${props => props.theme.request.methods.put}; + } + + &.method-delete { + color: ${props => props.theme.request.methods.delete}; + } + + &.method-patch { + color: ${props => props.theme.request.methods.patch}; + } + + &.method-options { + color: ${props => props.theme.request.methods.options}; + } + + &.method-head { + color: ${props => props.theme.request.methods.head}; + } + } + + .request-name { + flex: 1; + font-size: 0.875rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + .folder-path { + margin-left: 0.5rem; + font-size: 0.75rem; + color: ${props => props.theme.sidebar.muted}; + } + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx b/packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx new file mode 100644 index 000000000..aa3a3c9f0 --- /dev/null +++ b/packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx @@ -0,0 +1,327 @@ +import React, { useEffect, useState, useCallback, useRef } from 'react'; +import { useDrag, useDrop } from 'react-dnd'; +import { getEmptyImage } from 'react-dnd-html5-backend'; +import { IconGripVertical, IconCheck, IconAdjustmentsAlt } from '@tabler/icons'; +import { useDispatch } from 'react-redux'; +import { updateRunnerConfiguration } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; +import { isItemARequest } from 'utils/collections'; +import path from 'utils/common/path'; +import { cloneDeep, get } from 'lodash'; + +const ItemTypes = { + REQUEST_ITEM: 'request-item' +}; + +const RequestItem = ({ item, index, moveItem, isSelected, onSelect, onDrop }) => { + const ref = useRef(null); + const [dropType, setDropType] = useState(null); + + const determineDropType = (monitor) => { + const hoverBoundingRect = ref.current?.getBoundingClientRect(); + const clientOffset = monitor.getClientOffset(); + if (!hoverBoundingRect || !clientOffset) return null; + + const clientY = clientOffset.y - hoverBoundingRect.top; + const middleY = hoverBoundingRect.height / 2; + + return clientY < middleY ? 'above' : 'below'; + }; + + const [{ isDragging }, drag, preview] = useDrag({ + type: ItemTypes.REQUEST_ITEM, + item: { uid: item.uid, name: item.name, request: item.request, index }, + collect: (monitor) => ({ isDragging: monitor.isDragging() }), + options: { + dropEffect: "move" + }, + end: (draggedItem, monitor) => { + if (monitor.didDrop()) { + onDrop(); + } + }, + }); + + const [{ isOver, canDrop }, drop] = useDrop({ + accept: ItemTypes.REQUEST_ITEM, + hover: (draggedItem, monitor) => { + if (draggedItem.uid === item.uid) { + setDropType(null); + return; + } + + const dropType = determineDropType(monitor); + setDropType(dropType); + }, + drop: (draggedItem, monitor) => { + if (draggedItem.uid === item.uid) return; + + const dropType = determineDropType(monitor); + let targetIndex = index; + + if (dropType === 'below') { + targetIndex = index + 1; + } + + if (draggedItem.index < targetIndex) { + targetIndex = targetIndex - 1; + } + + moveItem(draggedItem.uid, targetIndex); + setDropType(null); + return { item: draggedItem }; + }, + collect: (monitor) => ({ + isOver: monitor.isOver(), + canDrop: monitor.canDrop() + }), + }); + + useEffect(() => { + preview(getEmptyImage(), { captureDraggingState: true }); + }, []); + + // Clear drop type when not hovering + useEffect(() => { + if (!isOver) { + setDropType(null); + } + }, [isOver]); + + drag(drop(ref)); + + const itemClasses = [ + 'request-item', + isDragging ? 'is-dragging' : '', + isSelected ? 'is-selected' : '', + isOver && canDrop && dropType === 'above' ? 'drop-target-above' : '', + isOver && canDrop && dropType === 'below' ? 'drop-target-below' : '' + ].filter(Boolean).join(' '); + + return ( +
+
+ +
+ +
onSelect(item)}> +
+ {isSelected && } +
+
+ +
+ {item.request?.method.toUpperCase()} +
+ +
+ {item.name} + {item.folderPath && ( + {item.folderPath} + )} +
+
+ ); +}; + +const RunConfigurationPanel = ({ collection, selectedItems, setSelectedItems }) => { + const dispatch = useDispatch(); + const [flattenedRequests, setFlattenedRequests] = useState([]); + const [originalRequests, setOriginalRequests] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + const flattenRequests = useCallback((collection) => { + const result = []; + + const processItems = (items) => { + if (!items?.length) return; + + items.forEach(item => { + if (isItemARequest(item) && !item.partial) { + const relativePath = path.relative(collection.pathname, path.dirname(item.pathname)); + const folderPath = relativePath !== '.' ? relativePath : ''; + + result.push({ + ...item, + folderPath: folderPath.replace(/\\/g, '/') + }); + } + + if (item.items?.length) { + processItems(item.items); + } + }); + }; + + processItems(collection.items); + return result; + }, []); + + useEffect(() => { + setIsLoading(true); + + try { + const structureCopy = cloneDeep(collection); + const requests = flattenRequests(structureCopy); + + const savedConfiguration = get(collection, 'runnerConfiguration', null); + if (savedConfiguration?.requestItemsOrder?.length > 0) { + const orderedRequests = []; + const requestMap = new Map(requests.map(req => [req.uid, req])); + + savedConfiguration.requestItemsOrder.forEach(uid => { + const request = requestMap.get(uid); + if (request) { + orderedRequests.push(request); + requestMap.delete(uid); + } + }); + + requestMap.forEach(request => { + orderedRequests.push(request); + }); + + setFlattenedRequests(orderedRequests); + } else { + setFlattenedRequests(requests); + } + + setOriginalRequests(cloneDeep(requests)); + } catch (error) { + console.error("Error loading collection structure:", error); + } finally { + setIsLoading(false); + } + }, [collection, flattenRequests]); + + const moveItem = useCallback((draggedItemUid, hoverIndex) => { + setFlattenedRequests((prevRequests) => { + const dragIndex = prevRequests.findIndex(item => item.uid === draggedItemUid); + + if (dragIndex === -1 || dragIndex === hoverIndex) { + return prevRequests; + } + + const updatedRequests = [...prevRequests]; + const [draggedItem] = updatedRequests.splice(dragIndex, 1); + updatedRequests.splice(hoverIndex, 0, draggedItem); + + return updatedRequests; + }); + }, []); + + const handleDrop = useCallback(() => { + const selectedUids = new Set(selectedItems); + + setFlattenedRequests(currentRequests => { + const newOrderedSelectedUids = currentRequests + .filter(item => selectedUids.has(item.uid)) + .map(item => item.uid); + + const allRequestUidsOrder = currentRequests.map(item => item.uid); + + setSelectedItems(newOrderedSelectedUids); + dispatch(updateRunnerConfiguration(collection.uid, newOrderedSelectedUids, allRequestUidsOrder)); + + return currentRequests; + }); + }, [selectedItems, collection.uid, dispatch, setSelectedItems]); + + const handleRequestSelect = useCallback((item) => { + try { + if (selectedItems.includes(item.uid)) { + const newSelectedUids = selectedItems.filter(uid => uid !== item.uid); + setSelectedItems(newSelectedUids); + + const allRequestUidsOrder = flattenedRequests.map(item => item.uid); + dispatch(updateRunnerConfiguration(collection.uid, newSelectedUids, allRequestUidsOrder)); + } else { + const newSelectedUids = [...selectedItems, item.uid]; + + const orderedSelectedUids = flattenedRequests + .filter(req => newSelectedUids.includes(req.uid)) + .map(req => req.uid); + + setSelectedItems(orderedSelectedUids); + + const allRequestUidsOrder = flattenedRequests.map(item => item.uid); + dispatch(updateRunnerConfiguration(collection.uid, orderedSelectedUids, allRequestUidsOrder)); + } + } catch (error) { + console.error("Error selecting item:", error); + } + }, [selectedItems, setSelectedItems, flattenedRequests, dispatch, collection.uid]); + + const handleSelectAll = useCallback(() => { + try { + const allRequestUidsOrder = flattenedRequests.map(item => item.uid); + + if (selectedItems.length === flattenedRequests.length) { + setSelectedItems([]); + dispatch(updateRunnerConfiguration(collection.uid, [], allRequestUidsOrder)); + } else { + setSelectedItems(allRequestUidsOrder); + dispatch(updateRunnerConfiguration(collection.uid, allRequestUidsOrder, allRequestUidsOrder)); + } + } catch (error) { + console.error("Error selecting/deselecting all items:", error); + } + }, [flattenedRequests, selectedItems, setSelectedItems, dispatch, collection.uid]); + + const handleReset = useCallback(() => { + try { + setFlattenedRequests(cloneDeep(originalRequests)); + setSelectedItems([]); + dispatch(updateRunnerConfiguration(collection.uid, [], [])); + } catch (error) { + console.error("Error resetting configuration:", error); + } + }, [originalRequests, setSelectedItems, collection.uid, dispatch]); + + return ( + +
+
+ {selectedItems.length} of {flattenedRequests.length} selected +
+
+ + +
+
+ +
+ {isLoading ? ( +
Loading requests...
+ ) : flattenedRequests.length === 0 ? ( +
No requests found in this collection
+ ) : ( +
+ {flattenedRequests.map((item, idx) => { + const isSelected = selectedItems.includes(item.uid); + + return ( + handleRequestSelect(item)} + moveItem={moveItem} + onDrop={handleDrop} + /> + ); + })} +
+ )} +
+
+ ); +}; + +export default RunConfigurationPanel; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx b/packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx index 984452d78..b61b5b9f6 100644 --- a/packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx @@ -89,13 +89,15 @@ const RunnerTags = ({ collectionUid, className = '' }) => { return (
- setTagsEnabled(!tagsEnabled)} /> +
{tagsEnabled && (
diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx index f2dbe9d7f..7956eb1ad 100644 --- a/packages/bruno-app/src/components/RunnerResults/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/index.jsx @@ -2,15 +2,17 @@ import React, { useState, useRef, useEffect } from 'react'; import path from 'utils/common/path'; import { useDispatch } from 'react-redux'; import { get, cloneDeep } from 'lodash'; -import { runCollectionFolder, cancelRunnerExecution } from 'providers/ReduxStore/slices/collections/actions'; +import { runCollectionFolder, cancelRunnerExecution, mountCollection, updateRunnerConfiguration } from 'providers/ReduxStore/slices/collections/actions'; import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections'; import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections'; -import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun } from '@tabler/icons'; +import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun, IconLoader2 } from '@tabler/icons'; import ResponsePane from './ResponsePane'; import StyledWrapper from './StyledWrapper'; import { areItemsLoading } from 'utils/collections'; import RunnerTags from './RunnerTags/index'; +import RunConfigurationPanel from './RunConfigurationPanel'; import { getRequestItemsForCollectionRun } from 'utils/collections/index'; +import { updateRunnerTagsDetails } from 'providers/ReduxStore/slices/collections/index'; const getDisplayName = (fullPath, pathname, name = '') => { let relativePath = path.relative(fullPath, pathname); @@ -25,25 +27,27 @@ const getTestStatus = (results) => { }; const allTestsPassed = (item) => { - return item.status !== 'error' && - item.testStatus === 'pass' && - item.assertionStatus === 'pass' && - item.preRequestTestStatus === 'pass' && - item.postResponseTestStatus === 'pass'; + return item.status !== 'error' && + item.testStatus === 'pass' && + item.assertionStatus === 'pass' && + item.preRequestTestStatus === 'pass' && + item.postResponseTestStatus === 'pass'; }; const anyTestFailed = (item) => { - return item.status === 'error' || - item.testStatus === 'fail' || - item.assertionStatus === 'fail' || - item.preRequestTestStatus === 'fail' || - item.postResponseTestStatus === 'fail'; + return item.status === 'error' || + item.testStatus === 'fail' || + item.assertionStatus === 'fail' || + item.preRequestTestStatus === 'fail' || + item.postResponseTestStatus === 'fail'; }; export default function RunnerResults({ collection }) { const dispatch = useDispatch(); const [selectedItem, setSelectedItem] = useState(null); const [delay, setDelay] = useState(null); + const [selectedRequestItems, setSelectedRequestItems] = useState([]); + const [configureMode, setConfigureMode] = useState(false); // ref for the runner output body const runnerBodyRef = useRef(); @@ -62,6 +66,25 @@ export default function RunnerResults({ collection }) { autoScrollRunnerBody(); }, [collection, setSelectedItem]); + useEffect(() => { + const runnerInfo = get(collection, 'runnerResult.info', {}); + if (runnerInfo.status === 'running') { + setConfigureMode(false); + } + }, [collection.runnerResult]); + + useEffect(() => { + const savedConfiguration = get(collection, 'runnerConfiguration', null); + if (savedConfiguration) { + if (savedConfiguration.selectedRequestItems && configureMode) { + setSelectedRequestItems(savedConfiguration.selectedRequestItems); + } + if (savedConfiguration.delay !== undefined && delay === null) { + setDelay(savedConfiguration.delay); + } + } + }, [collection.runnerConfiguration, configureMode, delay]); + const collectionCopy = cloneDeep(collection); const runnerInfo = get(collection, 'runnerResult.info', {}); @@ -93,7 +116,7 @@ export default function RunnerResults({ collection }) { displayName: getDisplayName(collection.pathname, info.pathname, info.name), tags: [...(info.request?.tags || [])].sort(), }; - if (newItem.status !== 'error' && newItem.status !== 'skipped') { + if (newItem.status !== 'error' && newItem.status !== 'skipped' && newItem.status !== 'running') { newItem.testStatus = getTestStatus(newItem.testResults); newItem.assertionStatus = getTestStatus(newItem.assertionResults); newItem.preRequestTestStatus = getTestStatus(newItem.preRequestTestResults); @@ -103,18 +126,41 @@ export default function RunnerResults({ collection }) { }) .filter(Boolean); + const ensureCollectionIsMounted = () => { + if(collection.mountStatus === 'mounted'){ + return; + } + dispatch(mountCollection({ + collectionUid: collection.uid, + collectionPathname: collection.pathname, + brunoConfig: collection.brunoConfig + })); + }; + const runCollection = () => { - dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags)); + if (configureMode && selectedRequestItems.length > 0) { + dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems, delay)); + dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags, selectedRequestItems)); + } else { + dispatch(updateRunnerConfiguration(collection.uid, [], [], delay)); + dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags)); + } }; const runAgain = () => { + ensureCollectionIsMounted(); + // Get the saved configuration to determine what to run + const savedConfiguration = get(collection, 'runnerConfiguration', null); + const savedSelectedItems = savedConfiguration?.selectedRequestItems || []; + const savedDelay = savedConfiguration?.delay !== undefined ? savedConfiguration.delay : delay; dispatch( runCollectionFolder( collection.uid, runnerInfo.folderUid, - runnerInfo.isRecursive, - Number(delay), - tagsEnabled && tags + true, + Number(savedDelay), + tagsEnabled && tags, + savedSelectedItems ) ); }; @@ -125,12 +171,26 @@ export default function RunnerResults({ collection }) { collectionUid: collection.uid }) ); + setSelectedRequestItems([]); + setConfigureMode(false); + setDelay(null); }; const cancelExecution = () => { dispatch(cancelRunnerExecution(runnerInfo.cancelTokenUid)); }; + const toggleConfigureMode = () => { + dispatch(updateRunnerTagsDetails({ collectionUid: collection.uid, tagsEnabled: false })); + setConfigureMode(!configureMode); + }; + + useEffect(() => { + if(tagsEnabled) { + setConfigureMode(false); + } + }, [tagsEnabled]); + const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy); const passedRequests = items.filter(allTestsPassed); const failedRequests = items.filter(anyTestFailed); @@ -142,59 +202,104 @@ export default function RunnerResults({ collection }) { if (!items || !items.length) { return ( - -
- Runner - -
-
- You have {totalRequestsInCollection} requests in this collection. -
- {isCollectionLoading ?
Requests in this collection are still loading.
: null} -
- - setDelay(e.target.value)} - /> -
+ +
+
+
+ Runner + +
+
+ You have {totalRequestsInCollection} requests in this collection. + {isCollectionLoading && ( + + (Loading...) + + )} +
+ {isCollectionLoading ?
Requests in this collection are still loading.
: null} +
+ + setDelay(e.target.value)} + /> +
- {/* Tags for the collection run */} - + {/* Tags for the collection run */} + - + {/* Configure requests option */} +
+
+ + +
+
- +
+ + + +
+
+ + {configureMode && ( +
+ +
+ )} +
); } return ( - -
-
+ +
+
Runner
{runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid && ( - )}
-
+ +
@@ -214,57 +319,59 @@ export default function RunnerResults({ collection }) {
)} - {runnerInfo?.statusText ? + {runnerInfo?.statusText ?
{runnerInfo?.statusText}
- : null} - - {items.map((item) => { - return ( -
-
-
- - {allTestsPassed(item) ? - - : null} - {item.status === 'skipped' ? - - :null} - {anyTestFailed(item) ? - - :null} - - - {item.displayName} - - {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( - - ) : item.responseReceived?.status ? ( - setSelectedItem(item)}> - {item.responseReceived?.status} - -  - {item.responseReceived?.statusText} - - ) : ( - setSelectedItem(item)}> - (request failed) - - )} -
- {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( -
- Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} -
- )} - {item.status == 'error' ?
{item.error}
: null} + : null} -
    - {item.preRequestTestResults - ? item.preRequestTestResults.map((result) => ( + {/* Items list */} +
    + {items.map((item) => { + return ( +
    +
    +
    + + {allTestsPassed(item) ? + + : null} + {item.status === 'skipped' ? + + : null} + {anyTestFailed(item) ? + + : null} + + + {item.displayName} + + {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( + + ) : item.responseReceived?.status ? ( + setSelectedItem(item)}> + {item.responseReceived?.status} + -  + {item.responseReceived?.statusText} + + ) : ( + setSelectedItem(item)}> + (request failed) + + )} +
    + {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( +
    + Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} +
    + )} + {item.status == 'error' ?
    {item.error}
    : null} + +
      + {item.preRequestTestResults + ? item.preRequestTestResults.map((result) => (
    • {result.status === 'pass' ? ( @@ -282,9 +389,9 @@ export default function RunnerResults({ collection }) { )}
    • )) - : null} - {item.postResponseTestResults - ? item.postResponseTestResults.map((result) => ( + : null} + {item.postResponseTestResults + ? item.postResponseTestResults.map((result) => (
    • {result.status === 'pass' ? ( @@ -302,9 +409,9 @@ export default function RunnerResults({ collection }) { )}
    • )) - : null} - {item.testResults - ? item.testResults.map((result) => ( + : null} + {item.testResults + ? item.testResults.map((result) => (
    • {result.status === 'pass' ? ( @@ -322,30 +429,32 @@ export default function RunnerResults({ collection }) { )}
    • )) - : null} - {item.assertionResults?.map((result) => ( -
    • - {result.status === 'pass' ? ( - - - {result.lhsExpr}: {result.rhsExpr} - - ) : ( - <> - - + : null} + {item.assertionResults?.map((result) => ( +
    • + {result.status === 'pass' ? ( + + {result.lhsExpr}: {result.rhsExpr} - {result.error} - - )} -
    • - ))} -
    + ) : ( + <> + + + {result.lhsExpr}: {result.rhsExpr} + + {result.error} + + )} + + ))} +
+
-
- ); - })} + ); + })} +
+ {runnerInfo.status === 'ended' ? (
+ } + /> + +
+ ); +}; + +export default SensitiveFieldWarning; diff --git a/packages/bruno-app/src/components/ShareCollection/index.js b/packages/bruno-app/src/components/ShareCollection/index.js index d0db00905..7fb5fd523 100644 --- a/packages/bruno-app/src/components/ShareCollection/index.js +++ b/packages/bruno-app/src/components/ShareCollection/index.js @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import Modal from 'components/Modal'; -import { IconDownload } from '@tabler/icons'; +import { IconDownload, IconLoader2, IconAlertTriangle } from '@tabler/icons'; import StyledWrapper from './StyledWrapper'; import Bruno from 'components/Bruno'; import exportBrunoCollection from 'utils/collections/export'; @@ -8,10 +8,25 @@ import exportPostmanCollection from 'utils/exporters/postman-collection'; import { cloneDeep } from 'lodash'; import { transformCollectionToSaveToExportAsFile } from 'utils/collections/index'; import { useSelector } from 'react-redux'; -import { findCollectionByUid } from 'utils/collections/index'; +import { findCollectionByUid, areItemsLoading } from 'utils/collections/index'; const ShareCollection = ({ onClose, collectionUid }) => { - const collection = useSelector(state => findCollectionByUid(state.collections.collections, collectionUid)); + const collection = useSelector((state) => findCollectionByUid(state.collections.collections, collectionUid)); + const isCollectionLoading = areItemsLoading(collection); + + const hasGrpcRequests = useMemo(() => { + const checkItem = (item) => { + if (item.type === 'grpc-request') { + return true; + } + if (item.items) { + return item.items.some(checkItem); + } + return false; + }; + return collection?.items?.some(checkItem) || false; + }, [collection]); + const handleExportBrunoCollection = () => { const collectionCopy = cloneDeep(collection); exportBrunoCollection(transformCollectionToSaveToExportAsFile(collectionCopy)); @@ -34,27 +49,55 @@ const ShareCollection = ({ onClose, collectionUid }) => { hideCancel > -
-
-
- -
-
-
Bruno Collection
-
Export in Bruno format
-
+
+
+
+ {isCollectionLoading ? : }
- -
+
+
Bruno Collection
+
{isCollectionLoading ? 'Loading collection...' : 'Export in Bruno format'}
+
+
+ +
+ {hasGrpcRequests && ( +
+ + Note: gRPC requests in this collection will not be exported +
+ )} +
- + {isCollectionLoading ? ( + + ) : ( + + )}
Postman Collection
-
Export in Postman format
+
+ {isCollectionLoading ? 'Loading collection...' : 'Export in Postman format'} +
+
); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemDragPreview/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemDragPreview/index.js index 1ad4065a8..fa1fb960b 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemDragPreview/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemDragPreview/index.js @@ -30,8 +30,9 @@ export const CollectionItemDragPreview = () => { clientOffset: monitor.getClientOffset(), })); if (!isDragging) return null; + if (!item.type) return null; const { x, y } = clientOffset || {}; - const shouldShowFolderIcon = !item.type || item.type === 'folder'; + const shouldShowFolderIcon = item.type === 'folder'; return (
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js index aabaafcba..fbef672eb 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js @@ -11,7 +11,7 @@ import { import { interpolateUrl, interpolateUrlPathParams } from 'utils/url/index'; import { getLanguages } from 'utils/codegenerator/targets'; import { useSelector } from 'react-redux'; -import { getGlobalEnvironmentVariables } from 'utils/collections/index'; +import { getAllVariables, getGlobalEnvironmentVariables } from 'utils/collections/index'; import { resolveInheritedAuth } from './utils/auth-utils'; const GenerateCodeItem = ({ collectionUid, item, onClose }) => { @@ -37,12 +37,13 @@ const GenerateCodeItem = ({ collectionUid, item, onClose }) => { const requestUrl = get(item, 'draft.request.url') !== undefined ? get(item, 'draft.request.url') : get(item, 'request.url'); + const variables = useMemo(() => { + return getAllVariables({ ...collection, globalEnvironmentVariables }, item); + }, [collection, globalEnvironmentVariables, item]); + const interpolatedUrl = interpolateUrl({ url: requestUrl, - globalEnvironmentVariables, - envVars, - runtimeVariables: collection.runtimeVariables, - processEnvVars: collection.processEnvVariables + variables }); // interpolate the path params diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/interpolation.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/interpolation.js index 22a52f84f..e7081b268 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/interpolation.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/interpolation.js @@ -69,24 +69,3 @@ export const interpolateBody = (body, variables = {}) => { return interpolatedBody; }; - -export const createVariablesObject = ({ - globalEnvironmentVariables = {}, - collectionVars = {}, - allVariables = {}, - collection = {}, - runtimeVariables = {}, - processEnvVars = {} -}) => { - return { - ...globalEnvironmentVariables, - ...allVariables, - ...collectionVars, - ...runtimeVariables, - process: { - env: { - ...processEnvVars - } - } - }; -}; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.js index 60f181ed1..41d9236ed 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.js @@ -1,7 +1,7 @@ import { buildHarRequest } from 'utils/codegenerator/har'; import { getAuthHeaders } from 'utils/codegenerator/auth'; import { getAllVariables, getTreePathFromCollectionToItem } from 'utils/collections/index'; -import { interpolateHeaders, interpolateBody, createVariablesObject } from './interpolation'; +import { interpolateHeaders, interpolateBody } from './interpolation'; // Merge headers from collection, folders, and request const mergeHeaders = (collection, request, requestTreePath) => { @@ -46,17 +46,7 @@ const generateSnippet = ({ language, item, collection, shouldInterpolate = false // Get HTTPSnippet dynamically so mocks can be applied in tests const { HTTPSnippet } = require('httpsnippet'); - const allVariables = getAllVariables(collection, item); - - // Create variables object for interpolation - const variables = createVariablesObject({ - globalEnvironmentVariables: collection.globalEnvironmentVariables || {}, - collectionVars: collection.collectionVars || {}, - allVariables, - collection, - runtimeVariables: collection.runtimeVariables || {}, - processEnvVars: collection.processEnvVariables || {} - }); + const variables = getAllVariables(collection, item); const request = item.request; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.spec.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.spec.js index 941ea7a76..43581b2b4 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.spec.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/utils/snippet-generator.spec.js @@ -46,7 +46,10 @@ jest.mock('utils/codegenerator/auth', () => ({ })); jest.mock('utils/collections/index', () => ({ - getAllVariables: jest.fn(() => ({ + getAllVariables: jest.fn((collection) => ({ + ...collection?.globalEnvironmentVariables, + ...collection?.runtimeVariables, + ...collection?.processEnvVariables, baseUrl: 'https://api.example.com', apiKey: 'secret-key-123', userId: '12345' diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/StyledWrapper.js index bdb62e843..04d338d5e 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/StyledWrapper.js @@ -34,6 +34,9 @@ const Wrapper = styled.div` .method-head { color: ${(props) => props.theme.request.methods.head}; } + .method-grpc { + color: ${(props) => props.theme.request.grpc}; + } `; export default Wrapper; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/index.js index e41309871..73cfc50ed 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RequestMethod/index.js @@ -3,10 +3,12 @@ import classnames from 'classnames'; import StyledWrapper from './StyledWrapper'; const RequestMethod = ({ item }) => { - if (!['http-request', 'graphql-request'].includes(item.type)) { + if (!['http-request', 'graphql-request', 'grpc-request'].includes(item.type)) { return null; } + const isGrpc = item.type === 'grpc-request'; + const getClassname = (method = '') => { method = method.toLocaleLowerCase(); return classnames('mr-1', { @@ -16,7 +18,8 @@ const RequestMethod = ({ item }) => { 'method-delete': method === 'delete', 'method-patch': method === 'patch', 'method-head': method === 'head', - 'method-options': method == 'options' + 'method-options': method === 'options', + 'method-grpc': isGrpc, }); }; @@ -24,7 +27,7 @@ const RequestMethod = ({ item }) => {
- {item.request.method.length > 5 ? item.request.method.substring(0, 3) : item.request.method} + {isGrpc ? 'grpc' : item.request.method.length > 5 ? item.request.method.substring(0, 3) : item.request.method}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index a20de70ea..e7a3a21ea 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -217,6 +217,12 @@ const CollectionItem = ({ item, collectionUid, collectionPathname, searchText }) ); }; + // prevent the parent's double-click handler from firing + const handleFolderDoubleClick = (e) => { + e.stopPropagation(); + e.preventDefault(); + }; + const handleRightClick = (event) => { const _menuDropdown = dropdownTippyRef.current; if (_menuDropdown) { @@ -358,6 +364,7 @@ const CollectionItem = ({ item, collectionUid, collectionPathname, searchText }) className={iconClassName} style={{ color: 'rgb(160 160 160)' }} onClick={handleFolderCollapse} + onDoubleClick={handleFolderDoubleClick} /> ) : null}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js index 404258d8d..0f44b467a 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -61,13 +61,14 @@ const Collection = ({ collection, searchText }) => { }; const ensureCollectionIsMounted = () => { - if (collection.mountStatus === 'unmounted') { - dispatch(mountCollection({ - collectionUid: collection.uid, - collectionPathname: collection.pathname, - brunoConfig: collection.brunoConfig - })); + if(collection.mountStatus === 'mounted'){ + return; } + dispatch(mountCollection({ + collectionUid: collection.uid, + collectionPathname: collection.pathname, + brunoConfig: collection.brunoConfig + })); } const hasSearchText = searchText && searchText?.trim()?.length; @@ -111,6 +112,12 @@ const Collection = ({ collection, searchText }) => { dispatch(toggleCollection(collection.uid)); } + // prevent the parent's double-click handler from firing + const handleCollectionDoubleClick = (e) => { + e.stopPropagation(); + e.preventDefault(); + }; + const handleRightClick = (event) => { const _menuDropdown = menuDropdownTippyRef.current; if (_menuDropdown) { @@ -223,6 +230,7 @@ const Collection = ({ collection, searchText }) => { className={`chevron-icon ${iconClassName}`} style={{ width: 16, minWidth: 16, color: 'rgb(160 160 160)' }} onClick={handleCollectionCollapse} + onDoubleClick={handleCollectionDoubleClick} />