diff --git a/package.json b/package.json index 6c56e90a415..31d74b362f4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "author": "Hoppscotch (support@hoppscotch.io)", "private": true, "license": "MIT", - "packageManager": "pnpm@10.33.4", + "packageManager": "pnpm@10.34.2", "scripts": { "preinstall": "npx only-allow pnpm", "prepare": "husky", @@ -38,16 +38,22 @@ "@xmldom/xmldom": "0.8.13", "apiconnect-wsdl": "2.0.36", "body-parser": "2.2.1", + "brace-expansion@<=5.0.7": "5.0.8", "cross-spawn": "7.0.6", "execa@<2.0.0": "2.0.0", + "fast-uri@3.1.2": "3.1.4", + "find-my-way@9.6.0": "9.7.0", "form-data@>=4.0.0 <4.0.6": "4.0.6", - "glob@>=11.0.0 <11.1.0": "11.1.0", - "hono@<4.12.25": "4.12.25", - "liquidjs@<10.26.0": "10.26.0", + "js-yaml@>=4.0.0 <4.3.0": "4.3.0", + "js-yaml@>=5.0.0 <=5.2.1": "5.2.2", + "linkify-it@<=5.0.1": "5.0.2", + "liquidjs@<10.27.0": "10.27.1", "minimatch@>=4.0.0 <4.2.5": "4.2.5", - "multer@2.1.1": "2.2.0", - "serialize-javascript@<7.0.3": "7.0.3", - "vue": "3.5.38", + "nodemailer@<=9.0.0": "9.0.1", + "postcss@<=8.5.17": "8.5.18", + "serialize-javascript@<7.0.7": "7.0.7", + "svgo@>=4.0.0 <4.0.2": "4.0.2", + "vue": "3.5.40", "ws": "8.21.0" }, "onlyBuiltDependencies": [ diff --git a/packages/hoppscotch-agent/package.json b/packages/hoppscotch-agent/package.json index 752d4821976..f4c0e65d9d8 100644 --- a/packages/hoppscotch-agent/package.json +++ b/packages/hoppscotch-agent/package.json @@ -21,27 +21,27 @@ "@tauri-apps/api": "2.1.1", "@tauri-apps/plugin-shell": "2.3.3", "@vueuse/core": "14.3.0", - "axios": "1.18.0", + "axios": "1.18.1", "fp-ts": "2.16.11", "lodash-es": "4.18.1", "vue": "3.5.38" }, "devDependencies": { - "@iconify-json/lucide": "1.2.114", + "@iconify-json/lucide": "1.2.118", "@tauri-apps/cli": "2.9.3", "@types/lodash-es": "4.17.12", "@types/node": "24.10.1", - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", - "@vitejs/plugin-vue": "6.0.7", - "@vue/eslint-config-typescript": "14.8.0", - "autoprefixer": "10.5.0", + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@vitejs/plugin-vue": "6.0.8", + "@vue/eslint-config-typescript": "14.9.0", + "autoprefixer": "10.5.4", "cross-env": "10.1.0", "eslint": "9.39.2", "eslint-plugin-prettier": "5.5.6", "eslint-plugin-vue": "10.9.2", "globals": "16.5.0", - "postcss": "8.5.15", + "postcss": "8.5.20", "tailwindcss": "3.4.16", "typescript": "5.9.3", "unplugin-icons": "22.5.0", diff --git a/packages/hoppscotch-backend/Dockerfile b/packages/hoppscotch-backend/Dockerfile index ccc4e8207ae..6f10e452058 100644 --- a/packages/hoppscotch-backend/Dockerfile +++ b/packages/hoppscotch-backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2 AS builder +FROM node:22 AS builder WORKDIR /usr/src/app diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index f34ab149cce..34ca930a154 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -1,6 +1,6 @@ { "name": "hoppscotch-backend", - "version": "2026.6.1", + "version": "2026.7.0", "description": "", "author": "", "private": true, @@ -35,19 +35,19 @@ "@as-integrations/express5": "1.1.2", "@nestjs-modules/mailer": "2.3.7", "@nestjs/apollo": "13.4.2", - "@nestjs/common": "11.1.27", + "@nestjs/common": "11.1.28", "@nestjs/config": "4.0.4", - "@nestjs/core": "11.1.27", + "@nestjs/core": "11.1.28", "@nestjs/graphql": "13.4.2", "@nestjs/jwt": "11.0.2", "@nestjs/passport": "11.0.0", - "@nestjs/platform-express": "11.1.27", + "@nestjs/platform-express": "11.1.28", "@nestjs/schedule": "6.1.3", - "@nestjs/swagger": "11.4.4", + "@nestjs/swagger": "11.4.6", "@nestjs/terminus": "11.1.1", "@nestjs/throttler": "6.5.0", - "@prisma/adapter-pg": "7.8.0", - "@prisma/client": "7.8.0", + "@prisma/adapter-pg": "7.9.0", + "@prisma/client": "7.9.0", "argon2": "0.44.0", "bcrypt": "6.0.0", "class-transformer": "0.5.1", @@ -64,7 +64,7 @@ "handlebars": "4.7.9", "io-ts": "2.2.22", "morgan": "1.11.0", - "nodemailer": "9.0.1", + "nodemailer": "9.0.3", "passport": "0.7.0", "passport-github2": "0.1.12", "passport-google-oauth20": "2.0.0", @@ -72,18 +72,18 @@ "passport-local": "1.0.0", "passport-microsoft": "2.1.0", "pg": "8.22.0", - "posthog-node": "5.38.2", - "prisma": "7.8.0", + "posthog-node": "5.46.1", + "prisma": "7.9.0", "reflect-metadata": "0.2.2", "rimraf": "6.1.3", "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.5", + "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", - "@nestjs/cli": "11.0.23", + "@nestjs/cli": "11.0.24", "@nestjs/schematics": "11.1.0", - "@nestjs/testing": "11.1.27", + "@nestjs/testing": "11.1.28", "@relmify/jest-fp-ts": "2.1.1", "@types/bcrypt": "6.0.0", "@types/cookie-parser": "1.4.10", @@ -95,21 +95,21 @@ "@types/passport-google-oauth20": "2.0.17", "@types/passport-jwt": "4.0.1", "@types/passport-microsoft": "2.1.1", - "@types/supertest": "7.2.0", - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", + "@types/supertest": "7.2.1", + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", "cross-env": "10.1.0", - "eslint": "10.5.0", + "eslint": "10.7.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-prettier": "5.5.6", - "globals": "17.6.0", + "globals": "17.7.0", "jest": "30.4.2", "jest-mock-extended": "4.0.1", - "prettier": "3.8.4", + "prettier": "3.9.6", "source-map-support": "0.5.21", "supertest": "7.2.2", - "ts-jest": "29.4.11", - "ts-loader": "9.6.1", + "ts-jest": "29.4.12", + "ts-loader": "9.6.2", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3" diff --git a/packages/hoppscotch-backend/src/team-request/team-request.service.spec.ts b/packages/hoppscotch-backend/src/team-request/team-request.service.spec.ts index 04a22fa88af..3814844d099 100644 --- a/packages/hoppscotch-backend/src/team-request/team-request.service.spec.ts +++ b/packages/hoppscotch-backend/src/team-request/team-request.service.spec.ts @@ -328,15 +328,74 @@ describe('getRequestsInCollection', () => { }); test('resolves with the correct info for the collection id and a valid cursor', async () => { + mockPrisma.teamRequest.findFirst.mockResolvedValue({ + orderIndex: dbTeamRequests[0].orderIndex, + } as DbTeamRequest); mockPrisma.teamRequest.findMany.mockResolvedValue([dbTeamRequests[1]]); - const response = teamRequestService.getRequestsInCollection( + const response = await teamRequestService.getRequestsInCollection( dbTeamRequests[1].collectionID, dbTeamRequests[0].id, 1, ); - expect(response).resolves.toEqual([teamRequests[1]]); + expect(response).toEqual([teamRequests[1]]); + }); + + test('paginates on the orderIndex of the cursor item, scoped to the collection', async () => { + mockPrisma.teamRequest.findFirst.mockResolvedValue({ + orderIndex: dbTeamRequests[0].orderIndex, + } as DbTeamRequest); + mockPrisma.teamRequest.findMany.mockResolvedValue([dbTeamRequests[1]]); + + await teamRequestService.getRequestsInCollection( + teamCollection.id, + dbTeamRequests[0].id, + 1, + ); + + expect(mockPrisma.teamRequest.findFirst).toHaveBeenCalledWith({ + where: { id: dbTeamRequests[0].id, collectionID: teamCollection.id }, + select: { orderIndex: true }, + }); + expect(mockPrisma.teamRequest.findMany).toHaveBeenCalledWith({ + take: 1, + where: { + collectionID: teamCollection.id, + orderIndex: { gt: dbTeamRequests[0].orderIndex }, + }, + orderBy: { orderIndex: 'asc' }, + }); + }); + + test('resolves with an empty array when cursor is provided but cursor item is not found', async () => { + mockPrisma.teamRequest.findFirst.mockResolvedValue(null); + + const result = await teamRequestService.getRequestsInCollection( + 'testcoll', + 'nonexistent-cursor', + 10, + ); + + expect(result).toEqual([]); + expect(mockPrisma.teamRequest.findMany).not.toHaveBeenCalled(); + }); + + test('does not look up a cursor item when no cursor is provided', async () => { + mockPrisma.teamRequest.findMany.mockResolvedValue(dbTeamRequests); + + await teamRequestService.getRequestsInCollection( + teamCollection.id, + null, + 10, + ); + + expect(mockPrisma.teamRequest.findFirst).not.toHaveBeenCalled(); + expect(mockPrisma.teamRequest.findMany).toHaveBeenCalledWith({ + take: 10, + where: { collectionID: teamCollection.id }, + orderBy: { orderIndex: 'asc' }, + }); }); }); diff --git a/packages/hoppscotch-backend/src/team-request/team-request.service.ts b/packages/hoppscotch-backend/src/team-request/team-request.service.ts index 13c0e90d4ac..0faf18bc56e 100644 --- a/packages/hoppscotch-backend/src/team-request/team-request.service.ts +++ b/packages/hoppscotch-backend/src/team-request/team-request.service.ts @@ -226,8 +226,15 @@ export class TeamRequestService { /** * Fetch team requests by Collection ID + * + * Pagination is keyed on `orderIndex` (unique per collection, see the + * `TeamRequest_teamID_collectionID_orderIndex_key` constraint) instead of + * Prisma's `cursor` + `skip`, so a page never depends on the offset of the + * cursor row within the result set. + * * @param collectionID Collection ID to fetch requests in - * @param cursor Cursor for pagination + * @param cursor ID of the last request of the previous page. Must belong to + * `collectionID`; an unknown cursor resolves to an empty page * @param take Take number of requests * @returns */ @@ -236,20 +243,31 @@ export class TeamRequestService { cursor: string, take = 10, ) { + let whereClause: Prisma.TeamRequestWhereInput = { collectionID }; + + if (cursor) { + const cursorItem = await this.prisma.teamRequest.findFirst({ + where: { id: cursor, collectionID }, + select: { orderIndex: true }, + }); + + if (!cursorItem) return []; + + whereClause = { + collectionID, + orderIndex: { gt: cursorItem.orderIndex }, + }; + } + const dbTeamRequests = await this.prisma.teamRequest.findMany({ - cursor: cursor ? { id: cursor } : undefined, - take: take, - skip: cursor ? 1 : 0, - where: { - collectionID: collectionID, - }, + take, + where: whereClause, orderBy: { orderIndex: 'asc', }, }); - const teamRequests = dbTeamRequests.map((tr) => this.cast(tr)); - return teamRequests; + return dbTeamRequests.map((tr) => this.cast(tr)); } /** diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 722442e9e98..3b996d35352 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -42,7 +42,7 @@ "private": false, "dependencies": { "aws4fetch": "1.0.20", - "axios": "1.18.0", + "axios": "1.18.1", "axios-cookiejar-support": "6.0.5", "chalk": "5.6.2", "commander": "14.0.3", @@ -51,9 +51,9 @@ "jsonc-parser": "3.3.1", "lodash-es": "4.18.1", "papaparse": "5.5.4", - "qs": "6.15.2", + "qs": "6.15.3", "semver": "7.8.5", - "tough-cookie": "6.0.1", + "tough-cookie": "6.0.2", "verzod": "0.4.0", "xmlbuilder2": "4.0.3", "zod": "3.25.32" @@ -66,9 +66,9 @@ "@types/papaparse": "5.5.2", "@types/qs": "6.15.1", "fp-ts": "2.16.11", - "prettier": "3.8.4", + "prettier": "3.8.5", "tsup": "8.5.1", "typescript": "5.9.3", - "vitest": "4.1.9" + "vitest": "4.1.10" } } diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 1f52c235919..ad5439d0f81 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -1301,7 +1301,8 @@ "delete_account": "Delete account", "delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.", "desktop": "Desktop", - "desktop_description": "Update behavior, keyboard handling, and display preferences for the Hoppscotch desktop app.", + "desktop_connection": "Connection", + "desktop_description": "Update behavior, connection timeouts, keyboard handling, and display preferences for the Hoppscotch desktop app.", "desktop_display": "Display", "desktop_keyboard": "Keyboard", "desktop_keyboard_strategy_label": "Match shortcuts by typed letter or physical position", @@ -1316,6 +1317,10 @@ "disable_encode_mode_tooltip": "Never encode the parameters in the request", "disable_update_checks": "Disable automatic update checks", "disable_update_checks_description": "Skip the update check at app startup. Use the button above to check on demand.", + "connection_timeout": "Connection timeout", + "connection_timeout_description": "How long to wait for your instance to respond. Downloading an instance allows ten times this limit. Restart the app for a change to reach the download.", + "connection_timeout_seconds": "{seconds} seconds", + "connection_timeout_seconds_default": "{seconds} seconds (default)", "zoom_level": "Zoom level", "zoom_level_description": "Scales the entire interface. Higher values make text and controls larger on high-resolution screens.", "zoom_level_100": "100%", diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index 5f909fce26c..a725e7b0ed8 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -1,7 +1,7 @@ { "name": "@hoppscotch/common", "private": true, - "version": "2026.6.1", + "version": "2026.7.0", "scripts": { "dev": "pnpm exec npm-run-all -p -l dev:*", "test": "vitest --run", @@ -40,7 +40,7 @@ "@hoppscotch/httpsnippet": "3.0.9", "@hoppscotch/js-sandbox": "workspace:^", "@hoppscotch/kernel": "workspace:^", - "@hoppscotch/plugin-appload": "github:CuriousCorrelation/tauri-plugin-appload#9d4528be4f385bccbe46859631d31aa2ee1ec0b6", + "@hoppscotch/plugin-appload": "github:CuriousCorrelation/tauri-plugin-appload#7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33", "@hoppscotch/ui": "0.2.6", "@hoppscotch/vue-toasted": "0.1.0", "@lezer/highlight": "1.2.1", @@ -53,17 +53,17 @@ "@types/markdown-it": "14.1.2", "@types/node": "24.10.1", "@unhead/vue": "2.1.12", - "@urql/core": "6.0.2", + "@urql/core": "6.0.3", "@urql/devtools": "2.0.3", "@urql/exchange-auth": "3.0.0", "@vueuse/core": "14.3.0", "acorn-walk": "8.3.5", "aws4fetch": "1.0.20", - "axios": "1.18.0", + "axios": "1.18.1", "buffer": "6.0.3", "cookie-es": "2.0.0", "dioc": "3.0.2", - "dompurify": "3.4.11", + "dompurify": "3.4.12", "esprima": "4.0.1", "events": "3.3.0", "fp-ts": "2.16.11", @@ -82,7 +82,7 @@ "jsonc-parser": "3.3.1", "lodash-es": "4.18.1", "lossless-json": "4.3.0", - "markdown-it": "14.2.0", + "markdown-it": "14.3.0", "minisearch": "7.2.0", "monaco-editor": "0.55.1", "nprogress": "0.2.0", @@ -90,9 +90,9 @@ "path": "0.12.7", "postman-collection": "5.3.0", "process": "0.11.10", - "qs": "6.15.2", + "qs": "6.15.3", "quicktype-core": "23.2.6", - "rollup": "4.60.2", + "rollup": "4.60.4", "rxjs": "7.8.2", "set-cookie-parser": "2.7.2", "set-cookie-parser-es": "1.0.5", @@ -114,7 +114,7 @@ "vue": "3.5.38", "vue-i18n": "11.4.6", "vue-json-pretty": "2.6.0", - "vue-pdf-embed": "2.1.4", + "vue-pdf-embed": "2.1.5", "vue-router": "4.6.4", "vue-tippy": "6.7.1", "vuedraggable-es": "4.1.1", @@ -127,7 +127,7 @@ "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", - "@eslint/eslintrc": "3.3.5", + "@eslint/eslintrc": "3.3.6", "@eslint/js": "9.39.2", "@graphql-codegen/add": "6.0.1", "@graphql-codegen/cli": "6.3.1", @@ -137,7 +137,7 @@ "@graphql-codegen/typescript-urql-graphcache": "3.1.1", "@graphql-codegen/urql-introspection": "3.0.1", "@graphql-typed-document-node/core": "3.2.0", - "@iconify-json/lucide": "1.2.114", + "@iconify-json/lucide": "1.2.118", "@import-meta-env/cli": "0.7.4", "@intlify/unplugin-vue-i18n": "11.2.4", "@relmify/jest-fp-ts": "2.1.1", @@ -151,13 +151,13 @@ "@types/qs": "6.15.1", "@types/splitpanes": "2.2.6", "@types/yargs-parser": "21.0.3", - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", - "@vitejs/plugin-vue": "6.0.7", - "@vue/compiler-sfc": "3.5.38", - "@vue/eslint-config-typescript": "14.8.0", - "@vue/runtime-core": "3.5.38", - "autoprefixer": "10.5.0", + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@vitejs/plugin-vue": "6.0.8", + "@vue/compiler-sfc": "3.5.40", + "@vue/eslint-config-typescript": "14.9.0", + "@vue/runtime-core": "3.5.40", + "autoprefixer": "10.5.4", "cross-env": "10.1.0", "dotenv": "17.4.2", "eslint": "9.39.2", @@ -168,8 +168,8 @@ "jsdom": "27.4.0", "npm-run-all": "4.1.5", "openapi-types": "12.1.3", - "postcss": "8.5.15", - "prettier": "3.8.4", + "postcss": "8.5.20", + "prettier": "3.8.5", "prettier-plugin-tailwindcss": "0.7.2", "rollup-plugin-polyfill-node": "0.13.0", "sass": "1.101.0", @@ -187,7 +187,7 @@ "vite-plugin-pages-sitemap": "1.7.1", "vite-plugin-pwa": "1.2.0", "vite-plugin-vue-layouts": "0.11.0", - "vitest": "4.1.9", + "vitest": "4.1.10", "vue-tsc": "1.8.8" } } diff --git a/packages/hoppscotch-common/src/components/collections/ImportExport.vue b/packages/hoppscotch-common/src/components/collections/ImportExport.vue index 051886f2f34..24e19eb4a8c 100644 --- a/packages/hoppscotch-common/src/components/collections/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/collections/ImportExport.vue @@ -45,7 +45,7 @@ import { flushUnmatchedRefIdsFromTree, populateLocalStoresFromCollectionTree, stripCollectionTreeForStore, -} from "~/helpers/secretVariables" +} from "~/helpers/clientLocalVariables" import IconInsomnia from "~icons/hopp/insomnia" import IconPostman from "~icons/hopp/postman" diff --git a/packages/hoppscotch-common/src/components/collections/documentation/PublishDocSnapshotPreview.vue b/packages/hoppscotch-common/src/components/collections/documentation/PublishDocSnapshotPreview.vue index 4ecc9a479d4..6b81de21f90 100644 --- a/packages/hoppscotch-common/src/components/collections/documentation/PublishDocSnapshotPreview.vue +++ b/packages/hoppscotch-common/src/components/collections/documentation/PublishDocSnapshotPreview.vue @@ -429,14 +429,9 @@ const fetchSnapshotPreview = async () => { const parsedVars = typeof rawEnvVars === "string" ? JSON.parse(rawEnvVars) : rawEnvVars if (Array.isArray(parsedVars)) { - snapshotEnvironmentVariables.value = parsedVars.map((v) => { - const normalized = translateToNewEnvironmentVariables(v) - // Ensure currentValue falls back to initialValue - return { - ...normalized, - currentValue: normalized.currentValue || normalized.initialValue, - } - }) + snapshotEnvironmentVariables.value = parsedVars.map((v) => + translateToNewEnvironmentVariables(v) + ) } } catch (e) { console.error("Error parsing snapshot environment variables:", e) diff --git a/packages/hoppscotch-common/src/components/collections/documentation/index.vue b/packages/hoppscotch-common/src/components/collections/documentation/index.vue index 6100f360f6e..16dfdf72be9 100644 --- a/packages/hoppscotch-common/src/components/collections/documentation/index.vue +++ b/packages/hoppscotch-common/src/components/collections/documentation/index.vue @@ -270,7 +270,7 @@ import { import { updateTeamCollection } from "~/helpers/backend/mutations/TeamCollection" import { updateTeamRequest } from "~/helpers/backend/mutations/TeamRequest" -import { stripSecretVariableValuesForWire } from "~/helpers/secretVariables" +import { stripClientLocalValuesForWire } from "~/helpers/clientLocalVariables" import { CollectionDataProps, getSingleTeamCollectionJSON, @@ -736,7 +736,7 @@ const saveCollectionDocumentation = async () => { const data: CollectionDataProps = { auth: collection.auth || { authType: "inherit", authActive: true }, headers: collection.headers || [], - variables: stripSecretVariableValuesForWire(collection.variables || []), + variables: stripClientLocalValuesForWire(collection.variables || []), description: documentationDescription.value, preRequestScript: collection.preRequestScript || "", testScript: collection.testScript || "", @@ -832,7 +832,7 @@ const saveCollectionDocumentationById = async ( const data: CollectionDataProps = { auth: collectionData.auth || { authType: "inherit", authActive: true }, headers: collectionData.headers || [], - variables: stripSecretVariableValuesForWire( + variables: stripClientLocalValuesForWire( collectionData.variables || [] ), description: documentation, diff --git a/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue b/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue index 14803af4914..b901d246e10 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue @@ -250,7 +250,7 @@ import { useService } from "dioc/vue" import { computed, ref } from "vue" import { Picked } from "~/helpers/types/HoppPicked" import { removeGraphqlCollection } from "~/newstore/collections" -import { flushLocalStoresForCollectionTree } from "~/helpers/secretVariables" +import { flushLocalStoresForCollectionTree } from "~/helpers/clientLocalVariables" import { handleTokenValidation } from "~/helpers/handleTokenValidation" import { GQLTabService } from "~/services/tab/graphql" import IconCheckCircle from "~icons/lucide/check-circle" diff --git a/packages/hoppscotch-common/src/components/collections/graphql/Folder.vue b/packages/hoppscotch-common/src/components/collections/graphql/Folder.vue index 2b710b144f2..f134c0a42bf 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/Folder.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/Folder.vue @@ -235,7 +235,7 @@ import { computed, ref } from "vue" import { handleTokenValidation } from "~/helpers/handleTokenValidation" import { Picked } from "~/helpers/types/HoppPicked" import { removeGraphqlFolder } from "~/newstore/collections" -import { flushLocalStoresForCollectionTree } from "~/helpers/secretVariables" +import { flushLocalStoresForCollectionTree } from "~/helpers/clientLocalVariables" import { GQLTabService } from "~/services/tab/graphql" import IconCheckCircle from "~icons/lucide/check-circle" import IconCopy from "~icons/lucide/copy" diff --git a/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue b/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue index f1b72ccc904..9733bab961b 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/ImportExport.vue @@ -40,7 +40,7 @@ import { ensureRefIds, populateLocalStoresFromCollectionTree, stripCollectionTreeForStore, -} from "~/helpers/secretVariables" +} from "~/helpers/clientLocalVariables" const t = useI18n() const toast = useToast() diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index efa2785e95f..d0b24107099 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -412,8 +412,8 @@ import { flushLocalStoresForCollectionTree, flushLocalStoresForTeamCollectionTree, stripCollectionTreeForStore, - stripSecretVariableValuesForWire, -} from "~/helpers/secretVariables" + stripClientLocalValuesForWire, +} from "~/helpers/clientLocalVariables" const t = useI18n() const toast = useToast() @@ -3615,7 +3615,7 @@ const setCollectionProperties = (newCollection: { currentEnvironmentValueService.addEnvironment(storeKey, nonSecretVariables) - collection.variables = stripSecretVariableValuesForWire(filteredVariables) + collection.variables = stripClientLocalValuesForWire(filteredVariables) } if (collectionsType.value.type === "my-collections") { diff --git a/packages/hoppscotch-common/src/components/environments/Add.vue b/packages/hoppscotch-common/src/components/environments/Add.vue index 1a9ec22c80d..7f47f6d8b8b 100644 --- a/packages/hoppscotch-common/src/components/environments/Add.vue +++ b/packages/hoppscotch-common/src/components/environments/Add.vue @@ -79,7 +79,7 @@ import { useToast } from "~/composables/toast" import { GQLError } from "~/helpers/backend/GQLClient" import { updateTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment" import { getEnvActionErrorMessage } from "~/helpers/error-messages" -import { stripSecretVariableValuesForWire } from "~/helpers/secretVariables" +import { stripClientLocalValuesForWire } from "~/helpers/clientLocalVariables" import { setGlobalEnvVariables, updateEnvironment, @@ -206,7 +206,7 @@ const addEnvironment = async () => { await pipe( updateTeamEnvironment( - JSON.stringify(stripSecretVariableValuesForWire(newVariables)), + JSON.stringify(stripClientLocalValuesForWire(newVariables)), scope.value.environment.id, scope.value.environment.environment.name ), @@ -223,8 +223,10 @@ const addEnvironment = async () => { key: editingName.value, currentValue: editingValue.value, isSecret: false, - varIndex: - scope.value.environment.environment.variables.length - 1, + // The new variable is appended at index `length` of the + // pre-append array; `length - 1` collided with the previous + // last variable's slot. + varIndex: scope.value.environment.environment.variables.length, } ) } diff --git a/packages/hoppscotch-common/src/components/environments/ImportExport.vue b/packages/hoppscotch-common/src/components/environments/ImportExport.vue index b1a168d5365..583234acca0 100644 --- a/packages/hoppscotch-common/src/components/environments/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/environments/ImportExport.vue @@ -13,8 +13,8 @@ import { Environment, generateUniqueRefId } from "@hoppscotch/data" import { populateLocalStoresFromVariables, promoteInitialValueForImport, - stripSecretVariableValuesForWire, -} from "~/helpers/secretVariables" + stripClientLocalValuesForWire, +} from "~/helpers/clientLocalVariables" import * as E from "fp-ts/Either" import { ref } from "vue" @@ -418,7 +418,7 @@ const handleImportToStore = async ( ]) // Append stripped imports; varIndex aligns with the hydrated entries. - stripSecretVariableValuesForWire(importedGlobals).forEach( + stripClientLocalValuesForWire(importedGlobals).forEach( ({ key, initialValue, currentValue, secret }) => { addGlobalEnvVariable({ key, initialValue, currentValue, secret }) } @@ -446,7 +446,7 @@ const handleImportToStore = async ( const strippedEnvironments = envsWithIds.map((env) => ({ ...env, - variables: stripSecretVariableValuesForWire(env.variables), + variables: stripClientLocalValuesForWire(env.variables), })) appendEnvironments(strippedEnvironments) toast.success(t("state.file_imported")) @@ -462,7 +462,7 @@ const importToTeams = async (content: Environment[]) => { for (const [, env] of content.entries()) { const res = createTeamEnvironment( - JSON.stringify(stripSecretVariableValuesForWire(env.variables)), + JSON.stringify(stripClientLocalValuesForWire(env.variables)), props.teamId as string, env.name )() diff --git a/packages/hoppscotch-common/src/components/environments/Selector.vue b/packages/hoppscotch-common/src/components/environments/Selector.vue index a553ab2de17..590e2e40487 100644 --- a/packages/hoppscotch-common/src/components/environments/Selector.vue +++ b/packages/hoppscotch-common/src/components/environments/Selector.vue @@ -258,16 +258,10 @@ {{ variable.key }} - - + {{ variable.initialValue }} - - + {{ variable.currentValue }}
@@ -327,16 +321,10 @@ {{ variable.key }} - - + {{ variable.initialValue }} - - + {{ variable.currentValue }}
{ - return (globalVals.value?.variables ?? []).map((variable, index) => ({ +// Resolve each variable's display values. Secrets are masked by length (an +// unset secret renders empty, not a fixed `********`) so the popover matches the +// env tooltip and never claims a value exists when it doesn't. +const resolveDisplayVariable = ( + variable: Environment["variables"][number], + envID: string, + index: number +) => { + if (variable.secret) { + const secretValue = + secretEnvironmentService.getSecretEnvironmentVariableValue(envID, index) + return { + ...variable, + initialValue: maskSecretValue(secretValue?.initialValue), + currentValue: maskSecretValue(secretValue?.value), + } + } + return { ...variable, currentValue: currentEnvironmentValueService.getEnvironmentVariableValue( - "Global", + envID, index ) ?? "", - })) + } +} + +const globalEnvs = computed(() => { + return (globalVals.value?.variables ?? []).map((variable, index) => + resolveDisplayVariable(variable, "Global", index) + ) }) const environmentVariables = computed(() => { if (selectedEnv.value.variables && selectedEnv.value.id) { - return selectedEnv.value.variables.map((variable, index) => ({ - ...variable, - currentValue: - currentEnvironmentValueService.getEnvironmentVariableValue( - selectedEnv.value.id ?? "", - index - ) ?? "", - })) + const envID = selectedEnv.value.id + return selectedEnv.value.variables.map((variable, index) => + resolveDisplayVariable(variable, envID, index) + ) } return [] }) diff --git a/packages/hoppscotch-common/src/components/environments/index.vue b/packages/hoppscotch-common/src/components/environments/index.vue index a74cafac6dc..5f36045284d 100644 --- a/packages/hoppscotch-common/src/components/environments/index.vue +++ b/packages/hoppscotch-common/src/components/environments/index.vue @@ -57,7 +57,7 @@