Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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": [
Expand Down
16 changes: 8 additions & 8 deletions packages/hoppscotch-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/hoppscotch-backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:20.12.2 AS builder
FROM node:22 AS builder

WORKDIR /usr/src/app

Expand Down
42 changes: 21 additions & 21 deletions packages/hoppscotch-backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hoppscotch-backend",
"version": "2026.6.1",
"version": "2026.7.0",
"description": "",
"author": "",
"private": true,
Expand Down Expand Up @@ -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",
Expand All @@ -64,26 +64,26 @@
"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",
"passport-jwt": "4.0.1",
"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",
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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));
}

/**
Expand Down
10 changes: 5 additions & 5 deletions packages/hoppscotch-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
Expand All @@ -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"
}
}
7 changes: 6 additions & 1 deletion packages/hoppscotch-common/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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%",
Expand Down
Loading
Loading