From 3aa5c853083a3b1d784be12c3bc83fb7dacacac8 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 20 Aug 2026 07:42:04 +0530 Subject: [PATCH 01/14] fix(common): cap connection timeout dropdown width (#6584) --- packages/hoppscotch-common/src/components/settings/Desktop.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hoppscotch-common/src/components/settings/Desktop.vue b/packages/hoppscotch-common/src/components/settings/Desktop.vue index e8d8703285d..792613e5386 100644 --- a/packages/hoppscotch-common/src/components/settings/Desktop.vue +++ b/packages/hoppscotch-common/src/components/settings/Desktop.vue @@ -86,7 +86,7 @@ -
+
Date: Mon, 24 Aug 2026 21:11:51 +0530 Subject: [PATCH 02/14] fix(desktop): probe auth before instance resume (#6587) --- packages/hoppscotch-desktop/package.json | 7 +- .../__tests__/useAppInitialization.spec.ts | 172 ++++++++++++++++++ .../src/composables/useAppInitialization.ts | 132 +++++++++++--- .../src/services/persistence.service.ts | 16 ++ packages/hoppscotch-desktop/vitest.config.mts | 16 ++ pnpm-lock.yaml | 6 + 6 files changed, 318 insertions(+), 31 deletions(-) create mode 100644 packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts create mode 100644 packages/hoppscotch-desktop/vitest.config.mts diff --git a/packages/hoppscotch-desktop/package.json b/packages/hoppscotch-desktop/package.json index 22ec4cbf92d..95d9d78d80b 100644 --- a/packages/hoppscotch-desktop/package.json +++ b/packages/hoppscotch-desktop/package.json @@ -7,6 +7,8 @@ "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview", + "test": "vitest --run", + "test:watch": "vitest", "tauri": "tauri", "lint": "eslint src", "lint:ts": "vue-tsc --noEmit", @@ -19,7 +21,8 @@ "dev:full": "pnpm tauri dev", "build:full": "pnpm tauri build", "dev:portable": "pnpm tauri dev -- --no-default-features --features portable", - "build:portable": "pnpm tauri build -- --no-default-features --features portable" + "build:portable": "pnpm tauri build -- --no-default-features --features portable", + "do-test": "pnpm run test" }, "dependencies": { "@fontsource-variable/inter": "5.2.8", @@ -57,6 +60,7 @@ "eslint-plugin-prettier": "5.5.6", "eslint-plugin-vue": "10.9.2", "globals": "16.5.0", + "jsdom": "27.4.0", "postcss": "8.5.15", "sass": "1.101.0", "tailwindcss": "3.4.16", @@ -64,6 +68,7 @@ "unplugin-icons": "22.5.0", "unplugin-vue-components": "30.0.0", "vite": "7.3.2", + "vitest": "4.1.10", "vue-tsc": "2.2.0" } } diff --git a/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts b/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts new file mode 100644 index 00000000000..814a7beaac5 --- /dev/null +++ b/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts @@ -0,0 +1,172 @@ +import { beforeEach, describe, expect, it, vi } from "vitest" + +import type { Instance } from "@hoppscotch/common/platform/instance" + +type LoadCall = { bundleName: string; host?: string } + +const { load, download, close } = vi.hoisted(() => ({ + load: vi.fn< + (opts: { bundleName: string; host?: string }) => Promise + >(async () => ({ success: true, windowLabel: "instance" })), + download: vi.fn<(opts: { serverUrl: string }) => Promise>( + async () => ({ + version: "26.7.0", + bundleName: "acme", + }) + ), + close: vi.fn<(opts: { windowLabel: string }) => void>(), +})) + +vi.mock("@hoppscotch/plugin-appload", () => ({ load, download, close })) + +vi.mock("@tauri-apps/api/app", () => ({ + getVersion: async () => "26.7.0", +})) + +vi.mock("@tauri-apps/api/core", () => ({ + invoke: async () => undefined, +})) + +vi.mock("~/services/instance-store-migration.service", () => ({ + InstanceStoreMigrationService: { + getInstance: () => ({ + initialize: async () => undefined, + getMigrationStatus: () => ({ value: { status: "completed" } }), + getMigrationError: () => ({ value: null }), + }), + }, +})) + +vi.mock("@hoppscotch/common/composables/desktop-settings", () => ({ + useDesktopSettings: () => ({ + ready: async () => undefined, + settings: { zoomLevel: 1.0 }, + }), +})) + +// One in-memory value per store key, standing in for the Tauri store the +// launcher and the instance webviews share on disk. +const store = { + connectionState: null as unknown, + recentInstances: [] as Instance[], + instanceAuthFailure: null as string | null, +} + +const resource = (key: K) => ({ + get: async () => store[key], + set: async (value: (typeof store)[K]) => { + store[key] = value + }, + watch: async () => () => undefined, +}) + +vi.mock("~/services/persistence.service", () => ({ + DesktopPersistenceService: { + getInstance: () => ({ + connectionState: resource("connectionState"), + recentInstances: resource("recentInstances"), + instanceAuthFailure: resource("instanceAuthFailure"), + init: async () => ({ _tag: "Right", right: undefined }), + }), + }, +})) + +const { useAppInitialization } = await import("../useAppInitialization") + +const ORG_INSTANCE: Instance = { + kind: "cloud-org", + serverUrl: "https://acme.example.com", + displayName: "Acme", + version: "26.7.0", + lastUsed: "2026-08-20T00:00:00.000Z", + bundleName: "Hoppscotch", +} + +// A cloud-org resume calls `load` with the instance's `serverUrl` as `host`, +// where the vendored fallback calls it with no host at all, so the presence +// of `host` is what separates the two outcomes. +const resumedHosts = () => + load.mock.calls + .map(([opts]) => (opts as LoadCall).host) + .filter((host): host is string => host !== undefined) + +describe("loadRecent auth probe", () => { + beforeEach(() => { + store.connectionState = { status: "connected", instance: ORG_INSTANCE } + store.recentInstances = [ORG_INSTANCE] + store.instanceAuthFailure = null + load.mockClear() + close.mockClear() + }) + + // The launcher read side ships ahead of the enterprise writer that records + // a failure, so on every OSS launch the record is absent and startup has + // to behave as it did before the probe existed. Adding the writer later + // fails this test if it changes that. + it("resumes the connected instance when no auth failure is recorded", async () => { + await useAppInitialization().loadRecent() + + expect(resumedHosts()).toEqual([ORG_INSTANCE.serverUrl]) + }) + + it("resumes when the recorded failure belongs to another instance", async () => { + store.instanceAuthFailure = "https://other.example.com" + + await useAppInitialization().loadRecent() + + expect(resumedHosts()).toEqual([ORG_INSTANCE.serverUrl]) + }) + + it("resumes when the record is unreadable", async () => { + const initialization = useAppInitialization() + vi.spyOn( + initialization.persistence.instanceAuthFailure, + "get" + ).mockRejectedValueOnce(new Error("store unavailable")) + + await initialization.loadRecent() + + expect(resumedHosts()).toEqual([ORG_INSTANCE.serverUrl]) + }) + + // The contract the enterprise writer targets. A recorded failure for the + // instance about to be resumed routes startup to the vendored app, whose + // header renders the instance switcher, and the record is consumed so a + // later manual reconnect is not blocked. + it("loads vendored and clears the record on a matching failure", async () => { + store.instanceAuthFailure = ORG_INSTANCE.serverUrl + + await useAppInitialization().loadRecent() + + expect(resumedHosts()).toEqual([]) + expect(load).toHaveBeenCalledWith( + expect.objectContaining({ bundleName: "Hoppscotch" }) + ) + expect(store.instanceAuthFailure).toBeNull() + }) + + // The webview and the launcher record the same server through different + // flows, so the two spellings have to compare equal. + it("matches a recorded failure that differs by case and trailing slash", async () => { + store.instanceAuthFailure = "https://ACME.example.com/desktop-app-server/" + + await useAppInitialization().loadRecent() + + expect(resumedHosts()).toEqual([]) + }) + + // `vendored` runs offline and default `cloud` stays usable signed out, so + // neither is probed and a stale record cannot divert them. + it("skips the probe for instances that need no auth", async () => { + const staleRecord = "https://acme.example.com" + store.instanceAuthFailure = staleRecord + store.connectionState = { + status: "connected", + instance: { ...ORG_INSTANCE, kind: "cloud" }, + } + + await useAppInitialization().loadRecent() + + expect(store.instanceAuthFailure).toBe(staleRecord) + }) +}) diff --git a/packages/hoppscotch-desktop/src/composables/useAppInitialization.ts b/packages/hoppscotch-desktop/src/composables/useAppInitialization.ts index 7b12554824b..89d560212e1 100644 --- a/packages/hoppscotch-desktop/src/composables/useAppInitialization.ts +++ b/packages/hoppscotch-desktop/src/composables/useAppInitialization.ts @@ -35,6 +35,22 @@ export enum AppState { LOADED = "loaded", } +const DESKTOP_APP_SERVER_PATH = "/desktop-app-server" + +// One spelling of an instance URL. The same server is written as +// `https://Acme.example.com/`, `https://acme.example.com` and with the +// `/desktop-app-server` suffix depending on which flow recorded it, so +// every comparison between a stored URL and an instance's `serverUrl` +// normalizes both sides first. +const normalizeServerUrl = (u: string) => { + let n = u.toLowerCase() + while (n.endsWith("/")) n = n.slice(0, -1) + if (n.endsWith(DESKTOP_APP_SERVER_PATH)) + n = n.slice(0, -DESKTOP_APP_SERVER_PATH.length) + while (n.endsWith("/")) n = n.slice(0, -1) + return n +} + export function useAppInitialization() { const persistence = DesktopPersistenceService.getInstance() const migration = InstanceStoreMigrationService.getInstance() @@ -220,15 +236,7 @@ export function useAppInitialization() { version: dlResp.version, bundleName: dlResp.bundleName, } - const DESKTOP_APP_SERVER_PATH = "/desktop-app-server" - const normUrl = (u: string) => { - let n = u.toLowerCase() - while (n.endsWith("/")) n = n.slice(0, -1) - if (n.endsWith(DESKTOP_APP_SERVER_PATH)) - n = n.slice(0, -DESKTOP_APP_SERVER_PATH.length) - while (n.endsWith("/")) n = n.slice(0, -1) - return n - } + const normUrl = normalizeServerUrl try { const recentInstances = await persistence.recentInstances.get() await persistence.recentInstances.set( @@ -295,6 +303,84 @@ export function useAppInitialization() { } } + // Only cloud-org and self-hosted (`on-prem`) instances require auth. + // `vendored` runs fully offline and `cloud` (default cloud) stays usable + // while signed out, so neither can leave the user on a login-required + // screen and neither needs an auth probe before resuming. + const isAuthRequiringInstance = (instance: Instance): boolean => + instance.kind === "cloud-org" || instance.kind === "on-prem" + + // The auth session for an instance is stored in that instance's webview + // (its `localStorage` bearer tokens), a context the launcher window cannot + // read, so the launcher cannot verify the session over the network on its + // own. Instead the webview records the instance's `serverUrl` under + // `instanceAuthFailure` when its auth flow routes to the login-required + // screen. Reading that record here is the launcher's auth probe, a match + // means the last resume of this instance ended unable to + // authenticate, so resuming again would route straight back to the same + // screen with the main window already closed. Returning false lets startup + // continue to the vendored app instead, whose header renders the + // instance switcher. + const probeInstanceAuth = async (instance: Instance): Promise => { + if (!isAuthRequiringInstance(instance)) return true + + try { + const failedUrl = await persistence.instanceAuthFailure.get() + if ( + failedUrl && + normalizeServerUrl(failedUrl) === normalizeServerUrl(instance.serverUrl) + ) { + return false + } + return true + } catch (err) { + // A degraded store must not block a resume that would otherwise + // succeed, so treat an unreadable record as "no known failure". + console.warn("Failed to read instance auth-failure record:", err) + return true + } + } + + // Resume `instance` unless its auth-failure record blocks it. A blocked + // resume loads the vendored app, which persists its own connection state, + // so the user reaches the instance switcher rather than the login-required + // screen the failed instance would route to. Returns true once startup is + // handled here (resume started, or the vendored redirect ran), and false + // when the resume threw so the caller can try the next candidate. + const clearInstanceAuthFailure = async () => { + try { + await persistence.instanceAuthFailure.set(null) + } catch (err) { + console.warn("Failed to clear instance auth-failure record:", err) + } + } + + const tryResumeInstance = async (instance: Instance): Promise => { + if (!(await probeInstanceAuth(instance))) { + mainDiag( + `loadRecent: auth probe failed for ${instance.displayName}, loading vendored` + ) + await loadVendoredInstance() + // The record is a one-shot, so a later manual reconnect through the + // switcher is not blocked once the user re-authenticates. Clearing it + // waits for the vendored app to be up, which `loadVendoredInstance` + // reports through `appState` rather than by throwing. Dropping it + // before that would let the next launch resume the same instance with + // nothing left to record that its auth had failed. + if (appState.value !== AppState.ERROR) { + await clearInstanceAuthFailure() + } + return true + } + try { + await loadVendoredIfMatches(instance) + return true + } catch (err) { + console.warn("Failed to resume instance:", err) + return false + } + } + const loadRecent = async () => { try { statusMessage.value = "Loading application..." @@ -322,16 +408,12 @@ export function useAppInitialization() { mainDiag( `loadRecent: resuming connected instance: kind=${connectionState.instance.kind}, displayName=${connectionState.instance.displayName}` ) + // A `connected` status persists across restarts, so without the + // auth probe in `tryResumeInstance` an auth-gated instance whose + // last resume could not authenticate would be resumed again on + // every launch, routing back to the login-required screen. statusMessage.value = `Connecting to ${connectionState.instance.displayName}...` - try { - await loadVendoredIfMatches(connectionState.instance) - return - } catch (err) { - console.warn( - "Failed to load previously connected instance:", - err - ) - } + if (await tryResumeInstance(connectionState.instance)) return } break @@ -343,12 +425,7 @@ export function useAppInitialization() { connectionState.target ) if (targetInstance) { - try { - await loadVendoredIfMatches(targetInstance) - return - } catch (err) { - console.warn("Failed to resume connection:", err) - } + if (await tryResumeInstance(targetInstance)) return } } break @@ -367,12 +444,7 @@ export function useAppInitialization() { if (mostRecentInstance) { statusMessage.value = `Connecting to ${mostRecentInstance.displayName}...` - try { - await loadVendoredIfMatches(mostRecentInstance) - return - } catch (err) { - console.warn("Failed to load most recent instance:", err) - } + if (await tryResumeInstance(mostRecentInstance)) return } console.log("No recent instances found, loading vendored as fallback") diff --git a/packages/hoppscotch-desktop/src/services/persistence.service.ts b/packages/hoppscotch-desktop/src/services/persistence.service.ts index adbf4c259c1..2281f1f23b9 100644 --- a/packages/hoppscotch-desktop/src/services/persistence.service.ts +++ b/packages/hoppscotch-desktop/src/services/persistence.service.ts @@ -34,6 +34,7 @@ export const STORE_KEYS = { UPDATE_STATE: UPDATE_STATE_STORE_KEY, CONNECTION_STATE: "connectionState", RECENT_INSTANCES: "recentInstances", + INSTANCE_AUTH_FAILURE: "instanceAuthFailure", SCHEMA_VERSION: "schema_version", // Legacy key. Written by portable builds in schema v1. Read only by the // v1 to v2 migration. All other code uses `DESKTOP_SETTINGS`. @@ -219,6 +220,15 @@ export class DesktopPersistenceService { readonly updateState: StoreResource readonly connectionState: StoreResource readonly recentInstances: StoreResource + // Cross-context record of the last instance whose auth flow failed. + // Written by an instance's webview when it routes to its login-required + // screen, read by the launcher on the next startup so it can skip resuming + // an instance it has no way to authenticate and continue to the vendored + // app's switcher. Set to the failing `serverUrl`, or `null` when there is no + // pending failure. The launcher window cannot verify the session itself + // because the bearer tokens are in the instance webview's `localStorage`, + // a separate context, so this shared record is the only auth signal it has. + readonly instanceAuthFailure: StoreResource private constructor() { this.desktopSettings = createStoreResource( @@ -245,6 +255,12 @@ export class DesktopPersistenceService { z.array(INSTANCE_SCHEMA), () => [] ) + this.instanceAuthFailure = createStoreResource( + STORE_NAMESPACE, + STORE_KEYS.INSTANCE_AUTH_FAILURE, + z.string().nullable(), + () => null + ) } public static getInstance(): DesktopPersistenceService { diff --git a/packages/hoppscotch-desktop/vitest.config.mts b/packages/hoppscotch-desktop/vitest.config.mts new file mode 100644 index 00000000000..6d818a9d92d --- /dev/null +++ b/packages/hoppscotch-desktop/vitest.config.mts @@ -0,0 +1,16 @@ +import { defineConfig } from "vitest/config" +import * as path from "path" + +export default defineConfig({ + test: { + // `mainDiag` reads `window.__TAURI_INTERNALS__` on every launcher + // step, so the launcher code needs a DOM even in a unit run. + environment: "jsdom", + }, + resolve: { + alias: { + "~": path.resolve(__dirname, "src"), + "@hoppscotch/common": path.resolve(__dirname, "../hoppscotch-common/src"), + }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a0d37e3ae4..72966c4b048 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1144,6 +1144,9 @@ importers: globals: specifier: 16.5.0 version: 16.5.0 + jsdom: + specifier: 27.4.0 + version: 27.4.0(@noble/hashes@2.2.0) postcss: specifier: 8.5.18 version: 8.5.18 @@ -1165,6 +1168,9 @@ importers: vite: specifier: 7.3.2 version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vitest: + specifier: 4.1.10 + version: 4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) vue-tsc: specifier: 2.2.0 version: 2.2.0(typescript@5.9.3) From 5f2e1f25f956007af6077349e01b46acb666dc27 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 24 Aug 2026 21:12:27 +0530 Subject: [PATCH 03/14] fix(common): agent Set-Cookie header fallback (#6586) --- .../__tests__/cookie-jar.service.spec.ts | 142 ++++++++++++++++- .../src/services/cookie-jar.service.ts | 146 +++++++++++++++++- 2 files changed, 286 insertions(+), 2 deletions(-) diff --git a/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts b/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts index 02294964aed..364ba75d065 100644 --- a/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts +++ b/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it } from "vitest" +import { beforeEach, describe, expect, it, vi } from "vitest" import { TestContainer } from "dioc/testing" import { Cookie } from "@hoppscotch/data" @@ -399,6 +399,48 @@ describe("CookieJarService", () => { expect(service.cookieJar.value.size).toBe(0) }) + it("rejects a Domain the response host does not domain-match", async () => { + await service.extractFromResponse( + [{ name: "sid", value: "attacker", domain: "example.com" }], + new URL("https://attacker.invalid/") + ) + expect(service.cookieJar.value.size).toBe(0) + expect( + service.getCookiesForURL(new URL("https://example.com/")) + ).toHaveLength(0) + }) + + it("accepts a parent Domain from a subdomain host", async () => { + await service.extractFromResponse( + [{ name: "sid", value: "1", domain: "example.com" }], + new URL("https://api.example.com/") + ) + expect( + service.getCookiesForURL(new URL("https://example.com/")) + ).toHaveLength(1) + }) + + it("rejects a partial IP suffix as a Domain attribute", async () => { + await service.extractFromResponse( + [{ name: "sid", value: "1", domain: "1.1" }], + new URL("https://192.168.1.1/") + ) + expect(service.cookieJar.value.size).toBe(0) + expect( + service.getCookiesForURL(new URL("https://10.0.1.1/")) + ).toHaveLength(0) + }) + + it("accepts a Domain attribute equal to the IP host", async () => { + await service.extractFromResponse( + [{ name: "sid", value: "1", domain: "192.168.1.1" }], + new URL("https://192.168.1.1/") + ) + expect( + service.getCookiesForURL(new URL("https://192.168.1.1/")) + ).toHaveLength(1) + }) + it("falls back to default-path when the Path attribute does not start with /", async () => { await service.extractFromResponse( [{ name: "a", value: "1", path: "foo" }], @@ -409,6 +451,104 @@ describe("CookieJarService", () => { }) }) + describe("captureResponseCookies header fallback", () => { + it("parses the Set-Cookie header when structured cookies are undefined", async () => { + await service.captureResponseCookies( + { headers: { "set-cookie": "sid=abc123; Path=/; HttpOnly" } }, + "https://example.com/" + ) + const stored = service.getCookiesForURL(new URL("https://example.com/")) + expect(stored).toHaveLength(1) + expect(stored[0].name).toBe("sid") + expect(stored[0].value).toBe("abc123") + }) + + it("resolves Max-Age into an expiry on the header fallback", async () => { + await service.captureResponseCookies( + { headers: { "set-cookie": "sid=abc; Max-Age=60; Path=/" } }, + "https://example.com/" + ) + const stored = service.getCookiesForURL(new URL("https://example.com/")) + expect(stored).toHaveLength(1) + expect(stored[0].expires).toBeDefined() + expect(new Date(stored[0].expires!).getTime()).toBeGreaterThan(Date.now()) + }) + + it("keeps a percent-encoded value undecoded on the header fallback", async () => { + await service.captureResponseCookies( + { headers: { "set-cookie": "sid=abc%20123; Path=/" } }, + "https://example.com/" + ) + const stored = service.getCookiesForURL(new URL("https://example.com/")) + expect(stored).toHaveLength(1) + expect(stored[0].value).toBe("abc%20123") + }) + + it("splits newline-joined Set-Cookie headers the agent relay concatenates", async () => { + await service.captureResponseCookies( + { headers: { "set-cookie": "a=1; Path=/\nb=2; Path=/" } }, + "https://example.com/" + ) + const stored = service.getCookiesForURL(new URL("https://example.com/")) + expect(stored).toHaveLength(2) + expect(stored.map((c) => c.name).sort()).toEqual(["a", "b"]) + }) + + it("drops an existing cookie on a Max-Age=0 fallback capture", async () => { + // Frozen so the read happens at the same instant as the + // capture, which is the only window in which an expiry equal + // to the capture time still reads as live. + vi.useFakeTimers() + vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z")) + try { + await service.captureResponseCookies( + { headers: { "set-cookie": "sid=abc; Path=/" } }, + "https://example.com/" + ) + await service.captureResponseCookies( + { headers: { "set-cookie": "sid=abc; Max-Age=0; Path=/" } }, + "https://example.com/" + ) + expect( + service.getCookiesForURL(new URL("https://example.com/")) + ).toHaveLength(0) + } finally { + vi.useRealTimers() + } + }) + + it("rejects a cross-domain Domain on the header fallback", async () => { + await service.captureResponseCookies( + { + headers: { + "set-cookie": "sid=attacker; Domain=example.com; Path=/", + }, + }, + "https://attacker.invalid/" + ) + expect(service.cookieJar.value.size).toBe(0) + expect( + service.getCookiesForURL(new URL("https://example.com/")) + ).toHaveLength(0) + }) + + it("does not re-parse headers when cookies is a defined empty array", async () => { + await service.captureResponseCookies( + { cookies: [], headers: { "set-cookie": "sid=abc123; Path=/" } }, + "https://example.com/" + ) + expect(service.cookieJar.value.size).toBe(0) + }) + + it("does nothing when cookies is undefined and no Set-Cookie header is present", async () => { + await service.captureResponseCookies( + { headers: { "content-type": "application/json" } }, + "https://example.com/" + ) + expect(service.cookieJar.value.size).toBe(0) + }) + }) + describe("serializeCookieHeader", () => { it("joins `name=value` pairs with `; `", () => { expect( diff --git a/packages/hoppscotch-common/src/services/cookie-jar.service.ts b/packages/hoppscotch-common/src/services/cookie-jar.service.ts index f0b18a3b01c..a073bc18d2d 100644 --- a/packages/hoppscotch-common/src/services/cookie-jar.service.ts +++ b/packages/hoppscotch-common/src/services/cookie-jar.service.ts @@ -4,6 +4,7 @@ import { parseString as setCookieParse } from "set-cookie-parser-es" import { Cookie } from "@hoppscotch/data" import * as E from "fp-ts/Either" import { Store } from "~/kernel/store" +import type { SetCookieValues } from "set-cookie-parser-es" // Cookies are per-organization state, so they persist through the // org-scoped `Store` (`~/kernel/store`), which resolves to @@ -441,6 +442,32 @@ export class CookieJarService extends Service { return stripped.toLowerCase() } + // Whether a response from `host` may set a cookie for `domain`. + // RFC 6265 5.3 step 5 treats an IP-literal host as an address + // rather than as a label chain, so only an exact match is allowed + // there. Running `domainMatches` on `192.168.1.1` would accept + // `Domain=1.1` as a parent, and the cookie would then attach to + // the unrelated address `10.0.1.1`, which shares the suffix and + // nothing else. Everything else is the 5.1.3 comparison, so a + // subdomain setting its parent domain still passes. + private hostAcceptsDomain(host: string, domain: string): boolean { + if (this.isIPLiteral(host)) { + return host === domain + } + return this.domainMatches(host, domain) + } + + // `URL.hostname` renders an IPv6 address bracketed, so the two + // forms are recognized separately. An IPv4 check on the four + // dotted fields is enough here, since a host that reached this + // point already parsed as a URL. + private isIPLiteral(host: string): boolean { + if (host.startsWith("[") && host.endsWith("]")) { + return true + } + return /^\d{1,3}(\.\d{1,3}){3}$/.test(host) + } + // Canonicalizes a cookie domain that came from a Set-Cookie // `Domain` attribute. Calls `canonStoreDomain` then rejects a // single-label domain that would let the cookie attach to every @@ -496,6 +523,21 @@ export class CookieJarService extends Service { let domain: string | null if (c.domain) { domain = this.canonAttrDomain(c.domain) + // RFC 6265 5.3 step 6 rejects a `Domain` attribute the + // request host does not domain-match. Without it a response + // from one host writes a cookie stored under another, and + // the next request to that other host attaches it, which is + // a cross-origin write for every capture path that reaches + // here. `api.example.com` setting `Domain=example.com` still + // passes, since `domainMatches` is the same 5.1.3 comparison + // the read side uses. + if (domain !== null && !this.hostAcceptsDomain(requestHost, domain)) { + console.warn( + "[CookieJar] Dropped cookie with a cross-domain Domain:", + c.domain + ) + continue + } } else { domain = this.canonHostOnly(requestHost) } @@ -822,10 +864,97 @@ export class CookieJarService extends Service { request.headers["Cookie"] = serialized } + // Maps the `set-cookie-parser-es` SameSite union (lowercase or a + // bare boolean) onto the `@hoppscotch/data` casing + // `extractFromResponse` expects. An unrecognized or boolean value + // yields undefined so the extractor applies its "Lax" default. + private normalizeSameSite( + raw: SetCookieValues["sameSite"] + ): ResponseCookie["sameSite"] { + if (typeof raw !== "string") { + return undefined + } + switch (raw.toLowerCase()) { + case "strict": + return "Strict" + case "lax": + return "Lax" + case "none": + return "None" + default: + return undefined + } + } + + // Parses raw Set-Cookie header strings into the response-cookie + // objects `extractFromResponse` canonicalizes. The agent relay joins + // multiple Set-Cookie headers with newlines (see the agent + // interceptor's multiHeaders split), so each line is parsed on its + // own. Lines the parser cannot resolve to a name are dropped rather + // than stored as `undefined=...`. Returns undefined when there is no + // Set-Cookie header to read, empty value included, and an array + // otherwise. `decodeValues` is off so a percent-encoded value is + // stored exactly as the relay's structured path stores it, since a + // decoded value would be re-emitted unencoded by + // `serializeCookieHeader`. + private cookiesFromSetCookieHeader( + headers: Record | undefined + ): ResponseCookie[] | undefined { + if (!headers) { + return undefined + } + const key = Object.keys(headers).find( + (h) => h.toLowerCase() === "set-cookie" + ) + if (key === undefined) { + return undefined + } + const raw = headers[key] + if (!raw) { + return undefined + } + const cookies: ResponseCookie[] = [] + for (const line of raw + .split("\n") + .map((s) => s.trim()) + .filter(Boolean)) { + const parsed = setCookieParse(line, { decodeValues: false }) + if (!parsed.name) { + continue + } + cookies.push({ + name: parsed.name, + value: parsed.value, + domain: parsed.domain, + path: parsed.path, + // RFC 6265 4.1.2.2, Max-Age takes precedence over Expires, and + // the structured relay path has already resolved it into an + // expiry by the time cookies arrive that way. Converting here + // keeps a `Max-Age` cookie from being stored as a session + // cookie, and lets `Max-Age=0` expire an entry on capture. + // A non-positive age resolves one millisecond behind the + // capture instant rather than onto it, since `pruneExpired` + // and the read filter both keep an entry while its expiry + // equals the current time, which would carry a deleted + // cookie into a read taken in that same millisecond. + expires: + parsed.maxAge !== undefined && Number.isFinite(parsed.maxAge) + ? new Date( + Date.now() + (parsed.maxAge > 0 ? parsed.maxAge * 1000 : -1) + ) + : parsed.expires, + secure: parsed.secure, + httpOnly: parsed.httpOnly, + sameSite: this.normalizeSameSite(parsed.sameSite), + }) + } + return cookies + } + // The one shared receive path. Captures structured cookies the // relay parsed out of the response into the jar. public async captureResponseCookies( - response: { cookies?: ResponseCookie[] }, + response: { cookies?: ResponseCookie[]; headers?: Record }, requestUrl: string | undefined ): Promise { if (!requestUrl) { @@ -835,6 +964,21 @@ export class CookieJarService extends Service { if (url === null) { return } + // Agent binaries built against a relay revision without + // `parse_cookies` return no structured `cookies` while the + // Set-Cookie headers are still present in `headers`. Parsing the + // header string in that case keeps the jar working on older agent + // builds. The fallback runs only when `cookies` is absent, since a + // present array (even empty) is the relay's own answer that the + // response had no cookies, and re-parsing headers would risk + // double-counting. + if (response.cookies === undefined) { + await this.extractFromResponse( + this.cookiesFromSetCookieHeader(response.headers), + url + ) + return + } await this.extractFromResponse(response.cookies, url) } } From 7175b49bb0b6dd41fbdcc3c5c8a39f3251ca60b4 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Tue, 25 Aug 2026 17:55:05 +0530 Subject: [PATCH 04/14] fix(common): distinguish host-only cookies (#6585) --- packages/hoppscotch-common/locales/en.json | 2 + .../src/components/cookies/AllModal.vue | 12 +++ .../__tests__/cookie-jar.service.spec.ts | 83 +++++++++++++++++++ .../src/services/cookie-jar.service.ts | 69 ++++++++++++--- .../src/types/post-request.d.ts | 1 + .../src/types/pre-request.d.ts | 1 + packages/hoppscotch-data/src/cookies.ts | 5 ++ 7 files changed, 163 insertions(+), 10 deletions(-) diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index ad5439d0f81..b928b959dd7 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -395,6 +395,8 @@ "empty_domains": "Domain list is empty", "invalid_domain": "Domain has invalid characters", "enter_cookie_string": "Enter cookie string", + "host_only": "HostOnly", + "host_only_info": "Sent to the host that set it. Subdomains do not receive it.", "http_only": "HttpOnly", "http_only_info": "Not exposed to page scripts. Still sent on matching requests.", "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", diff --git a/packages/hoppscotch-common/src/components/cookies/AllModal.vue b/packages/hoppscotch-common/src/components/cookies/AllModal.vue index 22de51691c7..522dd52dd62 100644 --- a/packages/hoppscotch-common/src/components/cookies/AllModal.vue +++ b/packages/hoppscotch-common/src/components/cookies/AllModal.vue @@ -89,6 +89,14 @@ :value="`${entry.name} => ${entry.value}`" readonly /> + + {{ t("cookies.modal.host_only") }} + { expect(stored).toHaveLength(1) expect(stored?.[0].value).toBe("new") }) + + it("drops a non-boolean hostOnly instead of persisting it", async () => { + await service.upsertCookies([ + cookie({ + name: "a", + value: "1", + hostOnly: "true" as unknown as boolean, + }), + ]) + const stored = service.cookieJar.value.get("example.com")?.[0] + expect(stored?.hostOnly).toBeUndefined() + }) }) describe("deleteCookies", () => { @@ -577,4 +589,75 @@ describe("CookieJarService", () => { ).toBe("b=2") }) }) + + describe("host-only cookies (FE-1284)", () => { + it("marks a Set-Cookie with no Domain attribute as host-only", async () => { + await service.extractFromResponse( + [{ name: "a", value: "1" }], + new URL("https://example.com/") + ) + expect(service.cookieJar.value.get("example.com")?.[0].hostOnly).toBe(true) + }) + + it("does not mark a Domain-scoped Set-Cookie as host-only", async () => { + await service.extractFromResponse( + [{ name: "a", value: "1", domain: "example.com" }], + new URL("https://example.com/") + ) + expect(service.cookieJar.value.get("example.com")?.[0].hostOnly).toBe( + false + ) + }) + + it("sends a host-only cookie to its exact host", async () => { + await service.extractFromResponse( + [{ name: "a", value: "1" }], + new URL("https://example.com/") + ) + expect( + service.getCookiesForURL(new URL("https://example.com/")) + ).toHaveLength(1) + }) + + it("does not send a host-only cookie to a subdomain of its host", async () => { + await service.extractFromResponse( + [{ name: "a", value: "1" }], + new URL("https://example.com/") + ) + expect( + service.getCookiesForURL(new URL("https://api.example.com/")) + ).toHaveLength(0) + }) + + it("does not duplicate a cookie name across a host-only and a Domain-scoped bucket on a child-host request", async () => { + // Host-only cookie set at the parent host (no Domain attribute). + await service.extractFromResponse( + [{ name: "sid", value: "host" }], + new URL("https://example.com/") + ) + // Same name, Domain-scoped to the child host it legitimately applies to. + await service.extractFromResponse( + [{ name: "sid", value: "child", domain: "api.example.com" }], + new URL("https://api.example.com/") + ) + const cookies = service.getCookiesForURL( + new URL("https://api.example.com/") + ) + expect(cookies).toHaveLength(1) + expect(cookies[0].value).toBe("child") + }) + + it("treats a legacy persisted cookie with no hostOnly flag as non-host-only", () => { + const map = (service as any).toMap({ + "example.com": [cookie({ name: "a", value: "1" })], + }) + service.cookieJar.value = map + // Backward compatibility, the pre-flag entry still matches the + // subdomains it matched before the upgrade. + expect(map.get("example.com")?.[0].hostOnly).toBe(false) + expect( + service.getCookiesForURL(new URL("https://api.example.com/")) + ).toHaveLength(1) + }) + }) }) diff --git a/packages/hoppscotch-common/src/services/cookie-jar.service.ts b/packages/hoppscotch-common/src/services/cookie-jar.service.ts index a073bc18d2d..8b4207d789e 100644 --- a/packages/hoppscotch-common/src/services/cookie-jar.service.ts +++ b/packages/hoppscotch-common/src/services/cookie-jar.service.ts @@ -234,13 +234,19 @@ export class CookieJarService extends Service { typeof (c as { value?: unknown }).value !== "string" || typeof (c as { domain?: unknown }).domain !== "string" || typeof (c as { path?: unknown }).path !== "string" || - typeof (c as { secure?: unknown }).secure !== "boolean" + typeof (c as { secure?: unknown }).secure !== "boolean" || + ((c as { hostOnly?: unknown }).hostOnly !== undefined && + typeof (c as { hostOnly?: unknown }).hostOnly !== "boolean") ) { // `path` is what `pathMatches` reads to decide whether // a cookie applies, `secure` is what gates the HTTPS-only // attach in `applyCookiesToRequest`, so a schema-drifted // payload that smuggled a string `"false"` past either // would silently mismatch path scope or attach over HTTP. + // `hostOnly` is checked only when present, because absent + // is the pre-flag jar that `toMap` migrates. A present but + // non-boolean value would read as false there and widen a + // host-only cookie to every subdomain. throw new Error("payload has malformed cookie") } } @@ -277,6 +283,13 @@ export class CookieJarService extends Service { const canonized: Cookie = { ...c, domain: this.canonStoreDomain(c.domain ?? key) || key, + // A jar persisted before the host-only flag existed has no + // `hostOnly` on its entries. Treating those as non-host-only + // keeps the subdomain matching they had before the + // upgrade, so an existing cookie whose bucket key is a parent + // domain keeps applying to child hosts as before. An entry + // that already has the flag keeps its value. + hostOnly: typeof c.hostOnly === "boolean" ? c.hostOnly : false, } // NUL separator matches the `cookieKey` pattern in // `RequestRunner.ts`. Empty-string path collapses to @@ -401,6 +414,13 @@ export class CookieJarService extends Service { secure: typeof cookie.secure === "boolean" ? cookie.secure : false, httpOnly: typeof cookie.httpOnly === "boolean" ? cookie.httpOnly : false, + // `parseStored` rejects a present non-boolean `hostOnly` and a + // rejected payload fails the whole load, so a script-set value + // of the wrong type is dropped at the write instead of + // persisted. Absent stays absent, which `toMap` reads as the + // pre-flag jar and migrates. + hostOnly: + typeof cookie.hostOnly === "boolean" ? cookie.hostOnly : undefined, } const existing = this.cookieJar.value.get(normalized.domain) ?? [] @@ -454,7 +474,11 @@ export class CookieJarService extends Service { if (this.isIPLiteral(host)) { return host === domain } - return this.domainMatches(host, domain) + // `hostOnly` is false because this runs only where the response + // sent a `Domain` attribute, and the host-only flag describes a + // response that sent none. A capture without the attribute takes + // the request host as its domain and never reaches here. + return this.domainMatches(host, domain, false) } // `URL.hostname` renders an IPv6 address bracketed, so the two @@ -507,8 +531,9 @@ export class CookieJarService extends Service { // Normalizes the kernel relay response cookies into the // `@hoppscotch/data` shape and merges them. Domain falls back to the - // request host (host-only cookie), path to "/", the flags default - // off, and SameSite to "Lax" matching the browser default. + // request host with the host-only flag set, path to "/", the + // httpOnly/secure flags default off, and SameSite to "Lax" matching + // the browser default. public async extractFromResponse( cookies: ResponseCookie[] | undefined, requestURL: URL @@ -521,6 +546,13 @@ export class CookieJarService extends Service { const normalized: Cookie[] = [] for (const c of cookies) { let domain: string | null + // A Set-Cookie without a Domain attribute is host-only per RFC 6265 + // 5.3, so it applies to the request host alone. The flag is what + // `domainMatches` reads to enforce that, so a + // host-only entry stored under a parent host cannot also apply to a + // child-host request where a Domain-scoped cookie of the same name + // already applies. + const hostOnly = !c.domain if (c.domain) { domain = this.canonAttrDomain(c.domain) // RFC 6265 5.3 step 6 rejects a `Domain` attribute the @@ -574,6 +606,7 @@ export class CookieJarService extends Service { httpOnly: c.httpOnly ?? false, secure: c.secure ?? false, sameSite: c.sameSite ?? "Lax", + hostOnly, ...(expires !== undefined ? { expires } : {}), }) } @@ -679,9 +712,19 @@ export class CookieJarService extends Service { // bare `hostname.endsWith(domain)`, which let `evil-example.com` // match `example.com` because there was no label boundary. Hosts // are lowercased here, stored domains were lowercased on capture, - // so the comparison is case-insensitive per RFC 6265 5.1.2. - private domainMatches(host: string, domain: string): boolean { + // so the comparison is case-insensitive per RFC 6265 5.1.2. A + // host-only cookie (RFC 6265 5.4 step 1.1, no Domain attribute on + // capture) requires exact host equality so it never applies to a + // subdomain of the host that set it. + private domainMatches( + host: string, + domain: string, + hostOnly: boolean + ): boolean { const h = host.toLowerCase() + if (hostOnly) { + return h === domain + } return h === domain || h.endsWith(`.${domain}`) } @@ -704,11 +747,17 @@ export class CookieJarService extends Service { const result: Cookie[] = [] for (const [domain, cookies] of this.cookieJar.value.entries()) { - if (!this.domainMatches(url.hostname, domain)) { - continue - } - for (const cookie of cookies) { + // The domain check runs per cookie, not per bucket, because + // the host-only flag is a per-cookie property. A bucket can + // contain both a host-only entry and a Domain-scoped one, and + // only the latter applies to a subdomain request. + if ( + !this.domainMatches(url.hostname, domain, cookie.hostOnly ?? false) + ) { + continue + } + const passesPath = this.pathMatches(url.pathname, cookie.path || "/") const passesExpires = (() => { diff --git a/packages/hoppscotch-common/src/types/post-request.d.ts b/packages/hoppscotch-common/src/types/post-request.d.ts index 0660094a056..66707363183 100644 --- a/packages/hoppscotch-common/src/types/post-request.d.ts +++ b/packages/hoppscotch-common/src/types/post-request.d.ts @@ -55,6 +55,7 @@ interface Cookie { secure: boolean httpOnly: boolean sameSite: "None" | "Lax" | "Strict" + hostOnly?: boolean } type AuthLocation = "HEADERS" | "QUERY_PARAMS" diff --git a/packages/hoppscotch-common/src/types/pre-request.d.ts b/packages/hoppscotch-common/src/types/pre-request.d.ts index 224bb15094d..fedbf85467e 100644 --- a/packages/hoppscotch-common/src/types/pre-request.d.ts +++ b/packages/hoppscotch-common/src/types/pre-request.d.ts @@ -50,6 +50,7 @@ interface Cookie { secure: boolean httpOnly: boolean sameSite: "None" | "Lax" | "Strict" + hostOnly?: boolean } type AuthLocation = "HEADERS" | "QUERY_PARAMS" diff --git a/packages/hoppscotch-data/src/cookies.ts b/packages/hoppscotch-data/src/cookies.ts index d6a2cb78bdf..82fa84f5b65 100644 --- a/packages/hoppscotch-data/src/cookies.ts +++ b/packages/hoppscotch-data/src/cookies.ts @@ -11,6 +11,11 @@ export const CookieSchema = z.object({ httpOnly: z.boolean(), // Whether cookie is HTTP-only (not accessible via JavaScript) secure: z.boolean(), // Whether cookie should only be sent over HTTPS sameSite: z.enum(["None", "Lax", "Strict"]), // SameSite attribute for CSRF protection + // RFC 6265 5.3 host-only-flag. True when the Set-Cookie carried no Domain + // attribute, so the cookie applies only to the exact request host and never + // to its subdomains. Optional and defaulted absent for backward compatibility + // with jars persisted before the flag existed. + hostOnly: z.boolean().optional(), }) export type Cookie = z.infer From e9ebf0854998ddc781acbd31128f64ea5afc9083 Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:13:50 +0530 Subject: [PATCH 05/14] chore: formatting updates --- .../src/services/__tests__/cookie-jar.service.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts b/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts index a1808597702..3eff048cc02 100644 --- a/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts +++ b/packages/hoppscotch-common/src/services/__tests__/cookie-jar.service.spec.ts @@ -596,7 +596,9 @@ describe("CookieJarService", () => { [{ name: "a", value: "1" }], new URL("https://example.com/") ) - expect(service.cookieJar.value.get("example.com")?.[0].hostOnly).toBe(true) + expect(service.cookieJar.value.get("example.com")?.[0].hostOnly).toBe( + true + ) }) it("does not mark a Domain-scoped Set-Cookie as host-only", async () => { From 6925ba5878bfb5b045c6ca81f72e387634206aea Mon Sep 17 00:00:00 2001 From: Mir Arif Hasan Date: Wed, 26 Aug 2026 18:21:34 +0600 Subject: [PATCH 06/14] chore: security patch for the dependency chain `v2026.8.0` (#6604) Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com> --- package.json | 20 +- packages/codemirror-lang-graphql/package.json | 2 +- packages/hoppscotch-agent/package.json | 12 +- packages/hoppscotch-backend/package.json | 36 +- packages/hoppscotch-cli/package.json | 8 +- packages/hoppscotch-common/package.json | 44 +- packages/hoppscotch-data/package.json | 4 +- packages/hoppscotch-desktop/package.json | 24 +- packages/hoppscotch-js-sandbox/package.json | 8 +- packages/hoppscotch-kernel/package.json | 2 +- packages/hoppscotch-selfhost-web/package.json | 18 +- packages/hoppscotch-sh-admin/package.json | 20 +- pnpm-lock.yaml | 4128 +++++++++-------- prod.Dockerfile | 55 +- 14 files changed, 2356 insertions(+), 2025 deletions(-) diff --git a/package.json b/package.json index 31d74b362f4..022cf7c2c62 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "author": "Hoppscotch (support@hoppscotch.io)", "private": true, "license": "MIT", - "packageManager": "pnpm@10.34.2", + "packageManager": "pnpm@10.34.5", "scripts": { "preinstall": "npx only-allow pnpm", "prepare": "husky", @@ -35,25 +35,31 @@ }, "pnpm": { "overrides": { + "@protobufjs/utf8@<=1.1.0": "1.1.2", "@xmldom/xmldom": "0.8.13", "apiconnect-wsdl": "2.0.36", - "body-parser": "2.2.1", - "brace-expansion@<=5.0.7": "5.0.8", + "body-parser": "2.3.0", + "brace-expansion@<5.0.9": "5.0.9", "cross-spawn": "7.0.6", + "deepmerge-ts@<8.0.0": "8.0.1", "execa@<2.0.0": "2.0.0", - "fast-uri@3.1.2": "3.1.4", + "fast-uri@>=3.0.0 <3.1.5": "3.1.5", "find-my-way@9.6.0": "9.7.0", "form-data@>=4.0.0 <4.0.6": "4.0.6", - "js-yaml@>=4.0.0 <4.3.0": "4.3.0", + "js-yaml@>=4.0.0 <4.3.1": "4.3.1", "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", + "mjml@<5.0.0-alpha.9": "5.4.0", "nodemailer@<=9.0.0": "9.0.1", - "postcss@<=8.5.17": "8.5.18", + "postcss@<=8.5.22": "8.5.26", + "qs@>=6.11.1 <6.15.2": "6.15.3", "serialize-javascript@<7.0.7": "7.0.7", "svgo@>=4.0.0 <4.0.2": "4.0.2", - "vue": "3.5.40", + "uuid@9.0.1": "11.1.1", + "valibot@<=1.4.1": "1.4.2", + "vue": "3.5.41", "ws": "8.21.0" }, "onlyBuiltDependencies": [ diff --git a/packages/codemirror-lang-graphql/package.json b/packages/codemirror-lang-graphql/package.json index 6f144921ccf..c4439d314b4 100644 --- a/packages/codemirror-lang-graphql/package.json +++ b/packages/codemirror-lang-graphql/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@lezer/generator": "1.8.0", "@rollup/plugin-typescript": "12.1.4", - "mocha": "11.7.6", + "mocha": "11.8.0", "rollup": "4.59.0", "typescript": "5.9.3" } diff --git a/packages/hoppscotch-agent/package.json b/packages/hoppscotch-agent/package.json index f4c0e65d9d8..c07de4a8e2e 100644 --- a/packages/hoppscotch-agent/package.json +++ b/packages/hoppscotch-agent/package.json @@ -20,14 +20,14 @@ "@hoppscotch/ui": "0.2.6", "@tauri-apps/api": "2.1.1", "@tauri-apps/plugin-shell": "2.3.3", - "@vueuse/core": "14.3.0", - "axios": "1.18.1", + "@vueuse/core": "14.4.0", + "axios": "1.19.0", "fp-ts": "2.16.11", "lodash-es": "4.18.1", - "vue": "3.5.38" + "vue": "3.5.41" }, "devDependencies": { - "@iconify-json/lucide": "1.2.118", + "@iconify-json/lucide": "1.2.125", "@tauri-apps/cli": "2.9.3", "@types/lodash-es": "4.17.12", "@types/node": "24.10.1", @@ -39,9 +39,9 @@ "cross-env": "10.1.0", "eslint": "9.39.2", "eslint-plugin-prettier": "5.5.6", - "eslint-plugin-vue": "10.9.2", + "eslint-plugin-vue": "10.10.0", "globals": "16.5.0", - "postcss": "8.5.20", + "postcss": "8.5.26", "tailwindcss": "3.4.16", "typescript": "5.9.3", "unplugin-icons": "22.5.0", diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index 34ca930a154..ad8a7bacebd 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -34,21 +34,21 @@ "@apollo/server": "5.5.1", "@as-integrations/express5": "1.1.2", "@nestjs-modules/mailer": "2.3.7", - "@nestjs/apollo": "13.4.2", - "@nestjs/common": "11.1.28", + "@nestjs/apollo": "13.4.5", + "@nestjs/common": "11.2.1", "@nestjs/config": "4.0.4", - "@nestjs/core": "11.1.28", - "@nestjs/graphql": "13.4.2", + "@nestjs/core": "11.2.1", + "@nestjs/graphql": "13.4.5", "@nestjs/jwt": "11.0.2", "@nestjs/passport": "11.0.0", - "@nestjs/platform-express": "11.1.28", + "@nestjs/platform-express": "11.2.1", "@nestjs/schedule": "6.1.3", - "@nestjs/swagger": "11.4.6", + "@nestjs/swagger": "11.4.7", "@nestjs/terminus": "11.1.1", "@nestjs/throttler": "6.5.0", - "@prisma/adapter-pg": "7.9.0", - "@prisma/client": "7.9.0", - "argon2": "0.44.0", + "@prisma/adapter-pg": "7.9.1", + "@prisma/client": "7.9.1", + "argon2": "0.45.1", "bcrypt": "6.0.0", "class-transformer": "0.5.1", "class-validator": "0.15.1", @@ -64,16 +64,16 @@ "handlebars": "4.7.9", "io-ts": "2.2.22", "morgan": "1.11.0", - "nodemailer": "9.0.3", + "nodemailer": "9.0.5", "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.46.1", - "prisma": "7.9.0", + "pg": "8.23.0", + "posthog-node": "5.50.0", + "prisma": "7.9.1", "reflect-metadata": "0.2.2", "rimraf": "6.1.3", "rxjs": "7.8.2" @@ -83,7 +83,7 @@ "@eslint/js": "10.0.1", "@nestjs/cli": "11.0.24", "@nestjs/schematics": "11.1.0", - "@nestjs/testing": "11.1.28", + "@nestjs/testing": "11.2.1", "@relmify/jest-fp-ts": "2.1.1", "@types/bcrypt": "6.0.0", "@types/cookie-parser": "1.4.10", @@ -96,13 +96,13 @@ "@types/passport-jwt": "4.0.1", "@types/passport-microsoft": "2.1.1", "@types/supertest": "7.2.1", - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", "cross-env": "10.1.0", - "eslint": "10.7.0", + "eslint": "10.8.1", "eslint-config-prettier": "10.1.8", "eslint-plugin-prettier": "5.5.6", - "globals": "17.7.0", + "globals": "17.11.0", "jest": "30.4.2", "jest-mock-extended": "4.0.1", "prettier": "3.9.6", diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 3b996d35352..8bdb1faa3ca 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.1", + "axios": "1.19.0", "axios-cookiejar-support": "6.0.5", "chalk": "5.6.2", "commander": "14.0.3", @@ -50,7 +50,7 @@ "js-md5": "0.8.3", "jsonc-parser": "3.3.1", "lodash-es": "4.18.1", - "papaparse": "5.5.4", + "papaparse": "5.6.0", "qs": "6.15.3", "semver": "7.8.5", "tough-cookie": "6.0.2", @@ -66,9 +66,9 @@ "@types/papaparse": "5.5.2", "@types/qs": "6.15.1", "fp-ts": "2.16.11", - "prettier": "3.8.5", + "prettier": "3.9.6", "tsup": "8.5.1", "typescript": "5.9.3", - "vitest": "4.1.10" + "vitest": "4.1.11" } } diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index a725e7b0ed8..6ddcf203b2f 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -44,26 +44,26 @@ "@hoppscotch/ui": "0.2.6", "@hoppscotch/vue-toasted": "0.1.0", "@lezer/highlight": "1.2.1", - "@noble/curves": "2.2.0", - "@scure/base": "2.2.0", + "@noble/curves": "2.3.0", + "@scure/base": "2.3.0", "@shopify/lang-jsonc": "1.0.1", "@tauri-apps/api": "2.1.1", "@tauri-apps/plugin-store": "2.4.1", "@types/hawk": "9.0.7", - "@types/markdown-it": "14.1.2", + "@types/markdown-it": "14.2.0", "@types/node": "24.10.1", "@unhead/vue": "2.1.12", "@urql/core": "6.0.3", "@urql/devtools": "2.0.3", "@urql/exchange-auth": "3.0.0", - "@vueuse/core": "14.3.0", + "@vueuse/core": "14.4.0", "acorn-walk": "8.3.5", "aws4fetch": "1.0.20", - "axios": "1.18.1", + "axios": "1.19.0", "buffer": "6.0.3", "cookie-es": "2.0.0", "dioc": "3.0.2", - "dompurify": "3.4.12", + "dompurify": "3.4.14", "esprima": "4.0.1", "events": "3.3.0", "fp-ts": "2.16.11", @@ -72,23 +72,23 @@ "graphql-language-service-interface": "2.10.2", "graphql-tag": "2.12.7", "hawk": "9.0.2", - "highlight.js": "11.11.1", + "highlight.js": "11.12.0", "highlightjs-curl": "1.3.0", "insomnia-importers": "3.6.0", "io-ts": "2.2.22", "jq-wasm": "1.1.0-jq-1.8.1", "js-md5": "0.8.3", - "js-yaml": "4.2.0", + "js-yaml": "4.3.1", "jsonc-parser": "3.3.1", "lodash-es": "4.18.1", - "lossless-json": "4.3.0", + "lossless-json": "4.3.1", "markdown-it": "14.3.0", "minisearch": "7.2.0", "monaco-editor": "0.55.1", "nprogress": "0.2.0", "paho-mqtt": "1.1.0", "path": "0.12.7", - "postman-collection": "5.3.0", + "postman-collection": "5.3.1", "process": "0.11.10", "qs": "6.15.3", "quicktype-core": "23.2.6", @@ -111,8 +111,8 @@ "util": "0.12.5", "uuid": "13.0.0", "verzod": "0.4.0", - "vue": "3.5.38", - "vue-i18n": "11.4.6", + "vue": "3.5.41", + "vue-i18n": "11.4.8", "vue-json-pretty": "2.6.0", "vue-pdf-embed": "2.1.5", "vue-router": "4.6.4", @@ -137,9 +137,9 @@ "@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.118", + "@iconify-json/lucide": "1.2.125", "@import-meta-env/cli": "0.7.4", - "@intlify/unplugin-vue-i18n": "11.2.4", + "@intlify/unplugin-vue-i18n": "11.2.5", "@relmify/jest-fp-ts": "2.1.1", "@rushstack/eslint-patch": "1.16.1", "@types/har-format": "1.2.16", @@ -154,25 +154,25 @@ "@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/compiler-sfc": "3.5.41", "@vue/eslint-config-typescript": "14.9.0", - "@vue/runtime-core": "3.5.40", + "@vue/runtime-core": "3.5.41", "autoprefixer": "10.5.4", "cross-env": "10.1.0", "dotenv": "17.4.2", "eslint": "9.39.2", "eslint-plugin-prettier": "5.5.6", - "eslint-plugin-vue": "10.9.2", + "eslint-plugin-vue": "10.10.0", "glob": "13.0.6", "globals": "16.5.0", "jsdom": "27.4.0", "npm-run-all": "4.1.5", "openapi-types": "12.1.3", - "postcss": "8.5.20", - "prettier": "3.8.5", + "postcss": "8.5.26", + "prettier": "3.9.6", "prettier-plugin-tailwindcss": "0.7.2", "rollup-plugin-polyfill-node": "0.13.0", - "sass": "1.101.0", + "sass": "1.103.1", "tailwindcss": "3.4.16", "tsup": "8.5.1", "typescript": "5.9.3", @@ -185,9 +185,9 @@ "vite-plugin-html-config": "2.0.2", "vite-plugin-pages": "0.33.3", "vite-plugin-pages-sitemap": "1.7.1", - "vite-plugin-pwa": "1.2.0", + "vite-plugin-pwa": "1.3.0", "vite-plugin-vue-layouts": "0.11.0", - "vitest": "4.1.10", + "vitest": "4.1.11", "vue-tsc": "1.8.8" } } diff --git a/packages/hoppscotch-data/package.json b/packages/hoppscotch-data/package.json index 81425cd6e0c..68a254a8bf0 100644 --- a/packages/hoppscotch-data/package.json +++ b/packages/hoppscotch-data/package.json @@ -35,14 +35,14 @@ }, "homepage": "https://github.com/hoppscotch/hoppscotch#readme", "devDependencies": { - "@types/lodash": "4.17.24", + "@types/lodash": "4.17.25", "typescript": "5.9.3", "vite": "7.3.2" }, "dependencies": { "fp-ts": "2.16.11", "io-ts": "2.2.22", - "jose": "6.2.3", + "jose": "6.2.10", "lodash": "4.18.1", "parser-ts": "0.7.0", "uuid": "13.0.0", diff --git a/packages/hoppscotch-desktop/package.json b/packages/hoppscotch-desktop/package.json index 95d9d78d80b..41fba6b02d2 100644 --- a/packages/hoppscotch-desktop/package.json +++ b/packages/hoppscotch-desktop/package.json @@ -25,9 +25,9 @@ "do-test": "pnpm run test" }, "dependencies": { - "@fontsource-variable/inter": "5.2.8", - "@fontsource-variable/material-symbols-rounded": "5.2.45", - "@fontsource-variable/roboto-mono": "5.2.9", + "@fontsource-variable/inter": "5.3.0", + "@fontsource-variable/material-symbols-rounded": "5.3.3", + "@fontsource-variable/roboto-mono": "5.3.0", "@hoppscotch/common": "workspace:^", "@hoppscotch/kernel": "workspace:^", "@hoppscotch/plugin-appload": "github:CuriousCorrelation/tauri-plugin-appload#7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33", @@ -40,29 +40,29 @@ "@tauri-apps/plugin-updater": "2.9.0", "fp-ts": "2.16.11", "rxjs": "7.8.2", - "vue": "3.5.38", + "vue": "3.5.41", "vue-router": "4.6.4", "vue-tippy": "6.7.1", "zod": "3.25.32" }, "devDependencies": { - "@eslint/eslintrc": "3.3.5", + "@eslint/eslintrc": "3.3.6", "@eslint/js": "9.39.2", - "@iconify-json/lucide": "1.2.114", + "@iconify-json/lucide": "1.2.125", "@rushstack/eslint-patch": "1.16.1", "@tauri-apps/cli": "2.9.3", "@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", + "@vitejs/plugin-vue": "6.0.8", + "@vue/eslint-config-typescript": "14.9.0", + "autoprefixer": "10.5.4", "eslint": "9.39.2", "eslint-plugin-prettier": "5.5.6", - "eslint-plugin-vue": "10.9.2", + "eslint-plugin-vue": "10.10.0", "globals": "16.5.0", "jsdom": "27.4.0", - "postcss": "8.5.15", - "sass": "1.101.0", + "postcss": "8.5.26", + "sass": "1.103.1", "tailwindcss": "3.4.16", "typescript": "5.9.3", "unplugin-icons": "22.5.0", diff --git a/packages/hoppscotch-js-sandbox/package.json b/packages/hoppscotch-js-sandbox/package.json index 9ea458be88b..321d2799200 100644 --- a/packages/hoppscotch-js-sandbox/package.json +++ b/packages/hoppscotch-js-sandbox/package.json @@ -57,7 +57,7 @@ "dependencies": { "@hoppscotch/data": "workspace:^", "@types/lodash-es": "4.17.12", - "acorn": "8.17.0", + "acorn": "8.18.0", "chai": "6.2.2", "faraday-cage": "0.1.0", "fp-ts": "2.16.11", @@ -71,7 +71,7 @@ "@relmify/jest-fp-ts": "2.1.1", "@types/chai": "5.2.3", "@types/jest": "30.0.0", - "@types/lodash": "4.17.24", + "@types/lodash": "4.17.25", "@types/node": "24.10.1", "@typescript-eslint/eslint-plugin": "8.64.0", "@typescript-eslint/parser": "8.64.0", @@ -80,10 +80,10 @@ "eslint-plugin-prettier": "5.5.6", "globals": "16.5.0", "io-ts": "2.2.22", - "prettier": "3.8.5", + "prettier": "3.9.6", "typescript": "5.9.3", "vite": "7.3.2", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "peerDependencies": { "isolated-vm": "6.1.2" diff --git a/packages/hoppscotch-kernel/package.json b/packages/hoppscotch-kernel/package.json index 5e58d9ac423..7e66e1ae17e 100644 --- a/packages/hoppscotch-kernel/package.json +++ b/packages/hoppscotch-kernel/package.json @@ -64,7 +64,7 @@ "@tauri-apps/plugin-shell": "2.3.3", "@tauri-apps/plugin-store": "2.4.1", "aws4fetch": "1.0.20", - "axios": "1.18.1", + "axios": "1.19.0", "fp-ts": "2.16.11", "superjson": "2.2.6", "zod": "3.25.32" diff --git a/packages/hoppscotch-selfhost-web/package.json b/packages/hoppscotch-selfhost-web/package.json index e77c04b1bc6..432613ca9a5 100644 --- a/packages/hoppscotch-selfhost-web/package.json +++ b/packages/hoppscotch-selfhost-web/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@fontsource-variable/inter": "5.3.0", - "@fontsource-variable/material-symbols-rounded": "5.3.0", + "@fontsource-variable/material-symbols-rounded": "5.3.3", "@fontsource-variable/roboto-mono": "5.3.0", "@hoppscotch/common": "workspace:^", "@hoppscotch/data": "workspace:^", @@ -36,8 +36,8 @@ "@tauri-apps/plugin-dialog": "2.0.1", "@tauri-apps/plugin-fs": "2.0.2", "@tauri-apps/plugin-shell": "2.3.3", - "@vueuse/core": "14.3.0", - "axios": "1.18.1", + "@vueuse/core": "14.4.0", + "axios": "1.19.0", "buffer": "6.0.3", "dioc": "3.0.2", "fp-ts": "2.16.11", @@ -46,7 +46,7 @@ "stream-browserify": "3.0.0", "util": "0.12.5", "verzod": "0.4.0", - "vue": "3.5.38", + "vue": "3.5.41", "workbox-window": "7.4.1", "zod": "3.25.32" }, @@ -61,8 +61,8 @@ "@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.118", - "@intlify/unplugin-vue-i18n": "11.2.4", + "@iconify-json/lucide": "1.2.125", + "@intlify/unplugin-vue-i18n": "11.2.5", "@rushstack/eslint-patch": "1.16.1", "@typescript-eslint/eslint-plugin": "8.64.0", "@typescript-eslint/parser": "8.64.0", @@ -74,10 +74,10 @@ "dotenv": "17.4.2", "eslint": "9.39.2", "eslint-plugin-prettier": "5.5.6", - "eslint-plugin-vue": "10.9.2", + "eslint-plugin-vue": "10.10.0", "globals": "16.5.0", "npm-run-all": "4.1.5", - "postcss": "8.5.20", + "postcss": "8.5.26", "prettier-plugin-tailwindcss": "0.7.2", "tailwindcss": "3.4.16", "typescript": "5.9.3", @@ -90,7 +90,7 @@ "vite-plugin-inspect": "11.4.1", "vite-plugin-pages": "0.33.3", "vite-plugin-pages-sitemap": "1.7.1", - "vite-plugin-pwa": "1.2.0", + "vite-plugin-pwa": "1.3.0", "vite-plugin-static-copy": "3.3.0", "vite-plugin-vue-layouts": "0.11.0", "vue-tsc": "2.1.6" diff --git a/packages/hoppscotch-sh-admin/package.json b/packages/hoppscotch-sh-admin/package.json index 258423ddaf8..b18c66ec16e 100644 --- a/packages/hoppscotch-sh-admin/package.json +++ b/packages/hoppscotch-sh-admin/package.json @@ -14,32 +14,32 @@ }, "dependencies": { "@fontsource-variable/inter": "5.3.0", - "@fontsource-variable/material-symbols-rounded": "5.3.0", + "@fontsource-variable/material-symbols-rounded": "5.3.3", "@fontsource-variable/roboto-mono": "5.3.0", "@graphql-typed-document-node/core": "3.2.0", "@hoppscotch/ui": "0.2.6", "@hoppscotch/vue-toasted": "0.1.0", - "@intlify/unplugin-vue-i18n": "11.2.4", + "@intlify/unplugin-vue-i18n": "11.2.5", "@types/cors": "2.8.19", "@urql/exchange-auth": "3.0.0", "@urql/vue": "2.1.1", - "@vueuse/core": "14.3.0", - "axios": "1.18.1", + "@vueuse/core": "14.4.0", + "axios": "1.19.0", "cors": "2.8.6", "date-fns": "4.4.0", "fp-ts": "2.16.11", "graphql": "16.13.2", "io-ts": "2.2.22", "lodash-es": "4.18.1", - "postcss": "8.5.20", + "postcss": "8.5.26", "prettier-plugin-tailwindcss": "0.7.1", "rxjs": "7.8.2", "tailwindcss": "3.4.16", "tippy.js": "6.3.7", "ts-node-dev": "2.0.0", "unplugin-vue-components": "30.0.0", - "vue": "3.5.38", - "vue-i18n": "11.4.6", + "vue": "3.5.41", + "vue-i18n": "11.4.8", "vue-router": "4.6.4", "vue-tippy": "6.7.1" }, @@ -52,18 +52,18 @@ "@graphql-codegen/typescript-document-nodes": "5.0.10", "@graphql-codegen/typescript-operations": "5.1.0", "@graphql-codegen/urql-introspection": "3.0.1", - "@iconify-json/lucide": "1.2.118", + "@iconify-json/lucide": "1.2.125", "@import-meta-env/cli": "0.7.4", "@import-meta-env/unplugin": "0.6.3", "@types/lodash-es": "4.17.12", "@vitejs/plugin-vue": "6.0.8", - "@vue/compiler-sfc": "3.5.40", + "@vue/compiler-sfc": "3.5.41", "autoprefixer": "10.5.4", "dotenv": "17.4.2", "graphql-tag": "2.12.7", "hoppscotch-backend": "workspace:^", "npm-run-all": "4.1.5", - "sass": "1.101.0", + "sass": "1.103.1", "ts-node": "10.9.2", "typescript": "5.9.3", "unplugin-fonts": "1.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72966c4b048..32760ebc287 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,25 +5,31 @@ settings: excludeLinksFromLockfile: false overrides: + '@protobufjs/utf8@<=1.1.0': 1.1.2 '@xmldom/xmldom': 0.8.13 apiconnect-wsdl: 2.0.36 - body-parser: 2.2.1 - brace-expansion@<=5.0.7: 5.0.8 + body-parser: 2.3.0 + brace-expansion@<5.0.9: 5.0.9 cross-spawn: 7.0.6 + deepmerge-ts@<8.0.0: 8.0.1 execa@<2.0.0: 2.0.0 - fast-uri@3.1.2: 3.1.4 + fast-uri@>=3.0.0 <3.1.5: 3.1.5 find-my-way@9.6.0: 9.7.0 form-data@>=4.0.0 <4.0.6: 4.0.6 - js-yaml@>=4.0.0 <4.3.0: 4.3.0 + js-yaml@>=4.0.0 <4.3.1: 4.3.1 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 + mjml@<5.0.0-alpha.9: 5.4.0 nodemailer@<=9.0.0: 9.0.1 - postcss@<=8.5.17: 8.5.18 + postcss@<=8.5.22: 8.5.26 + qs@>=6.11.1 <6.15.2: 6.15.3 serialize-javascript@<7.0.7: 7.0.7 svgo@>=4.0.0 <4.0.2: 4.0.2 - vue: 3.5.40 + uuid@9.0.1: 11.1.1 + valibot@<=1.4.1: 1.4.2 + vue: 3.5.41 ws: 8.21.0 importers: @@ -38,7 +44,7 @@ importers: version: 20.5.0 '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@10.7.0(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@10.8.1(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@types/node': specifier: 24.10.1 version: 24.10.1 @@ -74,8 +80,8 @@ importers: specifier: 12.1.4 version: 12.1.4(rollup@4.59.0)(tslib@2.8.1)(typescript@5.9.3) mocha: - specifier: 11.7.6 - version: 11.7.6 + specifier: 11.8.0 + version: 11.8.0 rollup: specifier: 4.59.0 version: 4.59.0 @@ -87,7 +93,7 @@ importers: dependencies: '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@tauri-apps/api': specifier: 2.1.1 version: 2.1.1 @@ -95,11 +101,11 @@ importers: specifier: 2.3.3 version: 2.3.3 '@vueuse/core': - specifier: 14.3.0 - version: 14.3.0(vue@3.5.40(typescript@5.9.3)) + specifier: 14.4.0 + version: 14.4.0(vue@3.5.41(typescript@5.9.3)) axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 fp-ts: specifier: 2.16.11 version: 2.16.11 @@ -107,12 +113,12 @@ importers: specifier: 4.18.1 version: 4.18.1 vue: - specifier: 3.5.40 - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.41 + version: 3.5.41(typescript@5.9.3) devDependencies: '@iconify-json/lucide': - specifier: 1.2.118 - version: 1.2.118 + specifier: 1.2.125 + version: 1.2.125 '@tauri-apps/cli': specifier: 2.9.3 version: 2.9.3 @@ -130,13 +136,13 @@ importers: version: 8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': specifier: 6.0.8 - version: 6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@vue/eslint-config-typescript': specifier: 14.9.0 - version: 14.9.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) autoprefixer: specifier: 10.5.4 - version: 10.5.4(postcss@8.5.20) + version: 10.5.4(postcss@8.5.26) cross-env: specifier: 10.1.0 version: 10.1.0 @@ -147,14 +153,14 @@ importers: specifier: 5.5.6 version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-vue: - specifier: 10.9.2 - version: 10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + specifier: 10.10.0 + version: 10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) globals: specifier: 16.5.0 version: 16.5.0 postcss: - specifier: 8.5.20 - version: 8.5.20 + specifier: 8.5.26 + version: 8.5.26 tailwindcss: specifier: 3.4.16 version: 3.4.16(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) @@ -163,13 +169,13 @@ importers: version: 5.9.3 unplugin-icons: specifier: 22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16) + version: 22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16) unplugin-vue-components: specifier: 30.0.0 - version: 30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)) + version: 30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)) vite: specifier: 7.3.2 - version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vue-tsc: specifier: 2.2.0 version: 2.2.0(typescript@5.9.3) @@ -184,52 +190,52 @@ importers: version: 1.1.2(@apollo/server@5.5.1(graphql@16.14.0))(express@5.2.1) '@nestjs-modules/mailer': specifier: 2.3.7 - version: 2.3.7(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/terminus@11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2))(chokidar@4.0.3)(nodemailer@9.0.3)(terser@5.46.1)(typescript@5.9.3) + version: 2.3.7(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/terminus@11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2))(chokidar@4.0.3)(nodemailer@9.0.5)(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) '@nestjs/apollo': - specifier: 13.4.2 - version: 13.4.2(@apollo/server@5.5.1(graphql@16.14.0))(@as-integrations/express5@1.1.2(@apollo/server@5.5.1(graphql@16.14.0))(express@5.2.1))(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/graphql@13.4.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2))(graphql@16.14.0) + specifier: 13.4.5 + version: 13.4.5(@apollo/server@5.5.1(graphql@16.14.0))(@as-integrations/express5@1.1.2(@apollo/server@5.5.1(graphql@16.14.0))(express@5.2.1))(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/graphql@13.4.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2))(graphql@16.14.0) '@nestjs/common': - specifier: 11.1.28 - version: 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.2.1 + version: 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/config': specifier: 4.0.4 - version: 4.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + version: 4.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) '@nestjs/core': - specifier: 11.1.28 - version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/graphql': - specifier: 13.4.2 - version: 13.4.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2) + specifier: 13.4.5 + version: 13.4.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2) '@nestjs/jwt': specifier: 11.0.2 - version: 11.0.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + version: 11.0.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/passport': specifier: 11.0.0 - version: 11.0.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) + version: 11.0.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) '@nestjs/platform-express': - specifier: 11.1.28 - version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1) '@nestjs/schedule': specifier: 6.1.3 - version: 6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + version: 6.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1) '@nestjs/swagger': - specifier: 11.4.6 - version: 11.4.6(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + specifier: 11.4.7 + version: 11.4.7(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 11.1.1 - version: 11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/throttler': specifier: 6.5.0 - version: 6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(reflect-metadata@0.2.2) + version: 6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2) '@prisma/adapter-pg': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.9.1 + version: 7.9.1 '@prisma/client': - specifier: 7.9.0 - version: 7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) + specifier: 7.9.1 + version: 7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) argon2: - specifier: 0.44.0 - version: 0.44.0 + specifier: 0.45.1 + version: 0.45.1 bcrypt: specifier: 6.0.0 version: 6.0.0 @@ -276,8 +282,8 @@ importers: specifier: 1.11.0 version: 1.11.0 nodemailer: - specifier: 9.0.3 - version: 9.0.3 + specifier: 9.0.5 + version: 9.0.5 passport: specifier: 0.7.0 version: 0.7.0 @@ -297,14 +303,14 @@ importers: specifier: 2.1.0 version: 2.1.0 pg: - specifier: 8.22.0 - version: 8.22.0 + specifier: 8.23.0 + version: 8.23.0 posthog-node: - specifier: 5.46.1 - version: 5.46.1(rxjs@7.8.2) + specifier: 5.50.0 + version: 5.50.0(rxjs@7.8.2) prisma: - specifier: 7.9.0 - version: 7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + specifier: 7.9.1 + version: 7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) reflect-metadata: specifier: 0.2.2 version: 0.2.2 @@ -320,7 +326,7 @@ importers: version: 3.3.6 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.7.0(jiti@2.6.1)) + version: 10.0.1(eslint@10.8.1(jiti@2.6.1)) '@nestjs/cli': specifier: 11.0.24 version: 11.0.24(@types/node@25.9.3)(prettier@3.9.6) @@ -328,8 +334,8 @@ importers: specifier: 11.1.0 version: 11.1.0(chokidar@4.0.3)(prettier@3.9.6)(typescript@5.9.3) '@nestjs/testing': - specifier: 11.1.28 - version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/platform-express@11.1.28) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/platform-express@11.2.1) '@relmify/jest-fp-ts': specifier: 2.1.1 version: 2.1.1(fp-ts@2.16.11)(io-ts@2.2.22(fp-ts@2.16.11)) @@ -367,26 +373,26 @@ importers: specifier: 7.2.1 version: 7.2.1 '@typescript-eslint/eslint-plugin': - specifier: 8.65.0 - version: 8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.67.0 + version: 8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': - specifier: 8.65.0 - version: 8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.67.0 + version: 8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) cross-env: specifier: 10.1.0 version: 10.1.0 eslint: - specifier: 10.7.0 - version: 10.7.0(jiti@2.6.1) + specifier: 10.8.1 + version: 10.8.1(jiti@2.6.1) eslint-config-prettier: specifier: 10.1.8 - version: 10.1.8(eslint@10.7.0(jiti@2.6.1)) + version: 10.1.8(eslint@10.8.1(jiti@2.6.1)) eslint-plugin-prettier: specifier: 5.5.6 - version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.7.0(jiti@2.6.1)))(eslint@10.7.0(jiti@2.6.1))(prettier@3.9.6) + version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.1(jiti@2.6.1)))(eslint@10.8.1(jiti@2.6.1))(prettier@3.9.6) globals: - specifier: 17.7.0 - version: 17.7.0 + specifier: 17.11.0 + version: 17.11.0 jest: specifier: 30.4.2 version: 30.4.2(@types/node@25.9.3)(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)) @@ -424,11 +430,11 @@ importers: specifier: 1.0.20 version: 1.0.20 axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 axios-cookiejar-support: specifier: 6.0.5 - version: 6.0.5(axios@1.18.1)(tough-cookie@6.0.2) + version: 6.0.5(axios@1.19.0)(tough-cookie@6.0.2) chalk: specifier: 5.6.2 version: 5.6.2 @@ -448,8 +454,8 @@ importers: specifier: 4.18.1 version: 4.18.1 papaparse: - specifier: 5.5.4 - version: 5.5.4 + specifier: 5.6.0 + version: 5.6.0 qs: specifier: 6.15.3 version: 6.15.3 @@ -491,17 +497,17 @@ importers: specifier: 2.16.11 version: 2.16.11 prettier: - specifier: 3.8.5 - version: 3.8.5 + specifier: 3.9.6 + version: 3.9.6 tsup: specifier: 8.5.1 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.18)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.26)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: 5.9.3 version: 5.9.3 vitest: - specifier: 4.1.10 - version: 4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + specifier: 4.1.11 + version: 4.1.11(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) packages/hoppscotch-common: dependencies: @@ -546,7 +552,7 @@ importers: version: 6.38.8 '@guolao/vue-monaco-editor': specifier: 1.6.0 - version: 1.6.0(monaco-editor@0.55.1)(vue@3.5.40(typescript@5.9.3)) + version: 1.6.0(monaco-editor@0.55.1)(vue@3.5.41(typescript@5.9.3)) '@hoppscotch/codemirror-lang-graphql': specifier: workspace:^ version: link:../codemirror-lang-graphql @@ -567,19 +573,19 @@ importers: version: '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33' '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@hoppscotch/vue-toasted': specifier: 0.1.0 - version: 0.1.0(vue@3.5.40(typescript@5.9.3)) + version: 0.1.0(vue@3.5.41(typescript@5.9.3)) '@lezer/highlight': specifier: 1.2.1 version: 1.2.1 '@noble/curves': - specifier: 2.2.0 - version: 2.2.0 + specifier: 2.3.0 + version: 2.3.0 '@scure/base': - specifier: 2.2.0 - version: 2.2.0 + specifier: 2.3.0 + version: 2.3.0 '@shopify/lang-jsonc': specifier: 1.0.1 version: 1.0.1 @@ -593,14 +599,14 @@ importers: specifier: 9.0.7 version: 9.0.7 '@types/markdown-it': - specifier: 14.1.2 - version: 14.1.2 + specifier: 14.2.0 + version: 14.2.0 '@types/node': specifier: 24.10.1 version: 24.10.1 '@unhead/vue': specifier: 2.1.12 - version: 2.1.12(vue@3.5.40(typescript@5.9.3)) + version: 2.1.12(vue@3.5.41(typescript@5.9.3)) '@urql/core': specifier: 6.0.3 version: 6.0.3(graphql@16.13.2) @@ -611,8 +617,8 @@ importers: specifier: 3.0.0 version: 3.0.0(@urql/core@6.0.3(graphql@16.13.2)) '@vueuse/core': - specifier: 14.3.0 - version: 14.3.0(vue@3.5.40(typescript@5.9.3)) + specifier: 14.4.0 + version: 14.4.0(vue@3.5.41(typescript@5.9.3)) acorn-walk: specifier: 8.3.5 version: 8.3.5 @@ -620,8 +626,8 @@ importers: specifier: 1.0.20 version: 1.0.20 axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 buffer: specifier: 6.0.3 version: 6.0.3 @@ -630,10 +636,10 @@ importers: version: 2.0.0 dioc: specifier: 3.0.2 - version: 3.0.2(vue@3.5.40(typescript@5.9.3)) + version: 3.0.2(vue@3.5.41(typescript@5.9.3)) dompurify: - specifier: 3.4.12 - version: 3.4.12 + specifier: 3.4.14 + version: 3.4.14 esprima: specifier: 4.0.1 version: 4.0.1 @@ -659,8 +665,8 @@ importers: specifier: 9.0.2 version: 9.0.2 highlight.js: - specifier: 11.11.1 - version: 11.11.1 + specifier: 11.12.0 + version: 11.12.0 highlightjs-curl: specifier: 1.3.0 version: 1.3.0 @@ -677,8 +683,8 @@ importers: specifier: 0.8.3 version: 0.8.3 js-yaml: - specifier: 4.3.0 - version: 4.3.0 + specifier: 4.3.1 + version: 4.3.1 jsonc-parser: specifier: 3.3.1 version: 3.3.1 @@ -686,8 +692,8 @@ importers: specifier: 4.18.1 version: 4.18.1 lossless-json: - specifier: 4.3.0 - version: 4.3.0 + specifier: 4.3.1 + version: 4.3.1 markdown-it: specifier: 14.3.0 version: 14.3.0 @@ -707,8 +713,8 @@ importers: specifier: 0.12.7 version: 0.12.7 postman-collection: - specifier: 5.3.0 - version: 5.3.0 + specifier: 5.3.1 + version: 5.3.1 process: specifier: 0.11.10 version: 0.11.10 @@ -776,26 +782,26 @@ importers: specifier: 0.4.0 version: 0.4.0(zod@3.25.32) vue: - specifier: 3.5.40 - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.41 + version: 3.5.41(typescript@5.9.3) vue-i18n: - specifier: 11.4.6 - version: 11.4.6(vue@3.5.40(typescript@5.9.3)) + specifier: 11.4.8 + version: 11.4.8(vue@3.5.41(typescript@5.9.3)) vue-json-pretty: specifier: 2.6.0 - version: 2.6.0(vue@3.5.40(typescript@5.9.3)) + version: 2.6.0(vue@3.5.41(typescript@5.9.3)) vue-pdf-embed: specifier: 2.1.5 - version: 2.1.5(vue@3.5.40(typescript@5.9.3)) + version: 2.1.5(vue@3.5.41(typescript@5.9.3)) vue-router: specifier: 4.6.4 - version: 4.6.4(vue@3.5.40(typescript@5.9.3)) + version: 4.6.4(vue@3.5.41(typescript@5.9.3)) vue-tippy: specifier: 6.7.1 - version: 6.7.1(vue@3.5.40(typescript@5.9.3)) + version: 6.7.1(vue@3.5.41(typescript@5.9.3)) vuedraggable-es: specifier: 4.1.1 - version: 4.1.1(vue@3.5.40(typescript@5.9.3)) + version: 4.1.1(vue@3.5.41(typescript@5.9.3)) wonka: specifier: 6.3.6 version: 6.3.6 @@ -829,7 +835,7 @@ importers: version: 6.0.1(graphql@16.13.2) '@graphql-codegen/cli': specifier: 6.3.1 - version: 6.3.1(@parcel/watcher@2.5.6)(@types/node@24.10.1)(graphql@16.13.2)(typescript@5.9.3) + version: 6.3.1(@parcel/watcher@2.6.0)(@types/node@24.10.1)(graphql@16.13.2)(typescript@5.9.3) '@graphql-codegen/typed-document-node': specifier: 6.1.8 version: 6.1.8(graphql@16.13.2) @@ -849,14 +855,14 @@ importers: specifier: 3.2.0 version: 3.2.0(graphql@16.13.2) '@iconify-json/lucide': - specifier: 1.2.118 - version: 1.2.118 + specifier: 1.2.125 + version: 1.2.125 '@import-meta-env/cli': specifier: 0.7.4 version: 0.7.4(@import-meta-env/unplugin@0.6.3) '@intlify/unplugin-vue-i18n': - specifier: 11.2.4 - version: 11.2.4(@vue/compiler-dom@3.5.40)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + specifier: 11.2.5 + version: 11.2.5(@vue/compiler-dom@3.5.41)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@relmify/jest-fp-ts': specifier: 2.1.1 version: 2.1.1(fp-ts@2.16.11)(io-ts@2.2.22(fp-ts@2.16.11)) @@ -898,19 +904,19 @@ importers: version: 8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': specifier: 6.0.8 - version: 6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@vue/compiler-sfc': - specifier: 3.5.40 - version: 3.5.40 + specifier: 3.5.41 + version: 3.5.41 '@vue/eslint-config-typescript': specifier: 14.9.0 - version: 14.9.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vue/runtime-core': - specifier: 3.5.40 - version: 3.5.40 + specifier: 3.5.41 + version: 3.5.41 autoprefixer: specifier: 10.5.4 - version: 10.5.4(postcss@8.5.20) + version: 10.5.4(postcss@8.5.26) cross-env: specifier: 10.1.0 version: 10.1.0 @@ -922,10 +928,10 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-plugin-prettier: specifier: 5.5.6 - version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.5) + version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-vue: - specifier: 10.9.2 - version: 10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + specifier: 10.10.0 + version: 10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) glob: specifier: 13.0.6 version: 13.0.6 @@ -934,7 +940,7 @@ importers: version: 16.5.0 jsdom: specifier: 27.4.0 - version: 27.4.0(@noble/hashes@2.2.0) + version: 27.4.0(@noble/hashes@2.3.0) npm-run-all: specifier: 4.1.5 version: 4.1.5 @@ -942,65 +948,65 @@ importers: specifier: 12.1.3 version: 12.1.3 postcss: - specifier: 8.5.20 - version: 8.5.20 + specifier: 8.5.26 + version: 8.5.26 prettier: - specifier: 3.8.5 - version: 3.8.5 + specifier: 3.9.6 + version: 3.9.6 prettier-plugin-tailwindcss: specifier: 0.7.2 - version: 0.7.2(prettier@3.8.5) + version: 0.7.2(prettier@3.9.6) rollup-plugin-polyfill-node: specifier: 0.13.0 version: 0.13.0(rollup@4.60.4) sass: - specifier: 1.101.0 - version: 1.101.0 + specifier: 1.103.1 + version: 1.103.1 tailwindcss: specifier: 3.4.16 version: 3.4.16(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) tsup: specifier: 8.5.1 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.20)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.26)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: 5.9.3 version: 5.9.3 unplugin-fonts: specifier: 1.4.0 - version: 1.4.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 1.4.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) unplugin-icons: specifier: 22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16) + version: 22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16) unplugin-vue-components: specifier: 30.0.0 - version: 30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)) + version: 30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)) vite: specifier: 7.3.2 - version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vite-plugin-checker: specifier: 0.12.0 - version: 0.12.0(eslint@9.39.2(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-tsc@1.8.8(typescript@5.9.3)) + version: 0.12.0(eslint@9.39.2(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-tsc@1.8.8(typescript@5.9.3)) vite-plugin-fonts: specifier: 0.7.0 - version: 0.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 0.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-html-config: specifier: 2.0.2 - version: 2.0.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 2.0.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-pages: specifier: 0.33.3 - version: 0.33.3(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))) + version: 0.33.3(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))) vite-plugin-pages-sitemap: specifier: 1.7.1 version: 1.7.1 vite-plugin-pwa: - specifier: 1.2.0 - version: 1.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1) + specifier: 1.3.0 + version: 1.3.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1) vite-plugin-vue-layouts: specifier: 0.11.0 - version: 0.11.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + version: 0.11.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) vitest: - specifier: 4.1.10 - version: 4.1.10(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + specifier: 4.1.11 + version: 4.1.11(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vue-tsc: specifier: 1.8.8 version: 1.8.8(typescript@5.9.3) @@ -1014,8 +1020,8 @@ importers: specifier: 2.2.22 version: 2.2.22(fp-ts@2.16.11) jose: - specifier: 6.2.3 - version: 6.2.3 + specifier: 6.2.10 + version: 6.2.10 lodash: specifier: 4.18.1 version: 4.18.1 @@ -1033,26 +1039,26 @@ importers: version: 3.25.32 devDependencies: '@types/lodash': - specifier: 4.17.24 - version: 4.17.24 + specifier: 4.17.25 + version: 4.17.25 typescript: specifier: 5.9.3 version: 5.9.3 vite: specifier: 7.3.2 - version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) packages/hoppscotch-desktop: dependencies: '@fontsource-variable/inter': - specifier: 5.2.8 - version: 5.2.8 + specifier: 5.3.0 + version: 5.3.0 '@fontsource-variable/material-symbols-rounded': - specifier: 5.2.45 - version: 5.2.45 + specifier: 5.3.3 + version: 5.3.3 '@fontsource-variable/roboto-mono': - specifier: 5.2.9 - version: 5.2.9 + specifier: 5.3.0 + version: 5.3.0 '@hoppscotch/common': specifier: workspace:^ version: link:../hoppscotch-common @@ -1064,7 +1070,7 @@ importers: version: '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33' '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@tauri-apps/api': specifier: 2.1.1 version: 2.1.1 @@ -1090,27 +1096,27 @@ importers: specifier: 7.8.2 version: 7.8.2 vue: - specifier: 3.5.40 - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.41 + version: 3.5.41(typescript@5.9.3) vue-router: specifier: 4.6.4 - version: 4.6.4(vue@3.5.40(typescript@5.9.3)) + version: 4.6.4(vue@3.5.41(typescript@5.9.3)) vue-tippy: specifier: 6.7.1 - version: 6.7.1(vue@3.5.40(typescript@5.9.3)) + version: 6.7.1(vue@3.5.41(typescript@5.9.3)) zod: specifier: 3.25.32 version: 3.25.32 devDependencies: '@eslint/eslintrc': - specifier: 3.3.5 - version: 3.3.5 + specifier: 3.3.6 + version: 3.3.6 '@eslint/js': specifier: 9.39.2 version: 9.39.2 '@iconify-json/lucide': - specifier: 1.2.114 - version: 1.2.114 + specifier: 1.2.125 + version: 1.2.125 '@rushstack/eslint-patch': specifier: 1.16.1 version: 1.16.1 @@ -1124,14 +1130,14 @@ importers: specifier: 8.61.1 version: 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: 6.0.7 - version: 6.0.7(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + specifier: 6.0.8 + version: 6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@vue/eslint-config-typescript': - specifier: 14.8.0 - version: 14.8.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: 14.9.0 + version: 14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) autoprefixer: - specifier: 10.5.0 - version: 10.5.0(postcss@8.5.18) + specifier: 10.5.4 + version: 10.5.4(postcss@8.5.26) eslint: specifier: 9.39.2 version: 9.39.2(jiti@2.6.1) @@ -1139,20 +1145,20 @@ importers: specifier: 5.5.6 version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-vue: - specifier: 10.9.2 - version: 10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + specifier: 10.10.0 + version: 10.10.0(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) globals: specifier: 16.5.0 version: 16.5.0 jsdom: specifier: 27.4.0 - version: 27.4.0(@noble/hashes@2.2.0) + version: 27.4.0(@noble/hashes@2.3.0) postcss: - specifier: 8.5.18 - version: 8.5.18 + specifier: 8.5.26 + version: 8.5.26 sass: - specifier: 1.101.0 - version: 1.101.0 + specifier: 1.103.1 + version: 1.103.1 tailwindcss: specifier: 3.4.16 version: 3.4.16(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)) @@ -1161,16 +1167,16 @@ importers: version: 5.9.3 unplugin-icons: specifier: 22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16) + version: 22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16) unplugin-vue-components: specifier: 30.0.0 - version: 30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)) + version: 30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)) vite: specifier: 7.3.2 - version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vitest: specifier: 4.1.10 - version: 4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vue-tsc: specifier: 2.2.0 version: 2.2.0(typescript@5.9.3) @@ -1205,7 +1211,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^1.0.1 - version: 1.4.0(svelte@3.59.2)(vite@3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1)) + version: 1.4.0(svelte@3.59.2)(vite@3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1)) '@tauri-apps/cli': specifier: ^2.0.0-alpha.17 version: 2.9.3 @@ -1214,7 +1220,7 @@ importers: version: 3.59.2 vite: specifier: ^3.0.2 - version: 3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1) + version: 3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1) packages/hoppscotch-desktop/plugin-workspace/tauri-plugin-relay: dependencies: @@ -1244,8 +1250,8 @@ importers: specifier: 4.17.12 version: 4.17.12 acorn: - specifier: 8.17.0 - version: 8.17.0 + specifier: 8.18.0 + version: 8.18.0 chai: specifier: 6.2.2 version: 6.2.2 @@ -1284,8 +1290,8 @@ importers: specifier: 30.0.0 version: 30.0.0 '@types/lodash': - specifier: 4.17.24 - version: 4.17.24 + specifier: 4.17.25 + version: 4.17.25 '@types/node': specifier: 24.10.1 version: 24.10.1 @@ -1303,7 +1309,7 @@ importers: version: 10.1.8(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-prettier: specifier: 5.5.6 - version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.5) + version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6) globals: specifier: 16.5.0 version: 16.5.0 @@ -1311,17 +1317,17 @@ importers: specifier: 2.2.22 version: 2.2.22(fp-ts@2.16.11) prettier: - specifier: 3.8.5 - version: 3.8.5 + specifier: 3.9.6 + version: 3.9.6 typescript: specifier: 5.9.3 version: 5.9.3 vite: specifier: 7.3.2 - version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vitest: - specifier: 4.1.10 - version: 4.1.10(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + specifier: 4.1.11 + version: 4.1.11(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) packages/hoppscotch-kernel: dependencies: @@ -1347,8 +1353,8 @@ importers: specifier: 1.0.20 version: 1.0.20 axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 fp-ts: specifier: 2.16.11 version: 2.16.11 @@ -1385,7 +1391,7 @@ importers: version: 5.9.3 vite: specifier: 7.3.2 - version: 7.3.2(@types/node@24.9.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@24.9.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) packages/hoppscotch-selfhost-web: dependencies: @@ -1393,8 +1399,8 @@ importers: specifier: 5.3.0 version: 5.3.0 '@fontsource-variable/material-symbols-rounded': - specifier: 5.3.0 - version: 5.3.0 + specifier: 5.3.3 + version: 5.3.3 '@fontsource-variable/roboto-mono': specifier: 5.3.0 version: 5.3.0 @@ -1412,7 +1418,7 @@ importers: version: '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33' '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@import-meta-env/unplugin': specifier: 0.6.3 version: 0.6.3 @@ -1429,17 +1435,17 @@ importers: specifier: 2.3.3 version: 2.3.3 '@vueuse/core': - specifier: 14.3.0 - version: 14.3.0(vue@3.5.40(typescript@5.9.3)) + specifier: 14.4.0 + version: 14.4.0(vue@3.5.41(typescript@5.9.3)) axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 buffer: specifier: 6.0.3 version: 6.0.3 dioc: specifier: 3.0.2 - version: 3.0.2(vue@3.5.40(typescript@5.9.3)) + version: 3.0.2(vue@3.5.41(typescript@5.9.3)) fp-ts: specifier: 2.16.11 version: 2.16.11 @@ -1459,8 +1465,8 @@ importers: specifier: 0.4.0 version: 0.4.0(zod@3.25.32) vue: - specifier: 3.5.40 - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.41 + version: 3.5.41(typescript@5.9.3) workbox-window: specifier: 7.4.1 version: 7.4.1 @@ -1479,7 +1485,7 @@ importers: version: 6.0.1(graphql@16.14.0) '@graphql-codegen/cli': specifier: 6.3.1 - version: 6.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.3)(graphql@16.14.0)(typescript@5.9.3) + version: 6.3.1(@parcel/watcher@2.6.0)(@types/node@25.9.3)(graphql@16.14.0)(typescript@5.9.3) '@graphql-codegen/typed-document-node': specifier: 6.1.8 version: 6.1.8(graphql@16.14.0) @@ -1499,11 +1505,11 @@ importers: specifier: 3.2.0 version: 3.2.0(graphql@16.14.0) '@iconify-json/lucide': - specifier: 1.2.118 - version: 1.2.118 + specifier: 1.2.125 + version: 1.2.125 '@intlify/unplugin-vue-i18n': - specifier: 11.2.4 - version: 11.2.4(@vue/compiler-dom@3.5.40)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + specifier: 11.2.5 + version: 11.2.5(@vue/compiler-dom@3.5.41)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@rushstack/eslint-patch': specifier: 1.16.1 version: 1.16.1 @@ -1515,16 +1521,16 @@ importers: version: 8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-legacy': specifier: 7.2.1 - version: 7.2.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 7.2.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) '@vitejs/plugin-vue': specifier: 6.0.8 - version: 6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@vue/eslint-config-typescript': specifier: 14.9.0 - version: 14.9.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) autoprefixer: specifier: 10.5.4 - version: 10.5.4(postcss@8.5.20) + version: 10.5.4(postcss@8.5.26) cross-env: specifier: 10.1.0 version: 10.1.0 @@ -1538,8 +1544,8 @@ importers: specifier: 5.5.6 version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-vue: - specifier: 10.9.2 - version: 10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + specifier: 10.10.0 + version: 10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) globals: specifier: 16.5.0 version: 16.5.0 @@ -1547,8 +1553,8 @@ importers: specifier: 4.1.5 version: 4.1.5 postcss: - specifier: 8.5.20 - version: 8.5.20 + specifier: 8.5.26 + version: 8.5.26 prettier-plugin-tailwindcss: specifier: 0.7.2 version: 0.7.2(prettier@3.9.6) @@ -1560,40 +1566,40 @@ importers: version: 5.9.3 unplugin-fonts: specifier: 1.4.0 - version: 1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) unplugin-icons: specifier: 22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16) + version: 22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16) unplugin-vue-components: specifier: 30.0.0 - version: 30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)) + version: 30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)) vite: specifier: 7.3.2 - version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vite-plugin-fonts: specifier: 0.7.0 - version: 0.7.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 0.7.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-html-config: specifier: 2.0.2 - version: 2.0.2(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 2.0.2(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-inspect: specifier: 11.4.1 - version: 11.4.1(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 11.4.1(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-pages: specifier: 0.33.3 - version: 0.33.3(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))) + version: 0.33.3(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))) vite-plugin-pages-sitemap: specifier: 1.7.1 version: 1.7.1 vite-plugin-pwa: - specifier: 1.2.0 - version: 1.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1) + specifier: 1.3.0 + version: 1.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1) vite-plugin-static-copy: specifier: 3.3.0 - version: 3.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 3.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-vue-layouts: specifier: 0.11.0 - version: 0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + version: 0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) vue-tsc: specifier: 2.1.6 version: 2.1.6(typescript@5.9.3) @@ -1604,8 +1610,8 @@ importers: specifier: 5.3.0 version: 5.3.0 '@fontsource-variable/material-symbols-rounded': - specifier: 5.3.0 - version: 5.3.0 + specifier: 5.3.3 + version: 5.3.3 '@fontsource-variable/roboto-mono': specifier: 5.3.0 version: 5.3.0 @@ -1614,13 +1620,13 @@ importers: version: 3.2.0(graphql@16.13.2) '@hoppscotch/ui': specifier: 0.2.6 - version: 0.2.6(eslint@10.7.0(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 0.2.6(eslint@10.8.1(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@hoppscotch/vue-toasted': specifier: 0.1.0 - version: 0.1.0(vue@3.5.40(typescript@5.9.3)) + version: 0.1.0(vue@3.5.41(typescript@5.9.3)) '@intlify/unplugin-vue-i18n': - specifier: 11.2.4 - version: 11.2.4(@vue/compiler-dom@3.5.40)(eslint@10.7.0(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + specifier: 11.2.5 + version: 11.2.5(@vue/compiler-dom@3.5.41)(eslint@10.8.1(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@types/cors': specifier: 2.8.19 version: 2.8.19 @@ -1629,13 +1635,13 @@ importers: version: 3.0.0(@urql/core@6.0.3(graphql@16.13.2)) '@urql/vue': specifier: 2.1.1 - version: 2.1.1(@urql/core@6.0.3(graphql@16.13.2))(vue@3.5.40(typescript@5.9.3)) + version: 2.1.1(@urql/core@6.0.3(graphql@16.13.2))(vue@3.5.41(typescript@5.9.3)) '@vueuse/core': - specifier: 14.3.0 - version: 14.3.0(vue@3.5.40(typescript@5.9.3)) + specifier: 14.4.0 + version: 14.4.0(vue@3.5.41(typescript@5.9.3)) axios: - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.19.0 + version: 1.19.0 cors: specifier: 2.8.6 version: 2.8.6 @@ -1655,8 +1661,8 @@ importers: specifier: 4.18.1 version: 4.18.1 postcss: - specifier: 8.5.20 - version: 8.5.20 + specifier: 8.5.26 + version: 8.5.26 prettier-plugin-tailwindcss: specifier: 0.7.1 version: 0.7.1(prettier@3.9.6) @@ -1674,23 +1680,23 @@ importers: version: 2.0.0(@types/node@25.9.3)(typescript@5.9.3) unplugin-vue-components: specifier: 30.0.0 - version: 30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)) + version: 30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)) vue: - specifier: 3.5.40 - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.41 + version: 3.5.41(typescript@5.9.3) vue-i18n: - specifier: 11.4.6 - version: 11.4.6(vue@3.5.40(typescript@5.9.3)) + specifier: 11.4.8 + version: 11.4.8(vue@3.5.41(typescript@5.9.3)) vue-router: specifier: 4.6.4 - version: 4.6.4(vue@3.5.40(typescript@5.9.3)) + version: 4.6.4(vue@3.5.41(typescript@5.9.3)) vue-tippy: specifier: 6.7.1 - version: 6.7.1(vue@3.5.40(typescript@5.9.3)) + version: 6.7.1(vue@3.5.41(typescript@5.9.3)) devDependencies: '@graphql-codegen/cli': specifier: 6.3.1 - version: 6.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.3)(graphql@16.13.2)(typescript@5.9.3) + version: 6.3.1(@parcel/watcher@2.6.0)(@types/node@25.9.3)(graphql@16.13.2)(typescript@5.9.3) '@graphql-codegen/client-preset': specifier: 5.3.0 version: 5.3.0(graphql@16.13.2) @@ -1713,8 +1719,8 @@ importers: specifier: 3.0.1 version: 3.0.1(graphql@16.13.2) '@iconify-json/lucide': - specifier: 1.2.118 - version: 1.2.118 + specifier: 1.2.125 + version: 1.2.125 '@import-meta-env/cli': specifier: 0.7.4 version: 0.7.4(@import-meta-env/unplugin@0.6.3) @@ -1726,13 +1732,13 @@ importers: version: 4.17.12 '@vitejs/plugin-vue': specifier: 6.0.8 - version: 6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3)) '@vue/compiler-sfc': - specifier: 3.5.40 - version: 3.5.40 + specifier: 3.5.41 + version: 3.5.41 autoprefixer: specifier: 10.5.4 - version: 10.5.4(postcss@8.5.20) + version: 10.5.4(postcss@8.5.26) dotenv: specifier: 17.4.2 version: 17.4.2 @@ -1746,8 +1752,8 @@ importers: specifier: 4.1.5 version: 4.1.5 sass: - specifier: 1.101.0 - version: 1.101.0 + specifier: 1.103.1 + version: 1.103.1 ts-node: specifier: 10.9.2 version: 10.9.2(@types/node@25.9.3)(typescript@5.9.3) @@ -1756,19 +1762,19 @@ importers: version: 5.9.3 unplugin-fonts: specifier: 1.4.0 - version: 1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + version: 1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) unplugin-icons: specifier: 22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16) + version: 22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16) vite: specifier: 7.3.2 - version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + version: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vite-plugin-pages: specifier: 0.33.2 - version: 0.33.2(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))) + version: 0.33.2(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))) vite-plugin-vue-layouts: specifier: 0.11.0 - version: 0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + version: 0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) vue-tsc: specifier: 2.1.6 version: 2.1.6(typescript@5.9.3) @@ -1783,8 +1789,16 @@ packages: graphql: optional: true + '@0no-co/graphql.web@1.3.4': + resolution: {integrity: sha512-imSwulOeDQodRy/olQmVEo2PiY6ntjkZ9eiGdw6lMYylh/tay9b7MusyJBmEnkL8GiKRKr6ltr+D42mY5bd8Bg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + peerDependenciesMeta: + graphql: + optional: true + '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33': - resolution: {gitHosted: true, integrity: sha512-F5iGxxm5OgX47c6BzFazPIFJgWuv10M2/CbpFc/SjfOIHyBXa3o+dLj3tupGPiPMCQPtVCzZ1wIMjH4Xc61pMw==, tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33} + resolution: {gitHosted: true, tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33} version: 0.1.0 '@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/273488c8f50a22ee707af6b50ccd5570851f8bc9': @@ -2019,8 +2033,8 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.7': - resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': @@ -2189,6 +2203,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -2708,8 +2727,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.29.7': - resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + '@babel/plugin-transform-modules-systemjs@7.29.8': + resolution: {integrity: sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2888,8 +2907,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.29.7': - resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + '@babel/plugin-transform-regenerator@7.29.8': + resolution: {integrity: sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2936,8 +2955,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.29.7': - resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + '@babel/plugin-transform-spread@7.29.8': + resolution: {integrity: sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -3043,10 +3062,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} @@ -3067,8 +3082,8 @@ packages: resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.7': - resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} engines: {node: '>=6.9.0'} '@babel/types@7.29.0': @@ -3079,6 +3094,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -3088,7 +3107,7 @@ packages: '@boringer-avatars/vue3@0.2.1': resolution: {integrity: sha512-KzAfh31SDXToTvFL0tBNG5Ur+VzfD1PP4jmY5/GS+eIuObGTIAiUu9eiht0LjuAGI+0xCgnaEgsTrOx8H3vLOQ==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@codemirror/autocomplete@6.20.0': resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==} @@ -3837,6 +3856,12 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3859,8 +3884,8 @@ packages: resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.6.0': - resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@0.17.0': @@ -3871,10 +3896,6 @@ packages: resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.6': resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3936,8 +3957,8 @@ packages: '@fontsource-variable/material-symbols-rounded@5.2.45': resolution: {integrity: sha512-CBHbmWtViOI0Qo2+rloF1lTfNEfpW/3JCykh1i3XIJ1xQUY/JCMdf72N9Za4LVuEDJDl7FL3XbSWuMJSyPXRkQ==} - '@fontsource-variable/material-symbols-rounded@5.3.0': - resolution: {integrity: sha512-+FzjzJ4pNqC9fB+bHR54mxwkYKzGNoS3L5ZUhWapfUqdED+mF0OtRDW4Oka6TAQFg5SU/m+b9fsBvUwn75JboA==} + '@fontsource-variable/material-symbols-rounded@5.3.3': + resolution: {integrity: sha512-Gwc1/RYQJgMvNPTzAvEiQ+YvFYn+LW9n0+qNEuMkDB9NtKcb1Lnf1+jh+zF0exSuvro6qpvVZhr1VwEWkniOZA==} '@fontsource-variable/roboto-mono@5.2.9': resolution: {integrity: sha512-OzFO2AXlSGcXl/NcXS3CGjImb6rczCByPJ1C+Dzp9kkYOrUPyrGTuAtqPcmA/d+nZGX5oyOWKXLk5BrwVLYqkw==} @@ -4230,6 +4251,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/merge@9.2.3': + resolution: {integrity: sha512-cKRoXqJGy2zSRBLvotQpkACbXlHAb0yuHLN0l0ypKGCuL3NnF0zofYalkBJqiBxxxpK0lr3s9uowKFHCKi1/ZQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/optimize@1.4.0': resolution: {integrity: sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw==} peerDependencies: @@ -4258,6 +4285,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@10.1.0': + resolution: {integrity: sha512-wao48XQnfY631s3jXoNrhEHvCI8mlKXmIuWrR7F6zAdv92VuSOfHoq9P9KL2EnUMgBUnaStnByOx9Mn6RieWDg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@9.0.19': resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==} peerDependencies: @@ -4286,6 +4319,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@12.0.0': + resolution: {integrity: sha512-aMdIo/l+8j4lhamWAf+MWHr+lOW8zArSBKXspqtKHgt5I2JF9LS55KDLUe/L9AiJOV/O6qJJ60hgYydbnJHbxg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@9.2.1': resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} peerDependencies: @@ -4312,7 +4351,7 @@ packages: peerDependencies: '@vue/composition-api': ^1.7.2 monaco-editor: '>=0.43.0' - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: '@vue/composition-api': optional: true @@ -4339,7 +4378,7 @@ packages: resolution: {integrity: sha512-wVM+Ba7XTswVZ0hNKAf1559Hw2/zp4KLBz9nxDaIH3t9JtB7/DCV+Nvjm3aiGdiw7NKX9tKN4ESQQCqqvH9bGQ==} engines: {node: '>=16'} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@hoppscotch/vue-sonner@1.2.3': resolution: {integrity: sha512-P1gyvHHLsPeB8lsLP5SrqwQatuwOKtbsP83sKhyIV3WL2rJj3+DiFfqo2ErNBa+Sl0gM68o1V+wuOS7zbR//6g==} @@ -4347,7 +4386,7 @@ packages: '@hoppscotch/vue-toasted@0.1.0': resolution: {integrity: sha512-DIgmeTHxWwX5UeaHLEqDYNLJFGRosx/5N1fCHkaO8zt+sZv8GrHlkrIpjfKF2drmA3kKw5cY42Cw7WuCoabR3g==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -4365,11 +4404,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@iconify-json/lucide@1.2.114': - resolution: {integrity: sha512-NbvH3B1BYo6wBtS7joLi7f2UVQOqK2dtZodMFf3kkBs+Tnh9TkRuy8oVHr1RM8UK6bUtvAXxfNlGAah0CuvPCw==} - - '@iconify-json/lucide@1.2.118': - resolution: {integrity: sha512-JBnK4YOq6K/lA0JP//27QxFxJ4120TjvfXAzGZZIGjCcXcRRRFxl1rcV7+IWdcVCe90KXdqVaAwLaLf6G3HELw==} + '@iconify-json/lucide@1.2.125': + resolution: {integrity: sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -4538,8 +4574,8 @@ packages: '@types/node': optional: true - '@intlify/bundle-utils@11.2.4': - resolution: {integrity: sha512-eE18yR9eM9k5n8snCkHIYp2MuVTxa19aF8z9OMyxXWv0frz2HlBZDGIPFjA38pP3OJ1IlRBXC/dW5GILeLMSCQ==} + '@intlify/bundle-utils@11.2.5': + resolution: {integrity: sha512-gl7CGygRALtuT4H8n/PNC0hubg4rnctPoT04//GZNsTemvJw0Q7i6jSaq0PZHxnH0cdGqyowNXmW4FYILbahzg==} engines: {node: '>= 22.13'} peerDependencies: petite-vue-i18n: '*' @@ -4550,29 +4586,29 @@ packages: vue-i18n: optional: true - '@intlify/core-base@11.4.6': - resolution: {integrity: sha512-EOeHO95XESK9IFHgHeZXunsM/WBAoCA0DlaWODvx14vKmetAuS97t+l6Xe9hTUqntPpF93vtVSjjUDafw3wXMw==} + '@intlify/core-base@11.4.8': + resolution: {integrity: sha512-A+Q7SKm5oEcy1E/cghqd7n/St4XjTqLhiiyDuieNcMrJcrHlkY5n0jp7Q9dD3txvVHzvsmBVV5M9wD5/s1zfzw==} engines: {node: '>= 22'} - '@intlify/devtools-types@11.4.6': - resolution: {integrity: sha512-wowQPpNem56b2d43IJmqbrzG2FeBKe5f/kUGlpNuBmXs6OSqncF8m1+1lxHuW8ISZJF0ma2RkW3iLkw0g0G4VA==} + '@intlify/devtools-types@11.4.8': + resolution: {integrity: sha512-MGpID+rlfzGUbNcnC20bm5NMSBHPrvx0atLTfv9dftn3kjXw1hGKDcIcwrO99tSrZEc2i+hczRL7ks8qXsHPkQ==} engines: {node: '>= 22'} - '@intlify/message-compiler@11.4.6': - resolution: {integrity: sha512-5nj3jULqeTAC1WovwMs1LQWgatTa2pM/rXN9T3XW8rdOtXW9ZF6/GLSNFTKDQmPLwclhPdgUWLJ/4w3fMeeC/Q==} + '@intlify/message-compiler@11.4.8': + resolution: {integrity: sha512-vbzk17dYwduYiv52EK61+FDCyhfVg1uPUtPmiD/d45W99uJIcXywrweOBcHv7n9/iEqmXiMGT52bgJbZDQqK3w==} engines: {node: '>= 22'} - '@intlify/shared@11.4.6': - resolution: {integrity: sha512-m1p1HHAMLhqSpTRH7VnXdrN0CQ4y+9vunFkpLkbD8soIuBsnQdawZXqMCgvwI2UVF9Ww7sVaw7g9tV2VO7shoA==} + '@intlify/shared@11.4.8': + resolution: {integrity: sha512-XbRgrv+XEuvDr7UCY55oibVrh+o4u+A0VB6nSL0F5Z8LcZxE/8j573LYG6bCrOigIcHdGpSNI7Rh5UpC5/B/eg==} engines: {node: '>= 22'} - '@intlify/unplugin-vue-i18n@11.2.4': - resolution: {integrity: sha512-bY0ZOaVUvWTyvy4bRGCUKw4Brx5uH/ojjVKsZ1aWzY2drFKIJbeP8DpGMD2QZT8aLpZSUsHtiJlRGLQSZenrvw==} + '@intlify/unplugin-vue-i18n@11.2.5': + resolution: {integrity: sha512-wLpS0cZTNAzaNL/STjjwSFhLunuoARKzEJh7jM/G00A40cVH+GEPhdo/WsvnesPvQVoN6JfjI/mGXwDNnTbvhg==} engines: {node: '>= 22.13'} peerDependencies: petite-vue-i18n: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - vue: 3.5.40 + vue: 3.5.41 vue-i18n: '*' peerDependenciesMeta: petite-vue-i18n: @@ -4588,7 +4624,7 @@ packages: peerDependencies: '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 '@vue/compiler-dom': ^3.0.0 - vue: 3.5.40 + vue: 3.5.41 vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 peerDependenciesMeta: '@intlify/shared': @@ -4906,8 +4942,8 @@ packages: bullmq: optional: true - '@nestjs/apollo@13.4.2': - resolution: {integrity: sha512-kkIC7ini4a3ApJpOByfd0uqDH9rM4ndrn3prDd7JZD1xl81Thd/ekz3g0UvMJmtvsuCYtS36In/zayNo8GuMTA==} + '@nestjs/apollo@13.4.5': + resolution: {integrity: sha512-/7hYsCTZK7lYyde4xnvOl+WKDM81ju7JniMnDxIZAOKfVF/9wFRzCT8lJchGsJLSULTJq5mOgjYaXW5DjRGPJw==} peerDependencies: '@apollo/gateway': ^2.0.0 '@apollo/server': ^5.0.0 @@ -4917,7 +4953,7 @@ packages: '@nestjs/common': ^11.0.1 '@nestjs/core': ^11.0.1 '@nestjs/graphql': ^13.0.0 - graphql: ^16.10.0 + graphql: ^16.10.0 || ^17.0.0 peerDependenciesMeta: '@apollo/gateway': optional: true @@ -4941,8 +4977,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@11.1.28': - resolution: {integrity: sha512-bRImsxibie+AM7xjdwcrm/gr5YeacI65kSBNzTufa1Ib5iwziaY/lqMtRh9THq6pbV4e1HP9aI2ZxGUumnmaoQ==} + '@nestjs/common@11.2.1': + resolution: {integrity: sha512-SEgtP+M9DqNhQkgJIlJ3oTp3gemo/8owySovzMGmJj2kcfIH1G6QP45AAb8dE4a3IpVicpUvdDAy7Syk7ebjBw==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -4960,8 +4996,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 rxjs: ^7.1.0 - '@nestjs/core@11.1.28': - resolution: {integrity: sha512-06m63xIRj8+l8uOeh/8LnYupGubkyu4f+bPKIadaSui6vK9KpXgoz7HveT1yOVLcEt0M0oCOEW5EuEXZkEmBBQ==} + '@nestjs/core@11.2.1': + resolution: {integrity: sha512-M5PWFU8NdRTgX9Po49d7TQKg7f5t8GAVUa/Esy4tmaMWcKugTzg3ZzpJfD3LEPMuRHjfs6+8pQYgtuP2uz3rDw==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -4978,15 +5014,15 @@ packages: '@nestjs/websockets': optional: true - '@nestjs/graphql@13.4.2': - resolution: {integrity: sha512-MIaMIaV9o3Tj2LsoGGwhISTZVXEIfDK8rDXplE3tSYULj6cXSY1dofOSLMF/aY+BZLwlrN4BUUowgu8qNdDZFg==} + '@nestjs/graphql@13.4.5': + resolution: {integrity: sha512-yfgH1ccLP6+PlnlJc+lq04VcAl7uHcocBOJCVrn95jARigxcWWYdyiGZckgt9jMqrFj3FQFnUMyzV7AvydmQtA==} peerDependencies: '@apollo/subgraph': ^2.9.3 '@nestjs/common': ^11.0.1 '@nestjs/core': ^11.0.1 class-transformer: '*' class-validator: '*' - graphql: ^16.11.0 + graphql: ^16.11.0 || ^17.0.0 reflect-metadata: ^0.1.13 || ^0.2.0 ts-morph: ^20.0.0 || ^21.0.0 || ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 peerDependenciesMeta: @@ -5023,8 +5059,8 @@ packages: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 passport: ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-express@11.1.28': - resolution: {integrity: sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==} + '@nestjs/platform-express@11.2.1': + resolution: {integrity: sha512-lbaVW94s1u8AJfgmBtdMPi16MEuFBiLrnflUIA9tZ9e5eoUsGTN7XXXRjU5kTTLgjnTCM53qgBXXGmTqmyfoQA==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -5044,8 +5080,8 @@ packages: prettier: optional: true - '@nestjs/swagger@11.4.6': - resolution: {integrity: sha512-Le136h2WC7HGsd70+WyK1qrm+Zq7kFxBLkYC1JgAVqNRCt8kNh7bMF7Qkn65D5j2t/aks0+VbWmUVlYIwPrs3A==} + '@nestjs/swagger@11.4.7': + resolution: {integrity: sha512-QyDYnmfP4IRucgmtQxMqzgRBdWtjFoDp8eFvvgf92+3wdLCL+Q0xOFO1948j/ntW/Wi7qT2dyck6ka8ADzPWQQ==} peerDependencies: '@fastify/static': ^8.0.0 || ^9.0.0 || ^10.0.0 '@nestjs/common': ^11.0.1 @@ -5109,8 +5145,8 @@ packages: typeorm: optional: true - '@nestjs/testing@11.1.28': - resolution: {integrity: sha512-B+VgRxeLaH7jkOMgAyUP3N3rpFlisQ7JRxixRbgHvG6a0VgKbbkNSofKExexCgKmQQak80undb3+2kE1lUBmRQ==} + '@nestjs/testing@11.2.1': + resolution: {integrity: sha512-3mdABjqFafW+ix6fGJMPHvnj/9Or6kAPiszN8zt9bHGPuHYdkkp+9zsBDDUVuP59BcbzBqNBa5fXHBaeMzVvog==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -5129,16 +5165,16 @@ packages: '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 reflect-metadata: ^0.1.13 || ^0.2.0 - '@noble/curves@2.2.0': - resolution: {integrity: sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==} + '@noble/curves@2.3.0': + resolution: {integrity: sha512-v7cY+4oWYPQszRj6ZFGzTVL7uP2TaLo1xMhWHzYC5wj0ZhOXQ5x+sBre8rF3hi8cAoi0bh1qXoovoOkdFtvqEg==} engines: {node: '>= 20.19.0'} '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@2.2.0': - resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} + '@noble/hashes@2.3.0': + resolution: {integrity: sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==} engines: {node: '>= 20.19.0'} '@nodelib/fs.scandir@2.1.5': @@ -5153,6 +5189,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@oozcitak/dom@2.0.2': resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} engines: {node: '>=20.0'} @@ -5172,92 +5211,86 @@ packages: '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} - '@parcel/watcher-android-arm64@2.5.6': - resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + '@parcel/watcher-android-arm64@2.6.0': + resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] - '@parcel/watcher-darwin-arm64@2.5.6': - resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + '@parcel/watcher-darwin-arm64@2.6.0': + resolution: {integrity: sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] - '@parcel/watcher-darwin-x64@2.5.6': - resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + '@parcel/watcher-darwin-x64@2.6.0': + resolution: {integrity: sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] - '@parcel/watcher-freebsd-x64@2.5.6': - resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + '@parcel/watcher-freebsd-x64@2.6.0': + resolution: {integrity: sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] - '@parcel/watcher-linux-arm-glibc@2.5.6': - resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + '@parcel/watcher-linux-arm-glibc@2.6.0': + resolution: {integrity: sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [glibc] - '@parcel/watcher-linux-arm-musl@2.5.6': - resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + '@parcel/watcher-linux-arm-musl@2.6.0': + resolution: {integrity: sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [musl] - '@parcel/watcher-linux-arm64-glibc@2.5.6': - resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + '@parcel/watcher-linux-arm64-glibc@2.6.0': + resolution: {integrity: sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@parcel/watcher-linux-arm64-musl@2.5.6': - resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + '@parcel/watcher-linux-arm64-musl@2.6.0': + resolution: {integrity: sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [musl] - '@parcel/watcher-linux-x64-glibc@2.5.6': - resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + '@parcel/watcher-linux-x64-glibc@2.6.0': + resolution: {integrity: sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [glibc] - '@parcel/watcher-linux-x64-musl@2.5.6': - resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + '@parcel/watcher-linux-x64-musl@2.6.0': + resolution: {integrity: sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [musl] - '@parcel/watcher-win32-arm64@2.5.6': - resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + '@parcel/watcher-win32-arm64@2.6.0': + resolution: {integrity: sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - '@parcel/watcher-win32-ia32@2.5.6': - resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - - '@parcel/watcher-win32-x64@2.5.6': - resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + '@parcel/watcher-win32-x64@2.6.0': + resolution: {integrity: sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - '@parcel/watcher@2.5.6': - resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + '@parcel/watcher@2.6.0': + resolution: {integrity: sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==} engines: {node: '>= 10.0.0'} '@peculiar/asn1-schema@2.6.0': @@ -5289,20 +5322,20 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@posthog/core@1.45.1': - resolution: {integrity: sha512-tLtvzomavb2PPWdGYKsusyIzIeL2Px47v348Smibkay7sMy/83TyPk+Ptsp2NdeOgJsbuwSxWkR2+XA0aSCAaA==} + '@posthog/core@1.48.8': + resolution: {integrity: sha512-LAOBOjMQrQmgcbZxnubl74l2GQd5OWqxCJJ8mzcWdZRWbfO6Y/A6E3fqWjDtm68hY4LxC/bbopG60cxZW8cfWw==} - '@posthog/types@1.398.0': - resolution: {integrity: sha512-sJMkl4k+u8yS/0fjHsKqE9xTdsAh30a2WvgChiptellnVoE0e8QJKFgqOMD2sk8FaEArPdeFklAhXvmENAt3Sg==} + '@posthog/types@1.405.1': + resolution: {integrity: sha512-JvaR4ChUKUk7qSTG58vKN2Br6es9riFF5mvlu7YGcwbB476vfyTO0o/TBh+zE88QhsfxnE5Tr/jKxI+e1v3Q5A==} - '@prisma/adapter-pg@7.9.0': - resolution: {integrity: sha512-kPYuFvNTlqnaFf2UpXBBG3ycTT3PL76uSZtLFBEwDytjMMUW8ZHrsb9cSNIarzdPW5EXWmBOeOq9/MVjMtbWkA==} + '@prisma/adapter-pg@7.9.1': + resolution: {integrity: sha512-Ho2RK1KanQxLNSC0sR5bpiiVep10sWPLXCcxK+KXfI/Q69TMRbiafSvLPv3V9snimX72rMCqGlyJ4sBO4lKTAw==} - '@prisma/client-runtime-utils@7.9.0': - resolution: {integrity: sha512-kMVmS4ZEy3xlkca+TfxOEm/ToVVlOS2x1Tc6/wIRf/HfczBqENtSPcKszy4ZpFNzjJ8SRKvlU5V0rrpoFw2KOg==} + '@prisma/client-runtime-utils@7.9.1': + resolution: {integrity: sha512-mVIBGYdO5CFmK0HvjxrtfIyQQcPdb88pSCeVQriVQPVZyDovIWblpHfOgcS8QO187j3QF0ePArH8qPhp0AU2vg==} - '@prisma/client@7.9.0': - resolution: {integrity: sha512-BTG/mB+WL/1sD2gWwdNc2uuVJjNNBgCDlPFdjco6jJArgbg4IAChtzVeW4debFa/NKBbsGedCjET316sjllWTQ==} + '@prisma/client@7.9.1': + resolution: {integrity: sha512-+xgrh2EhJVF79wC0yX5G4PI1Rdcm7Qn/nekNQ+t/O153wtNggruHal+fXHSa0QE+Tp/Cw5wvxeCEhZZ59xGm8Q==} engines: {node: ^20.19 || ^22.12 || >=24.0} peerDependencies: prisma: '*' @@ -5313,35 +5346,35 @@ packages: typescript: optional: true - '@prisma/config@7.9.0': - resolution: {integrity: sha512-CsoK2mhl0u+N4/8V+XroQMOUNIic4isqD+E2HBG8l1yGEKo62CFDu3FHo0FdwItjl6XkW+omA1STSzeN1DAXlg==} + '@prisma/config@7.9.1': + resolution: {integrity: sha512-4znKhxTmXmuPye9Z6pbIyYb5VZlkZ05qG1L6Dr4g+7oTwc6V50Bs9XirFBDdjWt+H/AabMn9aUnxBcvj8z05aA==} '@prisma/debug@7.2.0': resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - '@prisma/debug@7.9.0': - resolution: {integrity: sha512-i0KdVQuKUE6N9NloHs+sUNAk2c9svR3myBndQbA3BoeoArsSpwtNgTdHZL+wBtCLCcdS2OOC/PKhgTe36jkF5A==} + '@prisma/debug@7.9.1': + resolution: {integrity: sha512-/cpVZ4itxtcgB8GHBvZtcmuEjq+lWsLrRJxFMbwZrT1RIdtuKmUm7PPGo/wzfbYpBrk+9WmmBE8CHJw2rybKDQ==} - '@prisma/dev@0.24.14': - resolution: {integrity: sha512-NhFO49O2JPTdzYiLHvceQn/HiwmcKF/iGV39ko3CpYsoGqS3rz3ko6gzuxFSIeHNwNJeuNcDexyyGeTO3DW80A==} + '@prisma/dev@0.24.17': + resolution: {integrity: sha512-UvdZzmpFwknnfreh6Jije84ekkYGPYEJhXG1tFzCsCfQyzJifrOo38eZc0qajzvaC6OLUOrN9ML5XfCnEZL9DA==} - '@prisma/driver-adapter-utils@7.9.0': - resolution: {integrity: sha512-fFXujitfMyjk3kOd1Tbs5FXBm6i2OWwEhaP5lHgkUM99jHpPEQwCWj+z/WKPFq6EDMThE1zGzSlVegtR0Pmu2w==} + '@prisma/driver-adapter-utils@7.9.1': + resolution: {integrity: sha512-vmHehG7nn/heW32DXXpp13DxxAxVVe6n250oEt3dOL2E/4bt3olktKZN0mzSuxMMronyMSkbeW2uCOn3F4g8RQ==} '@prisma/engines-version@7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad': resolution: {integrity: sha512-2BsPPFksz3CQUXG6af3rVCtJKg6+JJGJTtfgu2fU8DdXhOfkBjulCq8mwybCd6ge0/jhZq2kOtLAbmUDMyI1nA==} - '@prisma/engines@7.9.0': - resolution: {integrity: sha512-lDWJp/pgSWCLfYsupmmNo96jfsbQnH1yjia8XVM2Kh8nRZhD0bQU2jCHuy3ZTPMLR3apRD3k145ybENalAYjYw==} + '@prisma/engines@7.9.1': + resolution: {integrity: sha512-UprXSMNXx2NF5ow4pqaQtE8OuBz6K78B0wc0tn2L28G5r933iWp1DR9Do2qWrsNvvFIP3x6mpEWnQtckMO0Uhg==} - '@prisma/fetch-engine@7.9.0': - resolution: {integrity: sha512-F0XlIgjbE3EywRVR/HpCerNI/dxo40vK66tHcWpsWYwH/Jk9+FsICEzATeMsZ7bdnpZz93hkD4sAb5rKLsCCpA==} + '@prisma/fetch-engine@7.9.1': + resolution: {integrity: sha512-9DwxrNTeT25Orbu9CWh0CZvVlyY1lmscpbaeLZcOnuR7zcuFrt91YSmmOfIm7zJ08YOZ6mVzURKwLoMwEBcK8w==} '@prisma/get-platform@7.2.0': resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - '@prisma/get-platform@7.9.0': - resolution: {integrity: sha512-4awv6ATdgrHdLms0XKikCyfArn8BrUHZfqg0mtCKrI4+WJe24nmpsdwsypM9ozd03wa846AngY+zSbnngkMrXQ==} + '@prisma/get-platform@7.9.1': + resolution: {integrity: sha512-PK8R60YZRQvYxBrGG9i7l2/rFyzy+2MuI1dKtmtrCqPH8YpiJx/MfiC7LRzX5786rZDEv7BngcjfIJW4/9ADuw==} '@prisma/query-plan-executor@7.2.0': resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} @@ -5385,8 +5418,8 @@ packages: '@protobufjs/pool@1.1.0': resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} @@ -5869,8 +5902,8 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - '@scure/base@2.2.0': - resolution: {integrity: sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg==} + '@scure/base@2.3.0': + resolution: {integrity: sha512-NsG6Y03tY6R5BUis4FdVtHVkur0U6FOzskgs9ZXNl78CUc9fkZ78HmENUle1nSOkCasDmbubmWD9qwB7mm4PZA==} '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -6194,14 +6227,17 @@ packages: '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + '@types/lodash@4.17.25': + resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==} + '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} '@types/luxon@3.7.1': resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} - '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + '@types/markdown-it@14.2.0': + resolution: {integrity: sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==} '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} @@ -6360,11 +6396,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/eslint-plugin@8.65.0': - resolution: {integrity: sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==} + '@typescript-eslint/eslint-plugin@8.67.0': + resolution: {integrity: sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.65.0 + '@typescript-eslint/parser': ^8.67.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -6382,8 +6418,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.65.0': - resolution: {integrity: sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==} + '@typescript-eslint/parser@8.67.0': + resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -6401,8 +6437,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.65.0': - resolution: {integrity: sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==} + '@typescript-eslint/project-service@8.67.0': + resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -6415,8 +6451,8 @@ packages: resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.65.0': - resolution: {integrity: sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==} + '@typescript-eslint/scope-manager@8.67.0': + resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.61.1': @@ -6437,6 +6473,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.67.0': + resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.61.1': resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6451,8 +6493,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.65.0': - resolution: {integrity: sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==} + '@typescript-eslint/type-utils@8.67.0': + resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -6470,6 +6512,10 @@ packages: resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.61.1': resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6482,8 +6528,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.65.0': - resolution: {integrity: sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==} + '@typescript-eslint/typescript-estree@8.67.0': + resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -6502,8 +6548,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.65.0': - resolution: {integrity: sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==} + '@typescript-eslint/utils@8.67.0': + resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -6517,8 +6563,8 @@ packages: resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.65.0': - resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==} + '@typescript-eslint/visitor-keys@8.67.0': + resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -6528,7 +6574,7 @@ packages: '@unhead/vue@2.1.12': resolution: {integrity: sha512-zEWqg0nZM8acpuTZE40wkeUl8AhIe0tU0OkilVi1D4fmVjACrwoh5HP6aNqJ8kUnKsoy6D+R3Vi/O+fmdNGO7g==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -6661,7 +6707,7 @@ packages: resolution: {integrity: sha512-gmQBQtHO7Rtw1W+CiLb6YcnQEczog3ZxLsFhMvTex1aM1mczT6dDJuiYpJDg2KkuJ8zqXSbMnbwwbm4ukr9vTA==} peerDependencies: '@urql/core': ^6.0.0 - vue: 3.5.40 + vue: 3.5.41 '@visx/curve@4.0.1-alpha.0': resolution: {integrity: sha512-jRu61Uz274pV1zyioXmboyrLutYbnKsgjj4njSGCnhdXj5GkZvZbg+ThDb6oOzoAnJOBRLz4rzPlWvNJOzuVMg==} @@ -6712,23 +6758,19 @@ packages: terser: ^5.16.0 vite: ^7.0.0 - '@vitejs/plugin-vue@6.0.7': - resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - vue: 3.5.40 - '@vitejs/plugin-vue@6.0.8': resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - vue: 3.5.40 + vue: 3.5.41 '@vitest/expect@4.1.10': resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + '@vitest/mocker@4.1.10': resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: @@ -6740,21 +6782,47 @@ packages: vite: optional: true + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@4.1.10': resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + '@vitest/runner@4.1.10': resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + '@vitest/snapshot@4.1.10': resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + '@vitest/spy@4.1.10': resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@volar/language-core@1.10.10': resolution: {integrity: sha512-nsV1o3AZ5n5jaEAObrS3MWLBWaGwUj/vAsc15FVNIv+DbpizQRISg9wzygsHBr56ELRH8r4K75vkYNMtsSNNWw==} @@ -6776,20 +6844,20 @@ packages: '@vue/compiler-core@3.5.38': resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} - '@vue/compiler-core@3.5.40': - resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + '@vue/compiler-core@3.5.41': + resolution: {integrity: sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==} '@vue/compiler-dom@3.5.38': resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} - '@vue/compiler-dom@3.5.40': - resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + '@vue/compiler-dom@3.5.41': + resolution: {integrity: sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==} - '@vue/compiler-sfc@3.5.40': - resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + '@vue/compiler-sfc@3.5.41': + resolution: {integrity: sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==} - '@vue/compiler-ssr@3.5.40': - resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==} + '@vue/compiler-ssr@3.5.41': + resolution: {integrity: sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -6797,17 +6865,6 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/eslint-config-typescript@14.8.0': - resolution: {integrity: sha512-yIquzhXH7ZsrwSSm+rYvoGCRY6wcuF4qBi76e0l7hHLq7YU0f9aC+RcR5fL+XJNfmBZxgX5cVl4sppt4x7ZCBg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.10.0 || ^10.0.0 - eslint-plugin-vue: ^9.28.0 || ^10.0.0 - typescript: '>=4.8.4' - peerDependenciesMeta: - typescript: - optional: true - '@vue/eslint-config-typescript@14.9.0': resolution: {integrity: sha512-E3j9hDlfVf10F30MRcLTPY2IIhWIx1nsvkVukk14kTcuA+oBVot9zsP1hzsO+PAMDxV3Fd9FimBJtUBNBL5KFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6847,59 +6904,59 @@ packages: '@vue/reactivity@3.5.38': resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} - '@vue/reactivity@3.5.40': - resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==} + '@vue/reactivity@3.5.41': + resolution: {integrity: sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==} - '@vue/runtime-core@3.5.40': - resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==} + '@vue/runtime-core@3.5.41': + resolution: {integrity: sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==} - '@vue/runtime-dom@3.5.40': - resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==} + '@vue/runtime-dom@3.5.41': + resolution: {integrity: sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==} - '@vue/server-renderer@3.5.40': - resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==} + '@vue/server-renderer@3.5.41': + resolution: {integrity: sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==} '@vue/shared@3.5.38': resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} - '@vue/shared@3.5.40': - resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + '@vue/shared@3.5.41': + resolution: {integrity: sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==} '@vue/typescript@1.8.8': resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==} - '@vueuse/core@14.3.0': - resolution: {integrity: sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==} + '@vueuse/core@14.4.0': + resolution: {integrity: sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@vueuse/core@8.9.4': resolution: {integrity: sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==} peerDependencies: '@vue/composition-api': ^1.1.0 - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: '@vue/composition-api': optional: true vue: optional: true - '@vueuse/metadata@14.3.0': - resolution: {integrity: sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==} + '@vueuse/metadata@14.4.0': + resolution: {integrity: sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==} '@vueuse/metadata@8.9.4': resolution: {integrity: sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==} - '@vueuse/shared@14.3.0': - resolution: {integrity: sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==} + '@vueuse/shared@14.4.0': + resolution: {integrity: sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 '@vueuse/shared@8.9.4': resolution: {integrity: sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==} peerDependencies: '@vue/composition-api': ^1.1.0 - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: '@vue/composition-api': optional: true @@ -6992,6 +7049,10 @@ packages: a-sync-waterfall@1.0.1: resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -7038,8 +7099,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true @@ -7091,6 +7152,9 @@ packages: ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -7172,8 +7236,8 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argon2@0.44.0: - resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} + argon2@0.45.1: + resolution: {integrity: sha512-skm+/WCjkGqCQxF7FG1LuZXM5yvbFjgbfiCGsud2oLgaDhh6b6dbH0b1EkghbM+xx4Bj8Ape+KKgixoIlWZicQ==} engines: {node: '>=16.17.0'} argparse@1.0.10: @@ -7238,19 +7302,12 @@ packages: resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} engines: {node: '>=8'} - autoprefixer@10.5.0: - resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: 8.5.18 - autoprefixer@10.5.4: resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} @@ -7270,8 +7327,8 @@ packages: axios: '>=0.20.0' tough-cookie: '>=4.0.0' - axios@1.18.1: - resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + axios@1.19.0: + resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==} babel-jest@30.4.1: resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} @@ -7358,8 +7415,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - baseline-browser-mapping@2.11.1: - resolution: {integrity: sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==} + baseline-browser-mapping@2.11.17: + resolution: {integrity: sha512-KAUDn1OSS0fmPlGO+NOUMRcOQ/b/shUBH3OgkG73mPgdf+JD/BQ6fHboGxNOxnUmlwcq+lLq3dTkayRPuSfXwg==} engines: {node: '>=6.0.0'} hasBin: true @@ -7390,8 +7447,8 @@ packages: blob@0.0.5: resolution: {integrity: sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==} - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} boolbase@1.0.0: @@ -7401,8 +7458,8 @@ packages: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} engines: {node: '>=10'} - brace-expansion@5.0.8: - resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} braces@3.0.3: @@ -7432,8 +7489,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.28.7: - resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -7533,11 +7590,8 @@ packages: caniuse-lite@1.0.30001781: resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} - caniuse-lite@1.0.30001791: - resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} - - caniuse-lite@1.0.30001806: - resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -7589,9 +7643,9 @@ packages: cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - cheerio@1.0.0-rc.12: - resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} - engines: {node: '>= 6'} + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -7713,6 +7767,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@13.1.0: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} @@ -7721,6 +7779,10 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -7732,10 +7794,6 @@ packages: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} - commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -7777,6 +7835,9 @@ packages: confbox@0.2.4: resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -7852,6 +7913,10 @@ packages: core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + core-js-compat@3.50.0: + resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} + engines: {node: '>=6.4.0'} + core-js@3.49.0: resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} @@ -7933,7 +7998,7 @@ packages: resolution: {integrity: sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==} engines: {node: ^14 || ^16 || >=18} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -7962,19 +8027,31 @@ packages: resolution: {integrity: sha512-waWlAMuCakP7//UCY+JPrQS1z0OSLeOXk2sKWJximKWGupVxre50bzPlvpbUwZIDylhf/ptf0Pk+Yf7C+hoa3g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 + + cssnano-preset-lite@4.0.6: + resolution: {integrity: sha512-EI/VDoucl8SmVkXUZtWIux31cWoxgNUbF7njnpPxdz5ZbnKOjAd5DueLuCE1RKKLrOPQsEUaNfUgB1taohIIyQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: 8.5.26 cssnano-utils@5.0.1: resolution: {integrity: sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 + + cssnano-utils@5.0.3: + resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: 8.5.26 cssnano@7.1.3: resolution: {integrity: sha512-mLFHQAzyapMVFLiJIn7Ef4C2UCEvtlTlbyILR6B5ZsUAV3D/Pa761R5uC1YPhyBkRd3eqaDm2ncaNrD7R4mTRg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} @@ -8128,8 +8205,8 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + deepmerge-ts@8.0.1: + resolution: {integrity: sha512-szCXE7YLCvLKR9bFPJcvsezOShdalctSvrgN/LM/QGUEPZQajwjmsMObZ6/DuANT5lxzM/wtO8Feubwdkz8myA==} engines: {node: '>=16.0.0'} deepmerge@4.3.1: @@ -8228,7 +8305,7 @@ packages: dioc@3.0.2: resolution: {integrity: sha512-D8S1vMTtBeXeUW2dR0rJ7xiPHxp1zm1NzO2B4Aj4RAJB6E6urA0/xD/CnGs6J1JkgUZvUgaC+oedx/k5NrT+/g==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: vue: optional: true @@ -8256,10 +8333,6 @@ packages: domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - domhandler@3.3.0: - resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} - engines: {node: '>= 4'} - domhandler@4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} @@ -8271,8 +8344,8 @@ packages: dompurify@3.2.7: resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} - dompurify@3.4.12: - resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} + dompurify@3.4.14: + resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==} domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} @@ -8327,6 +8400,11 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + engines: {node: '>=14'} + hasBin: true + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -8349,8 +8427,8 @@ packages: electron-to-chromium@1.5.344: resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} - electron-to-chromium@1.5.396: - resolution: {integrity: sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==} + electron-to-chromium@1.5.412: + resolution: {integrity: sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==} elkjs@0.11.1: resolution: {integrity: sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==} @@ -8380,6 +8458,9 @@ packages: resolution: {integrity: sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==} engines: {node: '>=8.10.0'} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -8475,6 +8556,9 @@ packages: es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -8491,8 +8575,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.1: - resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} es6-promise@3.3.1: @@ -8690,8 +8774,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-vue@10.9.2: - resolution: {integrity: sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==} + eslint-plugin-vue@10.10.0: + resolution: {integrity: sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -8728,8 +8812,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.7.0: - resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} + eslint@10.8.1: + resolution: {integrity: sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -8846,8 +8930,8 @@ packages: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} engines: {node: '>= 0.8.0'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} expect@29.7.0: @@ -8912,8 +8996,8 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.1.4: - resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + fast-uri@3.1.5: + resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} fast-url-parser@1.1.3: resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} @@ -9199,8 +9283,8 @@ packages: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} - globals@17.7.0: - resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + globals@17.11.0: + resolution: {integrity: sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==} engines: {node: '>=18'} globalthis@1.0.4: @@ -9317,6 +9401,22 @@ packages: ws: optional: true + graphql-ws@6.2.1: + resolution: {integrity: sha512-NMbPNeTwXpUOxmczdMtzEnynLNbbR267E9hRcJ81SSbQeIvZup3cMjbD1ZT3jpS2xkpxooisitvO7LZNOyz17Q==} + engines: {node: '>=20'} + peerDependencies: + '@fastify/websocket': ^10 || ^11 + crossws: ~0.3 + graphql: ^15.10.1 || ^16 || ^17 + ws: 8.21.0 + peerDependenciesMeta: + '@fastify/websocket': + optional: true + crossws: + optional: true + ws: + optional: true + graphql@16.13.2: resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -9380,8 +9480,8 @@ packages: header-case@2.0.4: resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} - highlight.js@11.11.1: - resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + highlight.js@11.12.0: + resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==} engines: {node: '>=12.0.0'} highlightjs-curl@1.3.0: @@ -9408,16 +9508,17 @@ packages: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} - htmlnano@2.1.5: - resolution: {integrity: sha512-IXffzXq1beGQN2rsr03aIPK/rVU1jR2uwHymlAIEf97Tl5WdpG50IsQ5nWGvSGQJ+x6U7S6yac9rRiFgAg4/xQ==} + htmlnano@3.4.0: + resolution: {integrity: sha512-5rgW9c/830dlDiWLlsT3ZLBs52UAupymGwkwr4rn2yyuzrNY0RZwgr6F4q2AkUqrRuuHb3kPelqYsUgtzQDtQw==} + hasBin: true peerDependencies: - cssnano: ^7.0.0 - postcss: 8.5.18 - purgecss: ^7.0.2 + cssnano: ^7.0.0 || ^8.0.0 + postcss: 8.5.26 + purgecss: ^8.0.0 relateurl: ^0.2.7 - srcset: 5.0.1 - svgo: ^3.0.2 - terser: ^5.10.0 + srcset: ^5.0.1 + svgo: 4.0.2 + terser: ^5.21.0 uncss: ^0.17.3 peerDependenciesMeta: cssnano: @@ -9437,9 +9538,6 @@ packages: uncss: optional: true - htmlparser2@5.0.1: - resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} - htmlparser2@7.2.0: resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} @@ -9507,6 +9605,10 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -9531,6 +9633,9 @@ packages: immutable@5.1.6: resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -10100,8 +10205,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.3: - resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + jose@6.2.10: + resolution: {integrity: sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==} joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -10113,6 +10218,14 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.8: + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + js-md5@0.8.3: resolution: {integrity: sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==} @@ -10126,12 +10239,12 @@ packages: resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true - js-yaml@4.3.0: - resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true - js-yaml@5.2.2: - resolution: {integrity: sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==} + js-yaml@5.3.0: + resolution: {integrity: sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==} hasBin: true jsdom@27.4.0: @@ -10199,9 +10312,9 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - juice@10.0.1: - resolution: {integrity: sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==} - engines: {node: '>=10.0.0'} + juice@11.1.1: + resolution: {integrity: sha512-4SBfZqKcc6DrIS+5b/WiGoWaZsdUPBH+e6SbRlNjJpaIRtfoBhYReAtobIEW6mcLeFFDXLBJMuZwkJLkBJjs2w==} + engines: {node: '>=18.17'} hasBin: true jwa@2.0.1: @@ -10401,8 +10514,8 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lossless-json@4.3.0: - resolution: {integrity: sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==} + lossless-json@4.3.1: + resolution: {integrity: sha512-SqD/Bg3ZfltBJ2Z14hJ/BihnvtV553WO4g9/ePtlp4lrnl9jF3AdIJt53A/Wkg/0Li+LMfxaBqgx1MiFZdQlpQ==} lower-case-first@2.0.2: resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==} @@ -10612,99 +10725,99 @@ packages: minisearch@7.2.0: resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} - mjml-accordion@5.0.0-alpha.4: - resolution: {integrity: sha512-Mw1DnHRJHwHLqkwAXcRLBHZMYLtw7qqDNJdxISihz5KyY2arc8MbZixoUHCd3M/2zw04J8fU5HJ8WslANrmu9g==} + mjml-accordion@5.4.0: + resolution: {integrity: sha512-yElB+84k5kZpTz8Ct3eRu63fkEeGc4mBZmbAfZrS5sZCb9DiamAfhozLee5WgO4Y3cwuf8cFsfhYGPuBw60XCw==} - mjml-body@5.0.0-alpha.4: - resolution: {integrity: sha512-hPa4JpaF7rmKgKdC/DqC9SM97XoXoWPAf8c+8GpSvn/9AwXnt9X0TgBoP7/sUR26N06j26+/fprB7cTiqy/glA==} + mjml-body@5.4.0: + resolution: {integrity: sha512-fPZLONKnRGR2NxkmfKPvnnr/ycVeyahi1ySX6Rk8lEO76ywXNFDWzTbzz/UQ2gzfnD8AhDbuqzd/UZRpW0vdOg==} - mjml-button@5.0.0-alpha.4: - resolution: {integrity: sha512-4rOobUMBuoDjsnqFgtLMBZMdnTmS8vMLI+ZfrvyyxaPL9RbeISZlbl3/RvxiZjAyctPh92X/PazKhHJyeSSqeg==} + mjml-button@5.4.0: + resolution: {integrity: sha512-HlecSMeio6xf21nh4vMaHIJF8bN24KatK3gwcR6ByxKfzTkQVR7jtWjJLUiyiuLOJcam9wCOVl7m5J6elrFpjg==} - mjml-carousel@5.0.0-alpha.4: - resolution: {integrity: sha512-cUPIFLoseSlsq0/w/gB5/sMd88P2LCPK+ISllSdvyO4Lo2+uHDlmwMxRCBBIuWJBunqVH9v2Z2MtAIOL6DqJsQ==} + mjml-carousel@5.4.0: + resolution: {integrity: sha512-fuhOEETPC/+ZtISh5iOlc6uQqOKWwhwg1W8XTJrzWj5pfa46BzMdR7Nx0Z+8I8/HRqrZA3Ws4hiSTTgbpTIRjg==} - mjml-cli@5.0.0-alpha.4: - resolution: {integrity: sha512-YXaCYxQ64I1DFmlJe5OI6S1U3jbF3CdfSw+IsOTxxY+i0lEyuiqJxLceKA2ogAwMjlZEm1BBGENqNnvDjeXmUw==} + mjml-cli@5.4.0: + resolution: {integrity: sha512-6HeOz0zadc9iOmMVg85ts1HhoW2CiLUNaTFfnC5YXfowPnjK0bWfMluDzxHcaAk12wlnDF06kROl8pKpZeiy6A==} hasBin: true - mjml-column@5.0.0-alpha.4: - resolution: {integrity: sha512-5gT0YNU+aAjpUxS39ySS2SqL+NLyXkCi4BPutzZTnmz2CvIwrBIOJVEHRAWSjNUWFfFLS4scquI8yO4g8AVfdA==} + mjml-column@5.4.0: + resolution: {integrity: sha512-vnseCiUUKhtQXx5ZEoApxA3elu2AZZyyQkOhE2ntG5cPQuZd3EhRv/mkKKCS99Vi51Tcf7AQ3chwSD4AL+bDHg==} - mjml-core@5.0.0-alpha.4: - resolution: {integrity: sha512-QioM27JKUWhCfDbHxY1YnkgpTF0Y+hV1MHy5XeVTQlvIbEeRcO+gAPzhVooGYsKqQL/dWNM9jl34el0peRoscQ==} + mjml-core@5.4.0: + resolution: {integrity: sha512-dhcbpBmxktzv/tV2Gcz7BJC15fKEP8LzXUlEYMhi0XDsNEkhHxPGXxvhMoc020jJ9Ypjobnh1q7ow+F8Xx72jA==} - mjml-divider@5.0.0-alpha.4: - resolution: {integrity: sha512-sPv5CARR7NX6ohbpJCzErgv3Y1rUnmtOs3SeiEgp4Y9J+O+wKaOZa/ffuNHVrxkC26U91e3zmbWItfIILPUgYA==} + mjml-divider@5.4.0: + resolution: {integrity: sha512-8mk7J0tn0rX+FBO43cJkaKO3x0GCjUX4bdPI5txoh1UWyq1N6xgoEqTAR3luwR0f8juTmUXZv97GnQdRUsWtvQ==} - mjml-group@5.0.0-alpha.4: - resolution: {integrity: sha512-V+YuKGwL6JMTAnvTsKQM4wF6VPiHCgo92aN9iNuY46N8oYM349pEgrHDBAWRhyZ7UAov/UoPUUJRydJk4PCGyw==} + mjml-group@5.4.0: + resolution: {integrity: sha512-gCCU0WV8Aytt68uczjId3xhsvUJ8qU1WDCL+b7I3wrI1ja5npRyXdATHM6Q2uXbm+an8Dxz5q77Ts9sodAHZIQ==} - mjml-head-attributes@5.0.0-alpha.4: - resolution: {integrity: sha512-EmyiNar6SeaMDcTa8gchUoONfNbUfCjI3eAwjkHy1SfDl5tXKku2W1oXCst8vtNpjoBzllHcTW81x0OpgDM4Cg==} + mjml-head-attributes@5.4.0: + resolution: {integrity: sha512-c2/Zi/2wCutEVChxY8RGbPIb2Wb0Ro3A9WVJ0DezyEeN9noTT1fRiMWYQIc2y3H5STfNtIFu6RbtDU+AK2p4rg==} - mjml-head-breakpoint@5.0.0-alpha.4: - resolution: {integrity: sha512-S8FBpMKO2wDTJscy6EtQuQRZMu1YSOD5fCZ6sHINWC2A40I1ZFsCAvlLtW/vr9P50XjgX06m1T/vTcYifMCMMQ==} + mjml-head-breakpoint@5.4.0: + resolution: {integrity: sha512-qtJZ7uaMxVObrr13um5tbktxih8ycTStYAdcKMdSsgqLG3dTNEfVF9wg7AEHs6e3GB1cPnHgQwF9v7nxe+vocw==} - mjml-head-font@5.0.0-alpha.4: - resolution: {integrity: sha512-bc/bduI1BljN1rjcF8w5TOBZ+D0eBu5O0BnSqLwoct7xeoTTvYLxuTsdgoloh6Jm1vf3RMqr4ANySDrXvFkoPw==} + mjml-head-font@5.4.0: + resolution: {integrity: sha512-c0shDE+Bt7tob9NNpNOk8pRpJMWztfgNuoyXAIkOc7VhILnsYzH5+4Ly70wlHOzQAspC3cODdkZxut8i3ApRcQ==} - mjml-head-html-attributes@5.0.0-alpha.4: - resolution: {integrity: sha512-NJwXgE3o1E3BcVTG6+Hl/ofCZFsoKnjt//Sm/Ks+0u+aZD7VycsF+nXxBlMLOWhMQrP+JIZAok7mYE+A1ztAPg==} + mjml-head-html-attributes@5.4.0: + resolution: {integrity: sha512-o9yEfrA1/5r3EbxXXUVBKf91wSh+vxBSNDYQFc0Do8/8gLg7MvczFVXH2Gq9PQdxPEO+AeBrP4sGP5ZxGJnSwg==} - mjml-head-preview@5.0.0-alpha.4: - resolution: {integrity: sha512-cH2VaTVapSeYd+OIfeG7yQtZVDSGqV86iUE4UHasTFpaxcPigpaS5NzAiDL9f7Pzp83q/eL6tdc3r7jX7IHkBQ==} + mjml-head-preview@5.4.0: + resolution: {integrity: sha512-jXbbRIGPn7IiAq6M/KZpQMX+RjRN9dW4Az4QTyqL4PObqsrn+rbug6vdZXdxXnCERUZiA7a7K3R4Z5BTOi+qFw==} - mjml-head-style@5.0.0-alpha.4: - resolution: {integrity: sha512-7WAsEctOMFOsH8WYrJ/6ZZ2x+m4SKCdpgXWoJwcIVVXiwt/I9C0iGW5b82ZJh0jaGEH5i1dsKMcMcvKnHuiTog==} + mjml-head-style@5.4.0: + resolution: {integrity: sha512-wUPT4G8GjHlcy0Zq67taYT0E65pa6gwSxO43VJfjpU8Qa1QNmFYkOuDkzbb9oZN0QsETmbfG/RPK/mS4uOAfsg==} - mjml-head-title@5.0.0-alpha.4: - resolution: {integrity: sha512-GL/LKPkqbyCb0fRrf5NL0Xx/1xX0nF5dVQsmwfH7YdGM8Syx+ging2lrOhRxUic6NE0STXz5H16c0+oisU2HCQ==} + mjml-head-title@5.4.0: + resolution: {integrity: sha512-Tx4a6/CPDapUwq8NjGqfb3xBrzMXCxo3s1IGnd1Fnohl0uAZ5EySeBFf8c0uNSwrEhCDOTC5qrERm890Yselfw==} - mjml-head@5.0.0-alpha.4: - resolution: {integrity: sha512-QF+l4pCYbmTvFPz522k8hbzJgWGmOj16/bTwE+mhGueRRMGmVAp7gCqeNnI9PO/O8zTF7fisgseUuHmAWkCIFg==} + mjml-head@5.4.0: + resolution: {integrity: sha512-npWsul6ANzgxl2AZP0kG1yn9G5tOoX8iCgMhRwJkbIJ2rEX91SUvim9P/75s7HuqhccVsjO2L3OVHmnUEpWYng==} - mjml-hero@5.0.0-alpha.4: - resolution: {integrity: sha512-KNjc+uEuEs5edlQxkoLnSSQw302M+GSBuGYEO1kThiFeJavZvdCeV9W+bTdeM6i7Cbn+UjfJQPPVaAo+yT6ETg==} + mjml-hero@5.4.0: + resolution: {integrity: sha512-j9bKjilTHqJMp3GIDtKUdP3JRnktAc0o7gHhv1NgThgv/z1DDQJ2zgtW/pme6wA5VWng5DTriPk9aoCLPuOlyQ==} - mjml-image@5.0.0-alpha.4: - resolution: {integrity: sha512-9oQJOOav9dWQcl8lUnn0ZVHCKnV/4Z8G6roT5FZBF6yKoqMCcgCJ9Sfhp3KqRzDvTVAsTnM8EzNDC+tBImD6Og==} + mjml-image@5.4.0: + resolution: {integrity: sha512-6Y8aMIZbzIZ7SSpo0/o4n5E+JQx5d7OCwUoIIiXWPWGevfOE8JvjFt5MIa24CmSDbKWbhVuJEi1h4TUJhvAVbQ==} - mjml-navbar@5.0.0-alpha.4: - resolution: {integrity: sha512-cMgeW1SeSlqYuMe7knVk/PXkroLwdI/jBopXetJVWFSURJij9AHto6vKmd+/aFlfPC8oWKPBKvEieCwEDgk6Lg==} + mjml-navbar@5.4.0: + resolution: {integrity: sha512-knEsmNN6uBLtEU3a9uwnRqUqAE38EeJcXYaGlI0ly75Zj+vyhKQGPjJzRN7XJqA2dFSgIm3dV9KU9vNW+jI3Zg==} - mjml-parser-xml@5.0.0-alpha.4: - resolution: {integrity: sha512-pk2sWuaUgiX2CwbL2qsh1g7Ry110YQMnX84KuIcEnzOQaCyuvGtOGIXuOiOthLRbVnKz15P7EsNnwHRg/d/Ihw==} + mjml-parser-xml@5.4.0: + resolution: {integrity: sha512-A+KzRx+AeWIWxYN9z2KungvmVKMdW+NGLc5h+B9DeY93lSvcSpWH26nGDW9pcPi9B3fdwgYvqgOkYtX6EQATCA==} - mjml-preset-core@5.0.0-alpha.4: - resolution: {integrity: sha512-V5I+3NJoSV/pFia5MjP5u8BgqJwHqR4KigUjGtOr5chZljyehFNOeL8ghEZ551BCzMrtMzarnChsEnkHI1Qirw==} + mjml-preset-core@5.4.0: + resolution: {integrity: sha512-rq22rNFCp4brsSAgpKrY4tXR/ZWeJeU/GyypihrzmDVu3dSFmOYbrJgW1eCo4g5rWMpEbnY8pn0t1SB8EB+ymQ==} - mjml-raw@5.0.0-alpha.4: - resolution: {integrity: sha512-puCKbIuMVFlFyZx1vaKy45iS3iTgFpmFcah5C+E5VnEyKDOB6su6Fs8OnuAHkq+TIdGc6q9kqI1MwlRn0Mrr8w==} + mjml-raw@5.4.0: + resolution: {integrity: sha512-ewGXtauxkE35Xd9cJ3REjfD6vNSU82HfIm2pPi2RZF9eXrmfuSrrbSpXuPy13BP2lPpDJa6umUAuDsg5XhXr4A==} - mjml-section@5.0.0-alpha.4: - resolution: {integrity: sha512-sbXvB9ik9i1zueCj996LvmiGn7EsZR5E8KXu08My3YxRbIoQrZtYdVOFM//858zDXtE/HB39HcLVXt1sG7GLig==} + mjml-section@5.4.0: + resolution: {integrity: sha512-BetZqHS31bK5FS7rr5HlFo24m5OGDZi3yjkSn0aanF1SgRkmX1+LwnMQoDdT986SMys0oUYFeNSlz9lp8QgtrA==} - mjml-social@5.0.0-alpha.4: - resolution: {integrity: sha512-lP+ykZB0wppYulBv1q0xM3kFCoYaKLyROZJgDjzvMlBRUA+p21/nu4JEjqYGdq0gQqoAhLQGW8hOUnEnS0Aydw==} + mjml-social@5.4.0: + resolution: {integrity: sha512-7LUoIAOUzXzkLWfdErGrrqc8isxmDxaYQPcUNubQ1d9IXR48/S7Pi5s6PEiDxlaIgWHBcAJEoMszpLHA8+oCSQ==} - mjml-spacer@5.0.0-alpha.4: - resolution: {integrity: sha512-xHEunDOUL7Al3Rs5z20mwJsPllZdClriOptti5DP2hJjPkF2X/nwFTaH/kXvaPd2/CSZGHO+aQ5r/X2huV/43w==} + mjml-spacer@5.4.0: + resolution: {integrity: sha512-v7P6InD4u7nyXTmNjKMOY0oQ2EaZzFzCcftexb6JdcqvFaZvO9vUSoOdfZUp2FzzFcXQaD7K5RRW3stJSdEJOQ==} - mjml-table@5.0.0-alpha.4: - resolution: {integrity: sha512-TCh5IJ6IDkv0bkn/8r7GslEpDiRaRoUonHzbFbsi1rNojayg+oOJbaUhpMh1gvBzVlmAyMeX2XGA92A1EiqJjw==} + mjml-table@5.4.0: + resolution: {integrity: sha512-e1Kq3AWzzVFv6rPgAy4eK1txA4rfMPivaavW9BrvCDJU3Wiz+fOo9FqtMDyUQXrsJJKSOi6MMA0h2lAESTsR5w==} - mjml-text@5.0.0-alpha.4: - resolution: {integrity: sha512-yJi6D1hDaKxtJLu0M330yHn0BLo55T9+TaOw9GaWWlF28yphUZ6Ge+ppSZYeMbmzWwCUMVPPOcsYMpaLHtd7Iw==} + mjml-text@5.4.0: + resolution: {integrity: sha512-QTLdNM6Fs6T4LlquEzJmM+i3lJ+toNmRLRSZyUXP1tjJQhlNmc9aT1UHRy1Gn5fb7XTAY4lo2LznVv/0Tb3qhw==} - mjml-validator@5.0.0-alpha.4: - resolution: {integrity: sha512-0RWcTmUxluJc6XR/7Wmve9z4ydUGnLTUuyaHWX624V/xOaRPIThCllluh67TbSK6W2t4mwIHCdT+MgQC/wFwog==} + mjml-validator@5.4.0: + resolution: {integrity: sha512-IVsV3RxiEFfAed9U0C5DYmQA06e1JWDQQ8MqBinU7lQCYUkZIexTStohDACzHpN6RTIawi+U0GkT6PUHprv+3Q==} - mjml-wrapper@5.0.0-alpha.4: - resolution: {integrity: sha512-sISlNUC3EVj5YMZfdQw19B9AIwCmgT8XWJ5r6HsBfYtxwdeYBHA/stygx84lEjDYPJK7U5FL65u01vfP71vM/w==} + mjml-wrapper@5.4.0: + resolution: {integrity: sha512-niCuz5T7IfLKIKVv6G4BNu6sW07yl/kJ0o8oovd+CfG4lO9K+tXUwJQ+Iv3Y3tEQp0TfJE0aN1ysGrhAz6aB6Q==} - mjml@5.0.0-alpha.4: - resolution: {integrity: sha512-SUdO4F/XYtXkIYKgjC3hO2oplSllb3DRsHxdNNMuyYh0y2HMxVgqjCcViCBLKc8zJrWM4NO5deZwO+8NjLcM2Q==} + mjml@5.4.0: + resolution: {integrity: sha512-nKeUbKsNtSLzqKcmOwGh3ELxLYHY1fdeSNLif+A33uQV4zBdHahNL7LLshvpTpG2yyu5LlZHQWogVs1dS/V30w==} hasBin: true mkdirp@1.0.4: @@ -10715,8 +10828,8 @@ packages: mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} - mocha@11.7.6: - resolution: {integrity: sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==} + mocha@11.8.0: + resolution: {integrity: sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true @@ -10762,13 +10875,8 @@ packages: resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} engines: {node: '>=8.0.0'} - nanoid@3.3.14: - resolution: {integrity: sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -10800,6 +10908,10 @@ packages: resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} + node-addon-api@8.9.2: + resolution: {integrity: sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg==} + engines: {node: ^18 || ^20 || >= 21} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -10841,18 +10953,23 @@ packages: node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} - node-releases@2.0.51: - resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} engines: {node: '>=18'} nodemailer@9.0.1: resolution: {integrity: sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==} engines: {node: '>=6.0.0'} - nodemailer@9.0.3: - resolution: {integrity: sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==} + nodemailer@9.0.5: + resolution: {integrity: sha512-wvjiKvjczmsN7U/8006JOdXubgBk2XFAbioDMbT+sM7cPs0QrhJTa6KBRX7P5REGGkDcLUz/EarWidb8G8C1jQ==} engines: {node: '>=6.0.0'} + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -10943,6 +11060,10 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -10992,6 +11113,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + p-event@4.2.0: resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} engines: {node: '>=8'} @@ -11051,8 +11176,8 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - papaparse@5.5.4: - resolution: {integrity: sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==} + papaparse@5.6.0: + resolution: {integrity: sha512-N2vuNQAYGK1/4vs6HJX86+VYU6OkiSTgdJz3JQfTk1y51cFCO/U8gnaeTF4iNE4r57Tt0sV47dUua1/19pxO6Q==} param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} @@ -11080,6 +11205,9 @@ packages: parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -11179,9 +11307,6 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@8.4.0: - resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} - path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} @@ -11236,12 +11361,15 @@ packages: pg-protocol@1.15.0: resolution: {integrity: sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==} + pg-protocol@1.16.0: + resolution: {integrity: sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==} + pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.22.0: - resolution: {integrity: sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==} + pg@8.23.0: + resolution: {integrity: sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -11263,6 +11391,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + pidtree@0.3.1: resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} engines: {node: '>=0.10'} @@ -11306,61 +11438,73 @@ packages: resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} engines: {node: ^18.12 || ^20.9 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-colormin@7.0.6: resolution: {integrity: sha512-oXM2mdx6IBTRm39797QguYzVEWzbdlFiMNfq88fCCN1Wepw3CYmJ/1/Ifa/KjWo+j5ZURDl2NTldLJIw51IeNQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-convert-values@7.0.9: resolution: {integrity: sha512-l6uATQATZaCa0bckHV+r6dLXfWtUBKXxO3jK+AtxxJJtgMPD+VhhPCCx51I4/5w8U5uHV67g3w7PXj+V3wlMlg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-discard-comments@7.0.6: resolution: {integrity: sha512-Sq+Fzj1Eg5/CPf1ERb0wS1Im5cvE2gDXCE+si4HCn1sf+jpQZxDI4DXEp8t77B/ImzDceWE2ebJQFXdqZ6GRJw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 + + postcss-discard-comments@7.0.8: + resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: 8.5.26 postcss-discard-duplicates@7.0.2: resolution: {integrity: sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-discard-empty@7.0.1: resolution: {integrity: sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 + + postcss-discard-empty@7.0.3: + resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: 8.5.26 postcss-discard-overridden@7.0.1: resolution: {integrity: sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-js@4.1.0: resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-load-config@4.0.2: resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 ts-node: '>=9.0.0' peerDependenciesMeta: postcss: @@ -11373,7 +11517,7 @@ packages: engines: {node: '>= 18'} peerDependencies: jiti: '>=1.21.0' - postcss: 8.5.18 + postcss: 8.5.26 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: @@ -11390,145 +11534,147 @@ packages: resolution: {integrity: sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-merge-rules@7.0.8: resolution: {integrity: sha512-BOR1iAM8jnr7zoQSlpeBmCsWV5Uudi/+5j7k05D0O/WP3+OFMPD86c1j/20xiuRtyt45bhxw/7hnhZNhW2mNFA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-minify-font-values@7.0.1: resolution: {integrity: sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-minify-gradients@7.0.1: resolution: {integrity: sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-minify-params@7.0.6: resolution: {integrity: sha512-YOn02gC68JijlaXVuKvFSCvQOhTpblkcfDre2hb/Aaa58r2BIaK4AtE/cyZf2wV7YKAG+UlP9DT+By0ry1E4VQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-minify-selectors@7.0.6: resolution: {integrity: sha512-lIbC0jy3AAwDxEgciZlBullDiMBeBCT+fz5G8RcA9MWqh/hfUkpOI3vNDUNEZHgokaoiv0juB9Y8fGcON7rU/A==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-charset@7.0.1: resolution: {integrity: sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-display-values@7.0.1: resolution: {integrity: sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-positions@7.0.1: resolution: {integrity: sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-repeat-style@7.0.1: resolution: {integrity: sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-string@7.0.1: resolution: {integrity: sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-timing-functions@7.0.1: resolution: {integrity: sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-unicode@7.0.6: resolution: {integrity: sha512-z6bwTV84YW6ZvvNoaNLuzRW4/uWxDKYI1iIDrzk6D2YTL7hICApy+Q1LP6vBEsljX8FM7YSuV9qI79XESd4ddQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-url@7.0.1: resolution: {integrity: sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-normalize-whitespace@7.0.1: resolution: {integrity: sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 + + postcss-normalize-whitespace@7.0.3: + resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: 8.5.26 postcss-ordered-values@7.0.2: resolution: {integrity: sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-reduce-initial@7.0.6: resolution: {integrity: sha512-G6ZyK68AmrPdMB6wyeA37ejnnRG2S8xinJrZJnOv+IaRKf6koPAVbQsiC7MfkmXaGmF1UO+QCijb27wfpxuRNg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-reduce-transforms@7.0.1: resolution: {integrity: sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.1.4: - resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + postcss-selector-parser@7.1.5: + resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} engines: {node: '>=4'} postcss-svgo@7.1.1: resolution: {integrity: sha512-zU9H9oEDrUFKa0JB7w+IYL7Qs9ey1mZyjhbf0KLxwJDdDRtoPvCmaEfknzqfHj44QS9VD6c5sJnBAVYTLRg/Sg==} engines: {node: ^18.12.0 || ^20.9.0 || >= 18} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-unique-selectors@7.0.5: resolution: {integrity: sha512-3QoYmEt4qg/rUWDn6Tc8+ZVPmbp4G1hXDtCNWDx0st8SjtCbRcxRXDDM1QrEiXGG3A45zscSJFb4QH90LViyxg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.18: - resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.20: - resolution: {integrity: sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==} + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -11555,8 +11701,8 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - posthog-node@5.46.1: - resolution: {integrity: sha512-WjCqExq44pBdyg9MSsH6UAE0tNZ88p4aIuVFicgqhjf2Fbws6IhS4ioYUa4aBrbUPS9EDRXtBTtF5DpP1ml8Pw==} + posthog-node@5.50.0: + resolution: {integrity: sha512-7trcTN4EcPBiYAI1nnD1rk+VB+OcjgN0Mdnp7kOK09PTCSW0LCAUVAYj3FD7u8Ne5zYCnW/ESv6k3VU7sOsL5w==} engines: {node: ^20.20.0 || >=22.22.0} peerDependencies: rxjs: ^7.0.0 @@ -11576,8 +11722,8 @@ packages: resolution: {integrity: sha512-7Hc+IvlQ7hlaIfQFZnxlRl0jnpWq2qwibORBhQYIb0QbNtuicc5ZxvKkVT71HJ4Py1wSZ/3VR1r8LfkCtoCzhw==} engines: {node: '>=12.0.0'} - postman-collection@5.3.0: - resolution: {integrity: sha512-PMa5vRheqDFfS1bkRg8WBidWxunRA80sT5YNLP27YC5+ycyfiLMCwPnqQd1zfvxkGk04Pr9UronWmmgsbpsVyQ==} + postman-collection@5.3.1: + resolution: {integrity: sha512-+ixY4KEGerw3I5dE6obXgXx31na8URU5ODNIA6Rjkbt3/BUpNRk03pxUAZFHr5dDXwCikJSa7pt5o0x1QXT77w==} engines: {node: '>=18'} postman-url-encoder@3.0.8: @@ -11706,11 +11852,6 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.8.5: - resolution: {integrity: sha512-zxcTTCedNGJM4R8sj/Cq/F0W/c4iE0afWBcBwMTRtw4WHYP9TWkYjdiH3npPRUYsXQCPR0hTU9yjovOu+E6EQA==} - engines: {node: '>=14'} - hasBin: true - prettier@3.9.6: resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} engines: {node: '>=14'} @@ -11740,8 +11881,8 @@ packages: resolution: {integrity: sha512-nrdhnt+E9ClJ4khk9rNzqgsxubH7xSJSKoqXx/7aed2eghegNGNWkSGOelNgFgUtMz3LmKGks0waH2NuXWWmPg==} engines: {node: '>=14'} - prisma@7.9.0: - resolution: {integrity: sha512-isQTJEK4pyOlAVzm6kBUDjzgdsgs0A/snpB38ycTHeOHW34qfepP+ClQltgDXqjZBnXALhEtE4duh9L3tN5fHw==} + prisma@7.9.1: + resolution: {integrity: sha512-aPqePoZIqwlAchbgbFDO/wHqGB+7H1nj9gaM+OsL9h77S5S3TnLd9BgD3LnoeDikULo7cl2HSUrEyQ55Z7DYbg==} engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: @@ -11766,6 +11907,9 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -11840,10 +11984,6 @@ packages: resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} engines: {node: '>=16.0.0'} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} - engines: {node: '>=0.6'} - qs@6.15.3: resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} @@ -11896,8 +12036,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.7: - resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + react-is@19.2.8: + resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} react@19.2.4: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} @@ -11929,8 +12069,8 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - readdirp@5.0.0: - resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} redis-errors@1.2.0: @@ -12154,8 +12294,8 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.101.0: - resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + sass@1.103.1: + resolution: {integrity: sha512-9icZURbP51S6S0QGoyaeqk9uB06GNWxsFYWfH5RgpFgqK5FA8tJcM3AdVxrZEVJ7dz+L87nG95gBKf4VuaMHGw==} engines: {node: '>=20.19.0'} hasBin: true @@ -12282,10 +12422,6 @@ packages: should@13.2.3: resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -12298,10 +12434,6 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - side-channel@1.1.1: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} @@ -12400,10 +12532,9 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} + engines: {node: '>= 12'} sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} @@ -12461,8 +12592,8 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -12586,7 +12717,7 @@ packages: resolution: {integrity: sha512-I3f053GBLIiS5Fg6OMFhq/c+yW+5Hc2+1fgq7gElDMMSqwlRb3tBf2ef6ucLStYRpId4q//bQO1FjcyNyy4yDQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: 8.5.18 + postcss: 8.5.26 subscriptions-transport-ws@0.11.0: resolution: {integrity: sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==} @@ -12646,8 +12777,8 @@ packages: resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==} engines: {node: '>=10'} - swagger-ui-dist@5.32.8: - resolution: {integrity: sha512-dgMdWXIgnI4zX4OPhKEdWnlDODbgm8W3AX0Ivn/BBqcUh6xZsBxhZMnvk6DJyRz1BTrj8dPxtarmEGgkz30oyA==} + swagger-ui-dist@5.32.13: + resolution: {integrity: sha512-qQobzb3DeC2LeK0j3E8812Ef4aIq1y9flJxvZkimkqUC/w4u7wS+yCc+VakqGJLweUUBrI24effhwo8OsAvNAw==} swagger2openapi@7.0.8: resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} @@ -12765,8 +12896,8 @@ packages: resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} engines: {node: '>=18'} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} tinyglobby@0.2.15: @@ -12777,8 +12908,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} tippy.js@6.3.7: @@ -12834,9 +12965,6 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} @@ -12952,7 +13080,7 @@ packages: peerDependencies: '@microsoft/api-extractor': ^7.36.0 '@swc/core': ^1 - postcss: 8.5.18 + postcss: 8.5.26 typescript: '>=4.5.0' peerDependenciesMeta: '@microsoft/api-extractor': @@ -13023,8 +13151,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.61.1: - resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + typescript-eslint@8.67.0: + resolution: {integrity: sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -13080,6 +13208,10 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} + engines: {node: '>=18.17'} + unhead@2.1.12: resolution: {integrity: sha512-iTHdWD9ztTunOErtfUFk6Wr11BxvzumcYJ0CzaSCBUOEtg+DUZ9+gnE99i8QkLFT2q1rZD48BYYGXpOZVDLYkA==} @@ -13171,7 +13303,7 @@ packages: peerDependencies: '@babel/parser': ^7.15.8 '@nuxt/kit': ^3.2.2 || ^4.0.0 - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: '@babel/parser': optional: true @@ -13203,6 +13335,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + upper-case-first@2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} @@ -13241,16 +13379,16 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true @@ -13261,8 +13399,8 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - valibot@1.2.0: - resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + valibot@1.4.2: + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} peerDependencies: typescript: '>=5' peerDependenciesMeta: @@ -13408,14 +13546,14 @@ packages: vue-router: optional: true - vite-plugin-pwa@1.2.0: - resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} + vite-plugin-pwa@1.3.0: + resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} engines: {node: '>=16.0.0'} peerDependencies: '@vite-pwa/assets-generator': ^1.0.0 - vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - workbox-build: ^7.4.0 - workbox-window: ^7.4.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 peerDependenciesMeta: '@vite-pwa/assets-generator': optional: true @@ -13430,7 +13568,7 @@ packages: resolution: {integrity: sha512-uh6NW7lt+aOXujK4eHfiNbeo55K9OTuB7fnv+5RVc4OBn/cZull6ThXdYH03JzKanUfgt6QZ37NbbtJ0og59qw==} peerDependencies: vite: ^4.0.0 || ^5.0.0 - vue: 3.5.40 + vue: 3.5.41 vue-router: ^4.0.11 vite@3.2.11: @@ -13547,6 +13685,47 @@ packages: jsdom: optional: true + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} @@ -13563,7 +13742,7 @@ packages: hasBin: true peerDependencies: '@vue/composition-api': ^1.0.0-rc.1 - vue: 3.5.40 + vue: 3.5.41 peerDependenciesMeta: '@vue/composition-api': optional: true @@ -13574,22 +13753,22 @@ packages: peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-i18n@11.4.6: - resolution: {integrity: sha512-l0gE7Rfy0phCa5ChKYkOq543Wgd39BCK6hkktfr1Ed4D99oRkgPK9ffShASZdeC8OJxGfdWmpYoAaAH6iLEuIg==} + vue-i18n@11.4.8: + resolution: {integrity: sha512-0ULeHP6Z9CGvAm67S77ZEp41cfGXIREGL8qfhos2BMgcQQewtQcDKuojt6jjasAD/S8GwfTp2ySPmDSpwvrCMQ==} engines: {node: '>= 22'} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 vue-json-pretty@2.6.0: resolution: {integrity: sha512-glz1aBVS35EO8+S9agIl3WOQaW2cJZW192UVKTuGmryx01ZvOVWc4pR3t+5UcyY4jdOfBUgVHjcpRpcnjRhCAg==} engines: {node: '>= 10.0.0', npm: '>= 5.0.0'} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 vue-pdf-embed@2.1.5: resolution: {integrity: sha512-IGFVBYlnOz2zSql1zk4YJyBu584EZa6RUykk5f8wkHF/AR31khCa+ruJoRag+Ff2UyntkWu0brENIKoikQ7F8g==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 vue-promise-modals@0.1.0: resolution: {integrity: sha512-LmPejeqvZSkxj4KkJe6ZUEJmCUQXVeEAj9ihTX+BRFfZftVCZSZd3B4uuZSKF0iCeQUemkodXUZFxcsNT/2dmg==} @@ -13597,7 +13776,7 @@ packages: vue-router@4.6.4: resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 vue-template-compiler@2.7.16: resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} @@ -13605,7 +13784,7 @@ packages: vue-tippy@6.7.1: resolution: {integrity: sha512-gdHbBV5/Vc8gH87hQHLA7TN1K4BlLco3MAPrTb70ZYGXxx+55rAU4a4mt0fIoP+gB3etu1khUZ6c29Br1n0CiA==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 vue-tsc@1.8.8: resolution: {integrity: sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==} @@ -13625,8 +13804,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.40: - resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==} + vue@3.5.41: + resolution: {integrity: sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -13636,7 +13815,7 @@ packages: vuedraggable-es@4.1.1: resolution: {integrity: sha512-F35pjSwC8HS/lnaOd+B59nYR4FZmwuhWAzccK9xftRuWds8SU1TZh5myKVM86j5dFOI7S26O64Kwe7LUHnXjlA==} peerDependencies: - vue: 3.5.40 + vue: 3.5.41 w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -13655,8 +13834,8 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-resource-inliner@6.0.1: - resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} + web-resource-inliner@8.0.0: + resolution: {integrity: sha512-Ezr98sqXW/+OCGoUEXuOKVR+oVFlSdn1tIySEEJdiSAw4IjrW8hQkwARSSBJTSB5Us5dnytDgL0ZDliAYBhaNA==} engines: {node: '>=10.0.0'} web-streams-polyfill@3.3.3: @@ -13669,9 +13848,6 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} @@ -13702,6 +13878,11 @@ packages: engines: {node: '>=12'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -13717,9 +13898,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -13872,10 +14050,6 @@ packages: resolution: {integrity: sha512-+8qTc3zv2UcJ1v9IsSIce37Dl4MQG14Cp7tWrwmy202UaI1wqRukw5QMX1JHsV+DX64yw77EgGsj2s5wGvuMbQ==} engines: {node: '>= 16'} - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -14016,6 +14190,14 @@ snapshots: optionalDependencies: graphql: 16.14.0 + '@0no-co/graphql.web@1.3.4(graphql@16.13.2)': + optionalDependencies: + graphql: 16.13.2 + + '@0no-co/graphql.web@1.3.4(graphql@16.14.0)': + optionalDependencies: + graphql: 16.14.0 + '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/7c5d9c23b73f2d22bed4c3198f36e2cfd5799a33': dependencies: '@tauri-apps/api': 2.9.1 @@ -14096,14 +14278,14 @@ snapshots: '@apidevtools/json-schema-ref-parser@14.0.1': dependencies: '@types/json-schema': 7.0.15 - js-yaml: 4.3.0 + js-yaml: 4.3.1 '@apidevtools/json-schema-ref-parser@9.1.2': dependencies: '@jsdevtools/ono': 7.1.3 '@types/json-schema': 7.0.15 call-me-maybe: 1.0.2 - js-yaml: 4.3.0 + js-yaml: 4.3.1 '@apidevtools/openapi-schemas@2.1.0': {} @@ -14154,7 +14336,7 @@ snapshots: '@protobufjs/inquire': 1.1.0 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 + '@protobufjs/utf8': 1.1.2 '@types/long': 4.0.2 long: 4.0.0 @@ -14185,7 +14367,7 @@ snapshots: '@apollo/utils.withrequired': 3.0.0 '@graphql-tools/schema': 10.0.33(graphql@16.14.0) async-retry: 1.3.3 - body-parser: 2.2.1 + body-parser: 2.3.0 content-type: 1.0.5 cors: 2.8.6 finalhandler: 2.1.1 @@ -14258,8 +14440,8 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@babel/parser': 7.29.7 - '@babel/runtime': 7.29.2 + '@babel/parser': 7.29.8 + '@babel/runtime': 7.29.7 '@babel/traverse': 7.29.0 '@babel/types': 7.29.7 babel-preset-fbjs: 3.4.0(@babel/core@7.29.0) @@ -14282,8 +14464,8 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@babel/parser': 7.29.7 - '@babel/runtime': 7.29.2 + '@babel/parser': 7.29.8 + '@babel/runtime': 7.29.7 '@babel/traverse': 7.29.0 '@babel/types': 7.29.7 babel-preset-fbjs: 3.4.0(@babel/core@7.29.0) @@ -14304,14 +14486,14 @@ snapshots: '@ardatan/relay-compiler@13.0.1(graphql@16.13.2)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 graphql: 16.13.2 immutable: 5.1.6 invariant: 2.2.4 '@ardatan/relay-compiler@13.0.1(graphql@16.14.0)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 graphql: 16.14.0 immutable: 5.1.6 invariant: 2.2.4 @@ -14384,14 +14566,14 @@ snapshots: '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@8.1.1) @@ -14409,27 +14591,27 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/generator@7.29.7': + '@babel/generator@7.29.8': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-compilation-targets@7.28.6': dependencies: '@babel/compat-data': 7.29.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.2 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -14437,7 +14619,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.7 + browserslist: 4.28.8 lru-cache: 5.1.1 semver: 6.3.1 @@ -14462,7 +14644,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.29.7 '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -14517,28 +14699,28 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: '@babel/traverse': 7.29.0 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-member-expression-to-functions@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.28.6': dependencies: '@babel/traverse': 7.29.0 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -14556,17 +14738,17 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-plugin-utils@7.28.6': {} @@ -14586,7 +14768,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-wrap-function': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -14604,21 +14786,21 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-member-expression-to-functions': 7.29.7 '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.29.0 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -14634,15 +14816,15 @@ snapshots: dependencies: '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-wrap-function@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -14654,12 +14836,16 @@ snapshots: '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -14672,7 +14858,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -14734,7 +14920,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -14904,7 +15090,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -14998,7 +15184,7 @@ snapshots: '@babel/helper-globals': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -15026,7 +15212,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -15146,7 +15332,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -15232,13 +15418,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-systemjs@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -15318,7 +15504,7 @@ snapshots: '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -15439,7 +15625,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-regenerator@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 @@ -15484,7 +15670,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-spread@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 @@ -15686,7 +15872,7 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-systemjs': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) @@ -15700,11 +15886,11 @@ snapshots: '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) @@ -15716,7 +15902,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) - core-js-compat: 3.49.0 + core-js-compat: 3.50.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -15735,8 +15921,6 @@ snapshots: '@babel/types': 7.29.7 esutils: 2.0.3 - '@babel/runtime@7.29.2': {} - '@babel/runtime@7.29.7': {} '@babel/standalone@7.29.2': {} @@ -15750,29 +15934,29 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.28.6 '@babel/types': 7.29.7 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/traverse@7.29.7': + '@babel/traverse@7.29.8': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -15787,13 +15971,18 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@0.2.3': {} '@borewit/text-codec@0.2.2': {} - '@boringer-avatars/vue3@0.2.1(vue@3.5.40(typescript@5.9.3))': + '@boringer-avatars/vue3@0.2.1(vue@3.5.41(typescript@5.9.3))': dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) '@codemirror/autocomplete@6.20.0': dependencies: @@ -16368,9 +16557,19 @@ snapshots: '@esbuild/win32-x64@0.27.4': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.8.1(jiti@2.6.1))': dependencies: - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.2(jiti@2.6.1))': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@10.8.1(jiti@2.6.1))': + dependencies: + eslint: 10.8.1(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': @@ -16400,7 +16599,7 @@ snapshots: dependencies: '@eslint/core': 0.17.0 - '@eslint/config-helpers@0.6.0': + '@eslint/config-helpers@0.7.0': dependencies: '@eslint/core': 1.2.1 @@ -16412,37 +16611,23 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': - dependencies: - ajv: 6.14.0 - debug: 4.4.3(supports-color@8.1.1) - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.3.0 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - '@eslint/eslintrc@3.3.6': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.3.1 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.7.0(jiti@2.6.1))': + '@eslint/js@10.0.1(eslint@10.8.1(jiti@2.6.1))': optionalDependencies: - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) '@eslint/js@9.39.2': {} @@ -16460,9 +16645,9 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@exodus/bytes@1.15.0(@noble/hashes@2.2.0)': + '@exodus/bytes@1.15.0(@noble/hashes@2.3.0)': optionalDependencies: - '@noble/hashes': 2.2.0 + '@noble/hashes': 2.3.0 '@exodus/schemasafe@1.3.0': {} @@ -16476,7 +16661,7 @@ snapshots: '@fontsource-variable/material-symbols-rounded@5.2.45': {} - '@fontsource-variable/material-symbols-rounded@5.3.0': {} + '@fontsource-variable/material-symbols-rounded@5.3.3': {} '@fontsource-variable/roboto-mono@5.2.9': {} @@ -16496,7 +16681,7 @@ snapshots: graphql: 16.14.0 tslib: 2.8.1 - '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.5.6)(@types/node@24.10.1)(graphql@16.13.2)(typescript@5.9.3)': + '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.6.0)(@types/node@24.10.1)(graphql@16.13.2)(typescript@5.9.3)': dependencies: '@babel/generator': 7.29.1 '@babel/template': 7.28.6 @@ -16535,7 +16720,7 @@ snapshots: yaml: 2.8.3 yargs: 17.7.2 optionalDependencies: - '@parcel/watcher': 2.5.6 + '@parcel/watcher': 2.6.0 transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -16547,7 +16732,7 @@ snapshots: - typescript - utf-8-validate - '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.3)(graphql@16.13.2)(typescript@5.9.3)': + '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.6.0)(@types/node@25.9.3)(graphql@16.13.2)(typescript@5.9.3)': dependencies: '@babel/generator': 7.29.1 '@babel/template': 7.28.6 @@ -16586,7 +16771,7 @@ snapshots: yaml: 2.8.3 yargs: 17.7.2 optionalDependencies: - '@parcel/watcher': 2.5.6 + '@parcel/watcher': 2.6.0 transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -16598,7 +16783,7 @@ snapshots: - typescript - utf-8-validate - '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.5.6)(@types/node@25.9.3)(graphql@16.14.0)(typescript@5.9.3)': + '@graphql-codegen/cli@6.3.1(@parcel/watcher@2.6.0)(@types/node@25.9.3)(graphql@16.14.0)(typescript@5.9.3)': dependencies: '@babel/generator': 7.29.1 '@babel/template': 7.28.6 @@ -16637,7 +16822,7 @@ snapshots: yaml: 2.8.3 yargs: 17.7.2 optionalDependencies: - '@parcel/watcher': 2.5.6 + '@parcel/watcher': 2.6.0 transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -17441,6 +17626,12 @@ snapshots: graphql: 16.14.0 tslib: 2.8.1 + '@graphql-tools/merge@9.2.3(graphql@16.14.0)': + dependencies: + '@graphql-tools/utils': 12.0.0(graphql@16.14.0) + graphql: 16.14.0 + tslib: 2.8.1 + '@graphql-tools/optimize@1.4.0(graphql@16.13.2)': dependencies: graphql: 16.13.2 @@ -17509,6 +17700,13 @@ snapshots: graphql: 16.14.0 tslib: 2.8.1 + '@graphql-tools/schema@10.1.0(graphql@16.14.0)': + dependencies: + '@graphql-tools/merge': 9.2.3(graphql@16.14.0) + '@graphql-tools/utils': 12.0.0(graphql@16.14.0) + graphql: 16.14.0 + tslib: 2.8.1 + '@graphql-tools/schema@9.0.19(graphql@16.13.2)': dependencies: '@graphql-tools/merge': 8.4.2(graphql@16.13.2) @@ -17637,6 +17835,14 @@ snapshots: graphql: 16.14.0 tslib: 2.8.1 + '@graphql-tools/utils@12.0.0(graphql@16.14.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.14.0 + tslib: 2.8.1 + '@graphql-tools/utils@9.2.1(graphql@16.13.2)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.2) @@ -17684,12 +17890,12 @@ snapshots: dependencies: graphql: 16.14.0 - '@guolao/vue-monaco-editor@1.6.0(monaco-editor@0.55.1)(vue@3.5.40(typescript@5.9.3))': + '@guolao/vue-monaco-editor@1.6.0(monaco-editor@0.55.1)(vue@3.5.41(typescript@5.9.3))': dependencies: '@monaco-editor/loader': 1.7.0 monaco-editor: 0.55.1 - vue: 3.5.40(typescript@5.9.3) - vue-demi: 0.14.10(vue@3.5.40(typescript@5.9.3)) + vue: 3.5.41(typescript@5.9.3) + vue-demi: 0.14.10(vue@3.5.41(typescript@5.9.3)) '@hapi/b64@5.0.0': dependencies: @@ -17714,23 +17920,23 @@ snapshots: stringify-object: 3.3.0 yargs: 17.7.2 - '@hoppscotch/ui@0.2.6(eslint@10.7.0(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@hoppscotch/ui@0.2.6(eslint@10.8.1(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@boringer-avatars/vue3': 0.2.1(vue@3.5.40(typescript@5.9.3)) + '@boringer-avatars/vue3': 0.2.1(vue@3.5.41(typescript@5.9.3)) '@fontsource-variable/inter': 5.2.8 '@fontsource-variable/material-symbols-rounded': 5.2.45 '@fontsource-variable/roboto-mono': 5.2.9 '@hoppscotch/vue-sonner': 1.2.3 - '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.40(typescript@5.9.3)) - '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - '@vueuse/core': 8.9.4(vue@3.5.40(typescript@5.9.3)) + '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.41(typescript@5.9.3)) + '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vueuse/core': 8.9.4(vue@3.5.41(typescript@5.9.3)) fp-ts: 2.16.11 lodash-es: 4.18.1 path: 0.12.7 - vite-plugin-eslint: 1.8.1(eslint@10.7.0(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - vue: 3.5.40(typescript@5.9.3) + vite-plugin-eslint: 1.8.1(eslint@10.8.1(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + vue: 3.5.41(typescript@5.9.3) vue-promise-modals: 0.1.0(typescript@5.9.3) - vuedraggable-es: 4.1.1(vue@3.5.40(typescript@5.9.3)) + vuedraggable-es: 4.1.1(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - eslint @@ -17738,23 +17944,23 @@ snapshots: - typescript - vite - '@hoppscotch/ui@0.2.6(eslint@10.7.0(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@hoppscotch/ui@0.2.6(eslint@10.8.1(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@boringer-avatars/vue3': 0.2.1(vue@3.5.40(typescript@5.9.3)) + '@boringer-avatars/vue3': 0.2.1(vue@3.5.41(typescript@5.9.3)) '@fontsource-variable/inter': 5.2.8 '@fontsource-variable/material-symbols-rounded': 5.2.45 '@fontsource-variable/roboto-mono': 5.2.9 '@hoppscotch/vue-sonner': 1.2.3 - '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.40(typescript@5.9.3)) - '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - '@vueuse/core': 8.9.4(vue@3.5.40(typescript@5.9.3)) + '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.41(typescript@5.9.3)) + '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vueuse/core': 8.9.4(vue@3.5.41(typescript@5.9.3)) fp-ts: 2.16.11 lodash-es: 4.18.1 path: 0.12.7 - vite-plugin-eslint: 1.8.1(eslint@10.7.0(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - vue: 3.5.40(typescript@5.9.3) + vite-plugin-eslint: 1.8.1(eslint@10.8.1(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + vue: 3.5.41(typescript@5.9.3) vue-promise-modals: 0.1.0(typescript@5.9.3) - vuedraggable-es: 4.1.1(vue@3.5.40(typescript@5.9.3)) + vuedraggable-es: 4.1.1(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - eslint @@ -17762,23 +17968,23 @@ snapshots: - typescript - vite - '@hoppscotch/ui@0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@hoppscotch/ui@0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@boringer-avatars/vue3': 0.2.1(vue@3.5.40(typescript@5.9.3)) + '@boringer-avatars/vue3': 0.2.1(vue@3.5.41(typescript@5.9.3)) '@fontsource-variable/inter': 5.2.8 '@fontsource-variable/material-symbols-rounded': 5.2.45 '@fontsource-variable/roboto-mono': 5.2.9 '@hoppscotch/vue-sonner': 1.2.3 - '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.40(typescript@5.9.3)) - '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - '@vueuse/core': 8.9.4(vue@3.5.40(typescript@5.9.3)) + '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.41(typescript@5.9.3)) + '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vueuse/core': 8.9.4(vue@3.5.41(typescript@5.9.3)) fp-ts: 2.16.11 lodash-es: 4.18.1 path: 0.12.7 - vite-plugin-eslint: 1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - vue: 3.5.40(typescript@5.9.3) + vite-plugin-eslint: 1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + vue: 3.5.41(typescript@5.9.3) vue-promise-modals: 0.1.0(typescript@5.9.3) - vuedraggable-es: 4.1.1(vue@3.5.40(typescript@5.9.3)) + vuedraggable-es: 4.1.1(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - eslint @@ -17786,23 +17992,23 @@ snapshots: - typescript - vite - '@hoppscotch/ui@0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@hoppscotch/ui@0.2.6(eslint@9.39.2(jiti@2.6.1))(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@boringer-avatars/vue3': 0.2.1(vue@3.5.40(typescript@5.9.3)) + '@boringer-avatars/vue3': 0.2.1(vue@3.5.41(typescript@5.9.3)) '@fontsource-variable/inter': 5.2.8 '@fontsource-variable/material-symbols-rounded': 5.2.45 '@fontsource-variable/roboto-mono': 5.2.9 '@hoppscotch/vue-sonner': 1.2.3 - '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.40(typescript@5.9.3)) - '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - '@vueuse/core': 8.9.4(vue@3.5.40(typescript@5.9.3)) + '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.41(typescript@5.9.3)) + '@vitejs/plugin-legacy': 2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vueuse/core': 8.9.4(vue@3.5.41(typescript@5.9.3)) fp-ts: 2.16.11 lodash-es: 4.18.1 path: 0.12.7 - vite-plugin-eslint: 1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - vue: 3.5.40(typescript@5.9.3) + vite-plugin-eslint: 1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + vue: 3.5.41(typescript@5.9.3) vue-promise-modals: 0.1.0(typescript@5.9.3) - vuedraggable-es: 4.1.1(vue@3.5.40(typescript@5.9.3)) + vuedraggable-es: 4.1.1(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/composition-api' - eslint @@ -17812,9 +18018,9 @@ snapshots: '@hoppscotch/vue-sonner@1.2.3': {} - '@hoppscotch/vue-toasted@0.1.0(vue@3.5.40(typescript@5.9.3))': + '@hoppscotch/vue-toasted@0.1.0(vue@3.5.41(typescript@5.9.3))': dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) '@humanfs/core@0.19.1': {} @@ -17827,11 +18033,7 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@iconify-json/lucide@1.2.114': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/lucide@1.2.118': + '@iconify-json/lucide@1.2.125': dependencies: '@iconify/types': 2.0.0 @@ -17958,14 +18160,14 @@ snapshots: '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 optionalDependencies: '@types/node': 24.10.1 '@inquirer/external-editor@1.0.3(@types/node@25.9.3)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 optionalDependencies: '@types/node': 25.9.3 @@ -18122,11 +18324,11 @@ snapshots: optionalDependencies: '@types/node': 25.9.3 - '@intlify/bundle-utils@11.2.4(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))': + '@intlify/bundle-utils@11.2.5(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))': dependencies: - '@intlify/message-compiler': 11.4.6 - '@intlify/shared': 11.4.6 - acorn: 8.17.0 + '@intlify/message-compiler': 11.4.8 + '@intlify/shared': 11.4.8 + acorn: 8.18.0 esbuild: 0.25.12 escodegen: 2.1.0 estree-walker: 2.0.2 @@ -18134,44 +18336,44 @@ snapshots: source-map-js: 1.2.1 yaml-eslint-parser: 1.3.2 optionalDependencies: - vue-i18n: 11.4.6(vue@3.5.40(typescript@5.9.3)) + vue-i18n: 11.4.8(vue@3.5.41(typescript@5.9.3)) - '@intlify/core-base@11.4.6': + '@intlify/core-base@11.4.8': dependencies: - '@intlify/devtools-types': 11.4.6 - '@intlify/message-compiler': 11.4.6 - '@intlify/shared': 11.4.6 + '@intlify/devtools-types': 11.4.8 + '@intlify/message-compiler': 11.4.8 + '@intlify/shared': 11.4.8 - '@intlify/devtools-types@11.4.6': + '@intlify/devtools-types@11.4.8': dependencies: - '@intlify/core-base': 11.4.6 - '@intlify/shared': 11.4.6 + '@intlify/core-base': 11.4.8 + '@intlify/shared': 11.4.8 - '@intlify/message-compiler@11.4.6': + '@intlify/message-compiler@11.4.8': dependencies: - '@intlify/shared': 11.4.6 + '@intlify/shared': 11.4.8 source-map-js: 1.2.1 - '@intlify/shared@11.4.6': {} + '@intlify/shared@11.4.8': {} - '@intlify/unplugin-vue-i18n@11.2.4(@vue/compiler-dom@3.5.40)(eslint@10.7.0(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.2.5(@vue/compiler-dom@3.5.41)(eslint@10.8.1(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.6.1)) - '@intlify/bundle-utils': 11.2.4(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3))) - '@intlify/shared': 11.4.6 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.40)(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1(jiti@2.6.1)) + '@intlify/bundle-utils': 11.2.5(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3))) + '@intlify/shared': 11.4.8 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.8)(@vue/compiler-dom@3.5.41)(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@rollup/pluginutils': 5.4.0(rollup@4.60.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 pathe: 2.0.3 picocolors: 1.1.1 unplugin: 2.3.11 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) optionalDependencies: - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue-i18n: 11.4.6(vue@3.5.40(typescript@5.9.3)) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue-i18n: 11.4.8(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -18179,24 +18381,24 @@ snapshots: - supports-color - typescript - '@intlify/unplugin-vue-i18n@11.2.4(@vue/compiler-dom@3.5.40)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.2.5(@vue/compiler-dom@3.5.41)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@intlify/bundle-utils': 11.2.4(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3))) - '@intlify/shared': 11.4.6 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.40)(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@2.6.1)) + '@intlify/bundle-utils': 11.2.5(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3))) + '@intlify/shared': 11.4.8 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.8)(@vue/compiler-dom@3.5.41)(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@rollup/pluginutils': 5.4.0(rollup@4.60.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 pathe: 2.0.3 picocolors: 1.1.1 unplugin: 2.3.11 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue-i18n: 11.4.6(vue@3.5.40(typescript@5.9.3)) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue-i18n: 11.4.8(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -18204,24 +18406,24 @@ snapshots: - supports-color - typescript - '@intlify/unplugin-vue-i18n@11.2.4(@vue/compiler-dom@3.5.40)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.2.5(@vue/compiler-dom@3.5.41)(eslint@9.39.2(jiti@2.6.1))(rollup@4.60.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@intlify/bundle-utils': 11.2.4(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3))) - '@intlify/shared': 11.4.6 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.40)(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@2.6.1)) + '@intlify/bundle-utils': 11.2.5(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3))) + '@intlify/shared': 11.4.8 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.8)(@vue/compiler-dom@3.5.41)(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)) '@rollup/pluginutils': 5.4.0(rollup@4.60.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 pathe: 2.0.3 picocolors: 1.1.1 unplugin: 2.3.11 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) optionalDependencies: - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue-i18n: 11.4.6(vue@3.5.40(typescript@5.9.3)) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue-i18n: 11.4.8(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -18229,14 +18431,14 @@ snapshots: - supports-color - typescript - '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.40)(vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.8)(@vue/compiler-dom@3.5.41)(vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3))': dependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 optionalDependencies: - '@intlify/shared': 11.4.6 - '@vue/compiler-dom': 3.5.40 - vue: 3.5.40(typescript@5.9.3) - vue-i18n: 11.4.6(vue@3.5.40(typescript@5.9.3)) + '@intlify/shared': 11.4.8 + '@vue/compiler-dom': 3.5.41 + vue: 3.5.41(typescript@5.9.3) + vue-i18n: 11.4.8(vue@3.5.41(typescript@5.9.3)) '@ioredis/commands@1.5.1': optional: true @@ -18633,29 +18835,28 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@nestjs-modules/mailer@2.3.7(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/terminus@11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2))(chokidar@4.0.3)(nodemailer@9.0.3)(terser@5.46.1)(typescript@5.9.3)': + '@nestjs-modules/mailer@2.3.7(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/terminus@11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2))(chokidar@4.0.3)(nodemailer@9.0.5)(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3)': dependencies: '@css-inline/css-inline': 0.20.0 - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) glob: 13.0.6 - nodemailer: 9.0.3 + nodemailer: 9.0.5 tslib: 2.8.1 optionalDependencies: - '@nestjs/terminus': 11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/terminus': 11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/ejs': 3.1.5 '@types/mjml': 4.7.4 '@types/pug': 2.0.10 ejs: 5.0.2 handlebars: 4.7.9 liquidjs: 10.27.1 - mjml: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) nunjucks: 3.2.4(chokidar@4.0.3) preview-email: 3.1.1 pug: 3.0.4 transitivePeerDependencies: - chokidar - - encoding - purgecss - relateurl - srcset @@ -18664,13 +18865,13 @@ snapshots: - typescript - uncss - '@nestjs/apollo@13.4.2(@apollo/server@5.5.1(graphql@16.14.0))(@as-integrations/express5@1.1.2(@apollo/server@5.5.1(graphql@16.14.0))(express@5.2.1))(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/graphql@13.4.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2))(graphql@16.14.0)': + '@nestjs/apollo@13.4.5(@apollo/server@5.5.1(graphql@16.14.0))(@as-integrations/express5@1.1.2(@apollo/server@5.5.1(graphql@16.14.0))(express@5.2.1))(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/graphql@13.4.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2))(graphql@16.14.0)': dependencies: '@apollo/server': 5.5.1(graphql@16.14.0) '@apollo/server-plugin-landing-page-graphql-playground': 4.0.1(@apollo/server@5.5.1(graphql@16.14.0)) - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/graphql': 13.4.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/graphql': 13.4.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2) graphql: 16.14.0 iterall: 1.3.0 lodash.omit: 4.18.0 @@ -18705,7 +18906,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: file-type: 21.3.4 iterare: 1.2.1 @@ -18720,17 +18921,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 17.4.1 dotenv-expand: 12.0.3 lodash: 4.18.1 rxjs: 7.8.2 - '@nestjs/core@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 8.4.2 @@ -18739,25 +18940,25 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + '@nestjs/platform-express': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1) - '@nestjs/graphql@13.4.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2)': + '@nestjs/graphql@13.4.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(graphql@16.14.0)(reflect-metadata@0.2.2)': dependencies: - '@graphql-tools/merge': 9.1.9(graphql@16.14.0) - '@graphql-tools/schema': 10.0.33(graphql@16.14.0) - '@graphql-tools/utils': 11.1.0(graphql@16.14.0) - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + '@graphql-tools/merge': 9.2.3(graphql@16.14.0) + '@graphql-tools/schema': 10.1.0(graphql@16.14.0) + '@graphql-tools/utils': 12.0.0(graphql@16.14.0) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) chokidar: 4.0.3 - fast-glob: 3.3.3 graphql: 16.14.0 graphql-tag: 2.12.6(graphql@16.14.0) - graphql-ws: 6.0.8(graphql@16.14.0)(ws@8.21.0) + graphql-ws: 6.2.1(graphql@16.14.0)(ws@8.21.0) lodash: 4.18.1 normalize-path: 3.0.0 reflect-metadata: 0.2.2 subscriptions-transport-ws: 0.11.0(graphql@16.14.0) + tinyglobby: 0.2.17 tslib: 2.8.1 ws: 8.21.0 optionalDependencies: @@ -18769,29 +18970,29 @@ snapshots: - crossws - utf-8-validate - '@nestjs/jwt@11.0.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': + '@nestjs/jwt@11.0.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 - '@nestjs/mapped-types@2.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': + '@nestjs/mapped-types@2.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.15.1 - '@nestjs/passport@11.0.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': + '@nestjs/passport@11.0.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) passport: 0.7.0 - '@nestjs/platform-express@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)': + '@nestjs/platform-express@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) cors: 2.8.6 express: 5.2.1 multer: 2.2.0 @@ -18800,10 +19001,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/schedule@6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)': + '@nestjs/schedule@6.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.4.0 '@nestjs/schematics@11.1.0(chokidar@4.0.3)(prettier@3.9.6)(typescript@5.9.3)': @@ -18819,53 +19020,53 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@11.4.6(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': + '@nestjs/swagger@11.4.7(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': dependencies: '@microsoft/tsdoc': 0.16.0 - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) - js-yaml: 5.2.2 + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + js-yaml: 5.3.0 lodash: 4.18.1 path-to-regexp: 8.4.2 reflect-metadata: 0.2.2 - swagger-ui-dist: 5.32.8 + swagger-ui-dist: 5.32.13 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.15.1 - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) boxen: 5.1.2 check-disk-space: 3.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 optionalDependencies: - '@prisma/client': 7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) + '@prisma/client': 7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - '@nestjs/testing@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/platform-express@11.1.28)': + '@nestjs/testing@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(@nestjs/platform-express@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-express': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + '@nestjs/platform-express': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1) - '@nestjs/throttler@6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(reflect-metadata@0.2.2)': + '@nestjs/throttler@6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 - '@noble/curves@2.2.0': + '@noble/curves@2.3.0': dependencies: - '@noble/hashes': 2.2.0 + '@noble/hashes': 2.3.0 '@noble/hashes@1.8.0': {} - '@noble/hashes@2.2.0': {} + '@noble/hashes@2.3.0': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -18879,6 +19080,9 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@one-ini/wasm@0.1.1': + optional: true + '@oozcitak/dom@2.0.2': dependencies: '@oozcitak/infra': 2.0.2 @@ -18900,65 +19104,61 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 - '@parcel/watcher-android-arm64@2.5.6': - optional: true - - '@parcel/watcher-darwin-arm64@2.5.6': + '@parcel/watcher-android-arm64@2.6.0': optional: true - '@parcel/watcher-darwin-x64@2.5.6': + '@parcel/watcher-darwin-arm64@2.6.0': optional: true - '@parcel/watcher-freebsd-x64@2.5.6': + '@parcel/watcher-darwin-x64@2.6.0': optional: true - '@parcel/watcher-linux-arm-glibc@2.5.6': + '@parcel/watcher-freebsd-x64@2.6.0': optional: true - '@parcel/watcher-linux-arm-musl@2.5.6': + '@parcel/watcher-linux-arm-glibc@2.6.0': optional: true - '@parcel/watcher-linux-arm64-glibc@2.5.6': + '@parcel/watcher-linux-arm-musl@2.6.0': optional: true - '@parcel/watcher-linux-arm64-musl@2.5.6': + '@parcel/watcher-linux-arm64-glibc@2.6.0': optional: true - '@parcel/watcher-linux-x64-glibc@2.5.6': + '@parcel/watcher-linux-arm64-musl@2.6.0': optional: true - '@parcel/watcher-linux-x64-musl@2.5.6': + '@parcel/watcher-linux-x64-glibc@2.6.0': optional: true - '@parcel/watcher-win32-arm64@2.5.6': + '@parcel/watcher-linux-x64-musl@2.6.0': optional: true - '@parcel/watcher-win32-ia32@2.5.6': + '@parcel/watcher-win32-arm64@2.6.0': optional: true - '@parcel/watcher-win32-x64@2.5.6': + '@parcel/watcher-win32-x64@2.6.0': optional: true - '@parcel/watcher@2.5.6': + '@parcel/watcher@2.6.0': dependencies: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.6 - '@parcel/watcher-darwin-arm64': 2.5.6 - '@parcel/watcher-darwin-x64': 2.5.6 - '@parcel/watcher-freebsd-x64': 2.5.6 - '@parcel/watcher-linux-arm-glibc': 2.5.6 - '@parcel/watcher-linux-arm-musl': 2.5.6 - '@parcel/watcher-linux-arm64-glibc': 2.5.6 - '@parcel/watcher-linux-arm64-musl': 2.5.6 - '@parcel/watcher-linux-x64-glibc': 2.5.6 - '@parcel/watcher-linux-x64-musl': 2.5.6 - '@parcel/watcher-win32-arm64': 2.5.6 - '@parcel/watcher-win32-ia32': 2.5.6 - '@parcel/watcher-win32-x64': 2.5.6 + '@parcel/watcher-android-arm64': 2.6.0 + '@parcel/watcher-darwin-arm64': 2.6.0 + '@parcel/watcher-darwin-x64': 2.6.0 + '@parcel/watcher-freebsd-x64': 2.6.0 + '@parcel/watcher-linux-arm-glibc': 2.6.0 + '@parcel/watcher-linux-arm-musl': 2.6.0 + '@parcel/watcher-linux-arm64-glibc': 2.6.0 + '@parcel/watcher-linux-arm64-musl': 2.6.0 + '@parcel/watcher-linux-x64-glibc': 2.6.0 + '@parcel/watcher-linux-x64-musl': 2.6.0 + '@parcel/watcher-win32-arm64': 2.6.0 + '@parcel/watcher-win32-x64': 2.6.0 optional: true '@peculiar/asn1-schema@2.6.0': @@ -18990,34 +19190,34 @@ snapshots: '@popperjs/core@2.11.8': {} - '@posthog/core@1.45.1': + '@posthog/core@1.48.8': dependencies: - '@posthog/types': 1.398.0 + '@posthog/types': 1.405.1 - '@posthog/types@1.398.0': {} + '@posthog/types@1.405.1': {} - '@prisma/adapter-pg@7.9.0': + '@prisma/adapter-pg@7.9.1': dependencies: - '@prisma/driver-adapter-utils': 7.9.0 + '@prisma/driver-adapter-utils': 7.9.1 '@types/pg': 8.20.0 - pg: 8.22.0 + pg: 8.23.0 postgres-array: 3.0.4 transitivePeerDependencies: - pg-native - '@prisma/client-runtime-utils@7.9.0': {} + '@prisma/client-runtime-utils@7.9.1': {} - '@prisma/client@7.9.0(prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)': + '@prisma/client@7.9.1(prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@prisma/client-runtime-utils': 7.9.0 + '@prisma/client-runtime-utils': 7.9.1 optionalDependencies: - prisma: 7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + prisma: 7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) typescript: 5.9.3 - '@prisma/config@7.9.0': + '@prisma/config@7.9.1': dependencies: c12: 3.3.4 - deepmerge-ts: 7.1.5 + deepmerge-ts: 8.0.1 effect: 3.20.0 empathic: 2.0.0 transitivePeerDependencies: @@ -19025,9 +19225,9 @@ snapshots: '@prisma/debug@7.2.0': {} - '@prisma/debug@7.9.0': {} + '@prisma/debug@7.9.1': {} - '@prisma/dev@0.24.14(typescript@5.9.3)': + '@prisma/dev@0.24.17(typescript@5.9.3)': dependencies: '@electric-sql/pglite': 0.4.3 '@electric-sql/pglite-socket': 0.1.3(@electric-sql/pglite@0.4.3) @@ -19042,37 +19242,37 @@ snapshots: proper-lockfile: 4.1.2 remeda: 2.33.4 std-env: 3.10.0 - valibot: 1.2.0(typescript@5.9.3) + valibot: 1.4.2(typescript@5.9.3) zeptomatch: 2.1.0 transitivePeerDependencies: - typescript - '@prisma/driver-adapter-utils@7.9.0': + '@prisma/driver-adapter-utils@7.9.1': dependencies: - '@prisma/debug': 7.9.0 + '@prisma/debug': 7.9.1 '@prisma/engines-version@7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad': {} - '@prisma/engines@7.9.0': + '@prisma/engines@7.9.1': dependencies: - '@prisma/debug': 7.9.0 + '@prisma/debug': 7.9.1 '@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad - '@prisma/fetch-engine': 7.9.0 - '@prisma/get-platform': 7.9.0 + '@prisma/fetch-engine': 7.9.1 + '@prisma/get-platform': 7.9.1 - '@prisma/fetch-engine@7.9.0': + '@prisma/fetch-engine@7.9.1': dependencies: - '@prisma/debug': 7.9.0 + '@prisma/debug': 7.9.1 '@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad - '@prisma/get-platform': 7.9.0 + '@prisma/get-platform': 7.9.1 '@prisma/get-platform@7.2.0': dependencies: '@prisma/debug': 7.2.0 - '@prisma/get-platform@7.9.0': + '@prisma/get-platform@7.9.1': dependencies: - '@prisma/debug': 7.9.0 + '@prisma/debug': 7.9.1 '@prisma/query-plan-executor@7.2.0': {} @@ -19123,7 +19323,7 @@ snapshots: '@protobufjs/pool@1.1.0': {} - '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.2': {} '@radix-ui/primitive@1.1.3': {} @@ -19296,7 +19496,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: rollup: 2.80.0 @@ -19304,7 +19504,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: rollup: 4.60.4 @@ -19462,7 +19662,7 @@ snapshots: '@scarf/scarf@1.4.0': {} - '@scure/base@2.2.0': {} + '@scure/base@2.3.0': {} '@selderee/plugin-htmlparser2@0.11.0': dependencies: @@ -19504,7 +19704,7 @@ snapshots: magic-string: 0.25.9 string.prototype.matchall: 4.0.12 - '@sveltejs/vite-plugin-svelte@1.4.0(svelte@3.59.2)(vite@3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1))': + '@sveltejs/vite-plugin-svelte@1.4.0(svelte@3.59.2)(vite@3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1))': dependencies: debug: 4.4.3(supports-color@8.1.1) deepmerge: 4.3.1 @@ -19512,8 +19712,8 @@ snapshots: magic-string: 0.26.7 svelte: 3.59.2 svelte-hmr: 0.15.3(svelte@3.59.2) - vite: 3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1) - vitefu: 0.2.5(vite@3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1)) + vite: 3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1) + vitefu: 0.2.5(vite@3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1)) transitivePeerDependencies: - supports-color @@ -19616,24 +19816,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/bcrypt@6.0.0': dependencies: @@ -19791,11 +19991,13 @@ snapshots: '@types/lodash@4.17.24': {} + '@types/lodash@4.17.25': {} + '@types/long@4.0.2': {} '@types/luxon@3.7.1': {} - '@types/markdown-it@14.1.2': + '@types/markdown-it@14.2.0': dependencies: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 @@ -19928,7 +20130,7 @@ snapshots: '@types/splitpanes@2.2.6(typescript@5.9.3)': dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) transitivePeerDependencies: - typescript @@ -20002,15 +20204,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.65.0 - '@typescript-eslint/type-utils': 8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.65.0 - eslint: 10.7.0(jiti@2.6.1) + '@typescript-eslint/parser': 8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 10.8.1(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -20042,14 +20260,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.65.0 - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.65.0 + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@8.1.1) + eslint: 10.8.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 10.7.0(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -20072,10 +20302,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.65.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.67.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) - '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@5.9.3) + '@typescript-eslint/types': 8.67.0 debug: 4.4.3(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: @@ -20091,10 +20321,10 @@ snapshots: '@typescript-eslint/types': 8.64.0 '@typescript-eslint/visitor-keys': 8.64.0 - '@typescript-eslint/scope-manager@8.65.0': + '@typescript-eslint/scope-manager@8.67.0': dependencies: - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/visitor-keys': 8.65.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': dependencies: @@ -20108,6 +20338,10 @@ snapshots: dependencies: typescript: 5.9.3 + '@typescript-eslint/tsconfig-utils@8.67.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@typescript-eslint/type-utils@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.61.1 @@ -20132,13 +20366,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -20150,6 +20396,8 @@ snapshots: '@typescript-eslint/types@8.65.0': {} + '@typescript-eslint/types@8.67.0': {} + '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) @@ -20180,12 +20428,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.65.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.67.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.65.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/visitor-keys': 8.65.0 + '@typescript-eslint/project-service': 8.67.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@5.9.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 debug: 4.4.3(supports-color@8.1.1) minimatch: 10.2.5 semver: 7.8.5 @@ -20217,23 +20465,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.65.0(eslint@10.7.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.67.0(eslint@10.8.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.65.0 - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3) - eslint: 10.7.0(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.1(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + eslint: 10.8.1(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.65.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.65.0 - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -20249,18 +20497,18 @@ snapshots: '@typescript-eslint/types': 8.64.0 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.65.0': + '@typescript-eslint/visitor-keys@8.67.0': dependencies: - '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/types': 8.67.0 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} - '@unhead/vue@2.1.12(vue@3.5.40(typescript@5.9.3))': + '@unhead/vue@2.1.12(vue@3.5.41(typescript@5.9.3))': dependencies: hookable: 6.1.0 unhead: 2.1.12 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -20348,7 +20596,7 @@ snapshots: '@urql/exchange-graphcache@7.2.4(@urql/core@6.0.3(graphql@16.13.2))(graphql@16.13.2)': dependencies: - '@0no-co/graphql.web': 1.3.2(graphql@16.13.2) + '@0no-co/graphql.web': 1.3.4(graphql@16.13.2) '@urql/core': 6.0.3(graphql@16.13.2) wonka: 6.3.6 transitivePeerDependencies: @@ -20356,7 +20604,7 @@ snapshots: '@urql/exchange-graphcache@7.2.4(@urql/core@6.0.3(graphql@16.14.0))(graphql@16.14.0)': dependencies: - '@0no-co/graphql.web': 1.3.2(graphql@16.14.0) + '@0no-co/graphql.web': 1.3.4(graphql@16.14.0) '@urql/core': 6.0.3(graphql@16.14.0) wonka: 6.3.6 transitivePeerDependencies: @@ -20370,10 +20618,10 @@ snapshots: dependencies: graphql: 16.14.0 - '@urql/vue@2.1.1(@urql/core@6.0.3(graphql@16.13.2))(vue@3.5.40(typescript@5.9.3))': + '@urql/vue@2.1.1(@urql/core@6.0.3(graphql@16.13.2))(vue@3.5.41(typescript@5.9.3))': dependencies: '@urql/core': 6.0.3(graphql@16.13.2) - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) wonka: 6.3.6 '@visx/curve@4.0.1-alpha.0': @@ -20406,7 +20654,7 @@ snapshots: '@visx/responsive@4.0.1-alpha.0(react@19.2.4)': dependencies: - '@types/lodash': 4.17.24 + '@types/lodash': 4.17.25 '@types/react': 19.2.14 lodash: 4.18.1 react: 19.2.4 @@ -20417,7 +20665,7 @@ snapshots: '@visx/shape@4.0.1-alpha.0(react@19.2.4)': dependencies: - '@types/lodash': 4.17.24 + '@types/lodash': 4.17.25 '@types/react': 19.2.14 '@visx/curve': 4.0.1-alpha.0 '@visx/group': 4.0.1-alpha.0(react@19.2.4) @@ -20453,7 +20701,7 @@ snapshots: d3-time-format: 4.1.0 internmap: 2.0.3 - '@vitejs/plugin-legacy@2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))': + '@vitejs/plugin-legacy@2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': dependencies: '@babel/standalone': 7.29.2 core-js: 3.49.0 @@ -20461,9 +20709,9 @@ snapshots: regenerator-runtime: 0.13.11 systemjs: 6.15.1 terser: 5.46.1 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - '@vitejs/plugin-legacy@2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))': + '@vitejs/plugin-legacy@2.3.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': dependencies: '@babel/standalone': 7.29.2 core-js: 3.49.0 @@ -20471,9 +20719,9 @@ snapshots: regenerator-runtime: 0.13.11 systemjs: 6.15.1 terser: 5.46.1 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - '@vitejs/plugin-legacy@7.2.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))': + '@vitejs/plugin-legacy@7.2.1(terser@5.46.1)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) @@ -20488,27 +20736,21 @@ snapshots: regenerator-runtime: 0.14.1 systemjs: 6.15.1 terser: 5.46.1 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.7(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': - dependencies: - '@rolldown/pluginutils': 1.0.1 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) - - '@vitejs/plugin-vue@6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.8(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue: 3.5.41(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.8(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue@3.5.41(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue: 3.5.41(typescript@5.9.3) '@vitest/expect@4.1.10': dependencies: @@ -20517,33 +20759,59 @@ snapshots: '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 + + '@vitest/expect@4.1.11': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + chai: 6.2.2 + tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - '@vitest/mocker@4.1.10(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.10 + '@vitest/spy': 4.1.11 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + + '@vitest/mocker@4.1.11(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 + + '@vitest/pretty-format@4.1.11': + dependencies: + tinyrainbow: 3.1.1 '@vitest/runner@4.1.10': dependencies: '@vitest/utils': 4.1.10 pathe: 2.0.3 + '@vitest/runner@4.1.11': + dependencies: + '@vitest/utils': 4.1.11 + pathe: 2.0.3 + '@vitest/snapshot@4.1.10': dependencies: '@vitest/pretty-format': 4.1.10 @@ -20551,13 +20819,28 @@ snapshots: magic-string: 0.30.21 pathe: 2.0.3 + '@vitest/snapshot@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@4.1.10': {} + '@vitest/spy@4.1.11': {} + '@vitest/utils@4.1.10': dependencies: '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 + + '@vitest/utils@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 '@volar/language-core@1.10.10': dependencies: @@ -20586,16 +20869,16 @@ snapshots: '@vue/compiler-core@3.5.38': dependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@vue/shared': 3.5.38 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-core@3.5.40': + '@vue/compiler-core@3.5.41': dependencies: - '@babel/parser': 7.29.7 - '@vue/shared': 3.5.40 + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.41 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 @@ -20605,27 +20888,27 @@ snapshots: '@vue/compiler-core': 3.5.38 '@vue/shared': 3.5.38 - '@vue/compiler-dom@3.5.40': + '@vue/compiler-dom@3.5.41': dependencies: - '@vue/compiler-core': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-core': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/compiler-sfc@3.5.40': + '@vue/compiler-sfc@3.5.41': dependencies: - '@babel/parser': 7.29.7 - '@vue/compiler-core': 3.5.40 - '@vue/compiler-dom': 3.5.40 - '@vue/compiler-ssr': 3.5.40 - '@vue/shared': 3.5.40 + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.41 + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-ssr': 3.5.41 + '@vue/shared': 3.5.41 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.20 + postcss: 8.5.26 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.40': + '@vue/compiler-ssr@3.5.41': dependencies: - '@vue/compiler-dom': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-dom': 3.5.41 + '@vue/shared': 3.5.41 '@vue/compiler-vue2@2.7.16': dependencies: @@ -20634,26 +20917,26 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/eslint-config-typescript@14.8.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-vue: 10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + eslint-plugin-vue: 10.10.0(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) fast-glob: 3.3.3 - typescript-eslint: 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) vue-eslint-parser: 10.4.1(eslint@9.39.2(jiti@2.6.1)) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.65.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-vue: 10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) + eslint-plugin-vue: 10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))) fast-glob: 3.3.3 - typescript-eslint: 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) vue-eslint-parser: 10.4.1(eslint@9.39.2(jiti@2.6.1)) optionalDependencies: typescript: 5.9.3 @@ -20676,9 +20959,9 @@ snapshots: '@vue/language-core@2.1.6(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.38 + '@vue/compiler-dom': 3.5.41 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.38 + '@vue/shared': 3.5.41 computeds: 0.0.1 minimatch: 9.0.9 muggle-string: 0.4.1 @@ -20703,31 +20986,31 @@ snapshots: dependencies: '@vue/shared': 3.5.38 - '@vue/reactivity@3.5.40': + '@vue/reactivity@3.5.41': dependencies: - '@vue/shared': 3.5.40 + '@vue/shared': 3.5.41 - '@vue/runtime-core@3.5.40': + '@vue/runtime-core@3.5.41': dependencies: - '@vue/reactivity': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/reactivity': 3.5.41 + '@vue/shared': 3.5.41 - '@vue/runtime-dom@3.5.40': + '@vue/runtime-dom@3.5.41': dependencies: - '@vue/reactivity': 3.5.40 - '@vue/runtime-core': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/reactivity': 3.5.41 + '@vue/runtime-core': 3.5.41 + '@vue/shared': 3.5.41 csstype: 3.2.3 - '@vue/server-renderer@3.5.40': + '@vue/server-renderer@3.5.41': dependencies: - '@vue/compiler-ssr': 3.5.40 - '@vue/runtime-dom': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-ssr': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/shared': 3.5.41 '@vue/shared@3.5.38': {} - '@vue/shared@3.5.40': {} + '@vue/shared@3.5.41': {} '@vue/typescript@1.8.8(typescript@5.9.3)': dependencies: @@ -20736,35 +21019,35 @@ snapshots: transitivePeerDependencies: - typescript - '@vueuse/core@14.3.0(vue@3.5.40(typescript@5.9.3))': + '@vueuse/core@14.4.0(vue@3.5.41(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 14.3.0 - '@vueuse/shared': 14.3.0(vue@3.5.40(typescript@5.9.3)) - vue: 3.5.40(typescript@5.9.3) + '@vueuse/metadata': 14.4.0 + '@vueuse/shared': 14.4.0(vue@3.5.41(typescript@5.9.3)) + vue: 3.5.41(typescript@5.9.3) - '@vueuse/core@8.9.4(vue@3.5.40(typescript@5.9.3))': + '@vueuse/core@8.9.4(vue@3.5.41(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.14 '@vueuse/metadata': 8.9.4 - '@vueuse/shared': 8.9.4(vue@3.5.40(typescript@5.9.3)) - vue-demi: 0.14.10(vue@3.5.40(typescript@5.9.3)) + '@vueuse/shared': 8.9.4(vue@3.5.41(typescript@5.9.3)) + vue-demi: 0.14.10(vue@3.5.41(typescript@5.9.3)) optionalDependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) - '@vueuse/metadata@14.3.0': {} + '@vueuse/metadata@14.4.0': {} '@vueuse/metadata@8.9.4': {} - '@vueuse/shared@14.3.0(vue@3.5.40(typescript@5.9.3))': + '@vueuse/shared@14.4.0(vue@3.5.41(typescript@5.9.3))': dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) - '@vueuse/shared@8.9.4(vue@3.5.40(typescript@5.9.3))': + '@vueuse/shared@8.9.4(vue@3.5.41(typescript@5.9.3))': dependencies: - vue-demi: 0.14.10(vue@3.5.40(typescript@5.9.3)) + vue-demi: 0.14.10(vue@3.5.41(typescript@5.9.3)) optionalDependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) '@webassemblyjs/ast@1.14.1': dependencies: @@ -20897,6 +21180,9 @@ snapshots: a-sync-waterfall@1.0.1: optional: true + abbrev@2.0.0: + optional: true + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -20906,13 +21192,13 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-import-phases@1.0.4(acorn@8.17.0): + acorn-import-phases@1.0.4(acorn@8.18.0): dependencies: - acorn: 8.17.0 + acorn: 8.18.0 - acorn-jsx@5.3.2(acorn@8.17.0): + acorn-jsx@5.3.2(acorn@8.18.0): dependencies: - acorn: 8.17.0 + acorn: 8.18.0 acorn-loose@6.1.0: dependencies: @@ -20931,7 +21217,7 @@ snapshots: acorn@8.16.0: {} - acorn@8.17.0: {} + acorn@8.18.0: {} after@0.8.2: {} @@ -20971,17 +21257,24 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.4 + fast-uri: 3.1.5 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.4 + fast-uri: 3.1.5 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -21038,7 +21331,7 @@ snapshots: dependencies: '@xmldom/xmldom': 0.8.13 iconv-lite: 0.6.3 - js-yaml: 4.3.0 + js-yaml: 4.3.1 jszip: 3.10.1 lodash: 4.18.1 oas-validator: 5.0.8 @@ -21056,11 +21349,11 @@ snapshots: arg@5.0.2: {} - argon2@0.44.0: + argon2@0.45.1: dependencies: '@phc/format': 1.0.0 cross-env: 10.1.0 - node-addon-api: 8.7.0 + node-addon-api: 8.9.2 node-gyp-build: 4.8.4 argparse@1.0.10: @@ -21119,22 +21412,13 @@ snapshots: auto-bind@4.0.0: {} - autoprefixer@10.5.0(postcss@8.5.18): + autoprefixer@10.5.4(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - caniuse-lite: 1.0.30001791 + browserslist: 4.28.8 + caniuse-lite: 1.0.30001809 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.18 - postcss-value-parser: 4.2.0 - - autoprefixer@10.5.4(postcss@8.5.20): - dependencies: - browserslist: 4.28.7 - caniuse-lite: 1.0.30001806 - fraction.js: 5.3.4 - picocolors: 1.1.1 - postcss: 8.5.20 + postcss: 8.5.26 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -21145,15 +21429,15 @@ snapshots: aws4fetch@1.0.20: {} - axios-cookiejar-support@6.0.5(axios@1.18.1)(tough-cookie@6.0.2): + axios-cookiejar-support@6.0.5(axios@1.19.0)(tough-cookie@6.0.2): dependencies: - axios: 1.18.1 + axios: 1.19.0 http-cookie-agent: 7.0.3(tough-cookie@6.0.2) tough-cookie: 6.0.2 transitivePeerDependencies: - undici - axios@1.18.1: + axios@1.19.0: dependencies: follow-redirects: 1.16.0 form-data: 4.0.6 @@ -21308,7 +21592,7 @@ snapshots: babel-walk@3.0.0-canary-5: dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 optional: true backo2@1.0.2: {} @@ -21325,7 +21609,7 @@ snapshots: baseline-browser-mapping@2.10.23: {} - baseline-browser-mapping@2.11.1: {} + baseline-browser-mapping@2.11.17: {} basic-auth@2.0.1: dependencies: @@ -21354,13 +21638,13 @@ snapshots: blob@0.0.5: {} - body-parser@2.2.1: + body-parser@2.3.0: dependencies: bytes: 3.1.2 - content-type: 1.0.5 + content-type: 2.0.0 debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 on-finished: 2.4.1 qs: 6.15.3 raw-body: 3.0.2 @@ -21381,7 +21665,7 @@ snapshots: widest-line: 3.1.0 wrap-ansi: 7.0.0 - brace-expansion@5.0.8: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -21409,18 +21693,18 @@ snapshots: browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.23 - caniuse-lite: 1.0.30001791 + caniuse-lite: 1.0.30001809 electron-to-chromium: 1.5.344 node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) - browserslist@4.28.7: + browserslist@4.28.8: dependencies: - baseline-browser-mapping: 2.11.1 - caniuse-lite: 1.0.30001806 - electron-to-chromium: 1.5.396 - node-releases: 2.0.51 - update-browserslist-db: 1.2.3(browserslist@4.28.7) + baseline-browser-mapping: 2.11.17 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.412 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) bs-logger@0.2.6: dependencies: @@ -21519,17 +21803,15 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.28.2 - caniuse-lite: 1.0.30001791 + browserslist: 4.28.8 + caniuse-lite: 1.0.30001809 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 optional: true caniuse-lite@1.0.30001781: {} - caniuse-lite@1.0.30001791: {} - - caniuse-lite@1.0.30001806: {} + caniuse-lite@1.0.30001809: {} capital-case@1.0.4: dependencies: @@ -21609,15 +21891,19 @@ snapshots: domutils: 3.2.2 optional: true - cheerio@1.0.0-rc.12: + cheerio@1.0.0: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 domhandler: 5.0.3 domutils: 3.2.2 - htmlparser2: 8.0.2 + encoding-sniffer: 0.2.1 + htmlparser2: 9.1.0 parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.28.0 + whatwg-mimetype: 4.0.0 optional: true chokidar@3.6.0: @@ -21638,7 +21924,7 @@ snapshots: chokidar@5.0.0: dependencies: - readdirp: 5.0.0 + readdirp: 5.1.1 chrome-trace-event@1.0.4: {} @@ -21733,10 +22019,16 @@ snapshots: commander@11.1.0: optional: true + commander@12.1.0: + optional: true + commander@13.1.0: {} commander@14.0.3: {} + commander@15.0.0: + optional: true + commander@2.20.3: {} commander@4.1.1: {} @@ -21744,9 +22036,6 @@ snapshots: commander@5.1.0: optional: true - commander@6.2.1: - optional: true - commander@7.2.0: {} commander@9.5.0: @@ -21783,6 +22072,12 @@ snapshots: confbox@0.2.4: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + optional: true + consola@3.4.2: {} constant-case@3.0.4: @@ -21793,8 +22088,8 @@ snapshots: constantinople@4.0.1: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 optional: true content-disposition@1.0.1: {} @@ -21846,7 +22141,11 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.2 + browserslist: 4.28.1 + + core-js-compat@3.50.0: + dependencies: + browserslist: 4.28.8 core-js@3.49.0: {} @@ -21869,14 +22168,14 @@ snapshots: cosmiconfig@8.0.0: dependencies: import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.3.1 parse-json: 5.2.0 path-type: 4.0.0 cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.3.1 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: @@ -21886,7 +22185,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.3.1 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 @@ -21929,9 +22228,9 @@ snapshots: crypto-random-string@2.0.0: {} - css-declaration-sorter@7.3.1(postcss@8.5.18): + css-declaration-sorter@7.3.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 optional: true css-select@5.2.2: @@ -21961,51 +22260,65 @@ snapshots: cssfilter@0.0.10: {} - cssnano-preset-default@7.0.11(postcss@8.5.18): + cssnano-preset-default@7.0.11(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - css-declaration-sorter: 7.3.1(postcss@8.5.18) - cssnano-utils: 5.0.1(postcss@8.5.18) - postcss: 8.5.18 - postcss-calc: 10.1.1(postcss@8.5.18) - postcss-colormin: 7.0.6(postcss@8.5.18) - postcss-convert-values: 7.0.9(postcss@8.5.18) - postcss-discard-comments: 7.0.6(postcss@8.5.18) - postcss-discard-duplicates: 7.0.2(postcss@8.5.18) - postcss-discard-empty: 7.0.1(postcss@8.5.18) - postcss-discard-overridden: 7.0.1(postcss@8.5.18) - postcss-merge-longhand: 7.0.5(postcss@8.5.18) - postcss-merge-rules: 7.0.8(postcss@8.5.18) - postcss-minify-font-values: 7.0.1(postcss@8.5.18) - postcss-minify-gradients: 7.0.1(postcss@8.5.18) - postcss-minify-params: 7.0.6(postcss@8.5.18) - postcss-minify-selectors: 7.0.6(postcss@8.5.18) - postcss-normalize-charset: 7.0.1(postcss@8.5.18) - postcss-normalize-display-values: 7.0.1(postcss@8.5.18) - postcss-normalize-positions: 7.0.1(postcss@8.5.18) - postcss-normalize-repeat-style: 7.0.1(postcss@8.5.18) - postcss-normalize-string: 7.0.1(postcss@8.5.18) - postcss-normalize-timing-functions: 7.0.1(postcss@8.5.18) - postcss-normalize-unicode: 7.0.6(postcss@8.5.18) - postcss-normalize-url: 7.0.1(postcss@8.5.18) - postcss-normalize-whitespace: 7.0.1(postcss@8.5.18) - postcss-ordered-values: 7.0.2(postcss@8.5.18) - postcss-reduce-initial: 7.0.6(postcss@8.5.18) - postcss-reduce-transforms: 7.0.1(postcss@8.5.18) - postcss-svgo: 7.1.1(postcss@8.5.18) - postcss-unique-selectors: 7.0.5(postcss@8.5.18) - optional: true - - cssnano-utils@5.0.1(postcss@8.5.18): - dependencies: - postcss: 8.5.18 - optional: true - - cssnano@7.1.3(postcss@8.5.18): - dependencies: - cssnano-preset-default: 7.0.11(postcss@8.5.18) + browserslist: 4.28.8 + css-declaration-sorter: 7.3.1(postcss@8.5.26) + cssnano-utils: 5.0.1(postcss@8.5.26) + postcss: 8.5.26 + postcss-calc: 10.1.1(postcss@8.5.26) + postcss-colormin: 7.0.6(postcss@8.5.26) + postcss-convert-values: 7.0.9(postcss@8.5.26) + postcss-discard-comments: 7.0.6(postcss@8.5.26) + postcss-discard-duplicates: 7.0.2(postcss@8.5.26) + postcss-discard-empty: 7.0.1(postcss@8.5.26) + postcss-discard-overridden: 7.0.1(postcss@8.5.26) + postcss-merge-longhand: 7.0.5(postcss@8.5.26) + postcss-merge-rules: 7.0.8(postcss@8.5.26) + postcss-minify-font-values: 7.0.1(postcss@8.5.26) + postcss-minify-gradients: 7.0.1(postcss@8.5.26) + postcss-minify-params: 7.0.6(postcss@8.5.26) + postcss-minify-selectors: 7.0.6(postcss@8.5.26) + postcss-normalize-charset: 7.0.1(postcss@8.5.26) + postcss-normalize-display-values: 7.0.1(postcss@8.5.26) + postcss-normalize-positions: 7.0.1(postcss@8.5.26) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.26) + postcss-normalize-string: 7.0.1(postcss@8.5.26) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.26) + postcss-normalize-unicode: 7.0.6(postcss@8.5.26) + postcss-normalize-url: 7.0.1(postcss@8.5.26) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.26) + postcss-ordered-values: 7.0.2(postcss@8.5.26) + postcss-reduce-initial: 7.0.6(postcss@8.5.26) + postcss-reduce-transforms: 7.0.1(postcss@8.5.26) + postcss-svgo: 7.1.1(postcss@8.5.26) + postcss-unique-selectors: 7.0.5(postcss@8.5.26) + optional: true + + cssnano-preset-lite@4.0.6(postcss@8.5.26): + dependencies: + cssnano-utils: 5.0.3(postcss@8.5.26) + postcss: 8.5.26 + postcss-discard-comments: 7.0.8(postcss@8.5.26) + postcss-discard-empty: 7.0.3(postcss@8.5.26) + postcss-normalize-whitespace: 7.0.3(postcss@8.5.26) + optional: true + + cssnano-utils@5.0.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + optional: true + + cssnano-utils@5.0.3(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + optional: true + + cssnano@7.1.3(postcss@8.5.26): + dependencies: + cssnano-preset-default: 7.0.11(postcss@8.5.26) lilconfig: 3.1.3 - postcss: 8.5.18 + postcss: 8.5.26 optional: true csso@5.0.5: @@ -22132,7 +22445,7 @@ snapshots: deep-is@0.1.4: {} - deepmerge-ts@7.1.5: {} + deepmerge-ts@8.0.1: {} deepmerge@4.3.1: {} @@ -22204,11 +22517,11 @@ snapshots: diff@7.0.0: {} - dioc@3.0.2(vue@3.5.40(typescript@5.9.3)): + dioc@3.0.2(vue@3.5.41(typescript@5.9.3)): dependencies: rxjs: 7.8.2 optionalDependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) dir-glob@3.0.1: dependencies: @@ -22242,11 +22555,6 @@ snapshots: domelementtype@2.3.0: optional: true - domhandler@3.3.0: - dependencies: - domelementtype: 2.3.0 - optional: true - domhandler@4.3.1: dependencies: domelementtype: 2.3.0 @@ -22261,7 +22569,7 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 - dompurify@3.4.12: + dompurify@3.4.14: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -22320,6 +22628,14 @@ snapshots: dependencies: safe-buffer: 5.2.1 + editorconfig@1.0.7: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.9 + semver: 7.8.5 + optional: true + ee-first@1.1.1: {} effect@3.20.0: @@ -22338,7 +22654,7 @@ snapshots: electron-to-chromium@1.5.344: {} - electron-to-chromium@1.5.396: {} + electron-to-chromium@1.5.412: {} elkjs@0.11.1: {} @@ -22357,6 +22673,12 @@ snapshots: encoding-japanese@2.2.0: optional: true + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + optional: true + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -22541,7 +22863,7 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.1 + es-to-primitive: 1.3.4 function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 @@ -22567,7 +22889,7 @@ snapshots: object-inspect: 1.13.4 object-keys: 1.1.1 object.assign: 4.1.7 - own-keys: 1.0.1 + own-keys: 1.0.2 regexp.prototype.flags: 1.5.4 safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 @@ -22590,6 +22912,8 @@ snapshots: es-module-lexer@2.1.0: {} + es-module-lexer@2.3.2: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -22611,9 +22935,10 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es-to-primitive@1.3.1: + es-to-primitive@1.3.4: dependencies: es-abstract-get: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 @@ -22813,33 +23138,23 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.8(eslint@10.7.0(jiti@2.6.1)): + eslint-config-prettier@10.1.8(eslint@10.8.1(jiti@2.6.1)): dependencies: - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.7.0(jiti@2.6.1)))(eslint@10.7.0(jiti@2.6.1))(prettier@3.9.6): + eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.1(jiti@2.6.1)))(eslint@10.8.1(jiti@2.6.1))(prettier@3.9.6): dependencies: - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) prettier: 3.9.6 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@10.7.0(jiti@2.6.1)) - - eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.5): - dependencies: - eslint: 9.39.2(jiti@2.6.1) - prettier: 3.8.5 - prettier-linter-helpers: 1.0.1 - synckit: 0.11.13 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) + eslint-config-prettier: 10.1.8(eslint@10.8.1(jiti@2.6.1)) eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.9.6): dependencies: @@ -22851,29 +23166,29 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))): + eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@2.6.1)) eslint: 9.39.2(jiti@2.6.1) natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.5 semver: 7.8.5 vue-eslint-parser: 10.4.1(eslint@9.39.2(jiti@2.6.1)) - xml-name-validator: 4.0.0 + xml-name-validator: 5.0.0 optionalDependencies: '@typescript-eslint/parser': 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))): + eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1))): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@2.6.1)) eslint: 9.39.2(jiti@2.6.1) natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.5 semver: 7.8.5 vue-eslint-parser: 10.4.1(eslint@9.39.2(jiti@2.6.1)) - xml-name-validator: 4.0.0 + xml-name-validator: 5.0.0 optionalDependencies: '@typescript-eslint/parser': 8.64.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) @@ -22890,7 +23205,7 @@ snapshots: eslint-scope@9.1.2: dependencies: '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -22900,12 +23215,12 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.7.0(jiti@2.6.1): + eslint@10.8.1(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.1(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 - '@eslint/config-helpers': 0.6.0 + '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@humanfs/node': 0.16.7 @@ -22980,20 +23295,20 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 4.2.1 espree@11.2.0: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.1 espree@9.6.1: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 3.4.3 esprima-extract-comments@1.1.0: @@ -23080,7 +23395,7 @@ snapshots: exit-x@0.2.2: {} - expect-type@1.3.0: {} + expect-type@1.4.0: {} expect@29.7.0: dependencies: @@ -23111,7 +23426,7 @@ snapshots: express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.1 + body-parser: 2.3.0 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 @@ -23130,7 +23445,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.0 + qs: 6.15.3 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -23187,7 +23502,7 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@3.1.4: {} + fast-uri@3.1.5: {} fast-url-parser@1.1.3: dependencies: @@ -23223,6 +23538,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -23535,7 +23854,7 @@ snapshots: globals@16.5.0: {} - globals@17.7.0: {} + globals@17.11.0: {} globalthis@1.0.4: dependencies: @@ -23744,6 +24063,12 @@ snapshots: optionalDependencies: ws: 8.21.0 + graphql-ws@6.2.1(graphql@16.14.0)(ws@8.21.0): + dependencies: + graphql: 16.14.0 + optionalDependencies: + ws: 8.21.0 + graphql@16.13.2: {} graphql@16.14.0: {} @@ -23803,7 +24128,7 @@ snapshots: capital-case: 1.0.4 tslib: 2.8.1 - highlight.js@11.11.1: {} + highlight.js@11.12.0: {} highlightjs-curl@1.3.0: {} @@ -23815,9 +24140,9 @@ snapshots: dependencies: whatwg-encoding: 2.0.0 - html-encoding-sniffer@6.0.0(@noble/hashes@2.2.0): + html-encoding-sniffer@6.0.0(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.0(@noble/hashes@2.2.0) + '@exodus/bytes': 1.15.0(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' @@ -23832,27 +24157,22 @@ snapshots: selderee: 0.11.0 optional: true - htmlnano@2.1.5(cssnano@7.1.3(postcss@8.5.18))(postcss@8.5.18)(terser@5.46.1)(typescript@5.9.3): + htmlnano@3.4.0(cssnano@7.1.3(postcss@8.5.26))(postcss@8.5.26)(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@types/relateurl': 0.2.33 + commander: 15.0.0 cosmiconfig: 9.0.1(typescript@5.9.3) posthtml: 0.16.7 + tinyglobby: 0.2.17 optionalDependencies: - cssnano: 7.1.3(postcss@8.5.18) - postcss: 8.5.18 + cssnano: 7.1.3(postcss@8.5.26) + postcss: 8.5.26 + svgo: 4.0.2 terser: 5.46.1 transitivePeerDependencies: - typescript optional: true - htmlparser2@5.0.1: - dependencies: - domelementtype: 2.3.0 - domhandler: 3.3.0 - domutils: 2.8.0 - entities: 2.2.0 - optional: true - htmlparser2@7.2.0: dependencies: domelementtype: 2.3.0 @@ -23953,6 +24273,11 @@ snapshots: iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 + optional: true + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 idb@7.1.1: {} @@ -23968,6 +24293,8 @@ snapshots: immutable@5.1.6: {} + immutable@5.1.9: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -24288,7 +24615,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.8.5 @@ -24746,7 +25073,7 @@ snapshots: jiti@2.6.1: {} - jose@6.2.3: {} + jose@6.2.10: {} joycon@3.1.1: {} @@ -24754,6 +25081,18 @@ snapshots: js-base64@3.7.8: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.7 + glob: 10.5.0 + js-cookie: 3.0.8 + nopt: 7.2.1 + optional: true + + js-cookie@3.0.8: + optional: true + js-md5@0.8.3: {} js-stringify@1.0.2: @@ -24766,23 +25105,23 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.3.0: + js-yaml@4.3.1: dependencies: argparse: 2.0.1 - js-yaml@5.2.2: + js-yaml@5.3.0: dependencies: argparse: 2.0.1 - jsdom@27.4.0(@noble/hashes@2.2.0): + jsdom@27.4.0(@noble/hashes@2.3.0): dependencies: '@acemir/cssom': 0.9.31 '@asamuzakjp/dom-selector': 6.8.1 - '@exodus/bytes': 1.15.0(@noble/hashes@2.2.0) + '@exodus/bytes': 1.15.0(@noble/hashes@2.3.0) cssstyle: 5.3.7 data-urls: 6.0.1 decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0(@noble/hashes@2.2.0) + html-encoding-sniffer: 6.0.0(@noble/hashes@2.3.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 @@ -24825,7 +25164,7 @@ snapshots: jsonc-eslint-parser@2.4.2: dependencies: - acorn: 8.17.0 + acorn: 8.18.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 semver: 7.8.5 @@ -24866,15 +25205,14 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - juice@10.0.1: + juice@11.1.1: dependencies: - cheerio: 1.0.0-rc.12 - commander: 6.2.1 + cheerio: 1.0.0 + commander: 12.1.0 + entities: 7.0.1 mensch: 0.3.4 slick: 1.12.2 - web-resource-inliner: 6.0.1 - transitivePeerDependencies: - - encoding + web-resource-inliner: 8.0.0 optional: true jwa@2.0.1: @@ -25062,7 +25400,7 @@ snapshots: dependencies: js-tokens: 4.0.0 - lossless-json@4.3.0: {} + lossless-json@4.3.1: {} lower-case-first@2.0.2: dependencies: @@ -25217,27 +25555,27 @@ snapshots: minimatch@10.2.3: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@10.2.5: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@3.1.5: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@4.2.5: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@5.1.9: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@9.0.9: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimist@1.2.8: {} @@ -25245,13 +25583,12 @@ snapshots: minisearch@7.2.0: {} - mjml-accordion@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-accordion@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25261,13 +25598,12 @@ snapshots: - uncss optional: true - mjml-body@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-body@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25277,13 +25613,12 @@ snapshots: - uncss optional: true - mjml-button@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-button@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25293,13 +25628,12 @@ snapshots: - uncss optional: true - mjml-carousel@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-carousel@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25309,19 +25643,19 @@ snapshots: - uncss optional: true - mjml-cli@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-cli@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 - chokidar: 3.6.0 - glob: 10.5.0 + chokidar: 4.0.3 + glob: 11.1.0 lodash: 4.18.1 - minimatch: 9.0.9 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-parser-xml: 5.0.0-alpha.4 - mjml-validator: 5.0.0-alpha.4 + minimatch: 10.2.5 + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-parser-xml: 5.4.0 + mjml-preset-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-validator: 5.4.0 yargs: 17.7.3 transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25331,13 +25665,12 @@ snapshots: - uncss optional: true - mjml-column@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-column@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25347,21 +25680,21 @@ snapshots: - uncss optional: true - mjml-core@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-core@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 - cheerio: 1.0.0-rc.12 - cssnano: 7.1.3(postcss@8.5.18) + cheerio: 1.0.0 + cssnano: 7.1.3(postcss@8.5.26) + cssnano-preset-lite: 4.0.6(postcss@8.5.26) detect-node: 2.1.0 - htmlnano: 2.1.5(cssnano@7.1.3(postcss@8.5.18))(postcss@8.5.18)(terser@5.46.1)(typescript@5.9.3) - juice: 10.0.1 + htmlnano: 3.4.0(cssnano@7.1.3(postcss@8.5.26))(postcss@8.5.26)(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + js-beautify: 1.15.4 + juice: 11.1.1 lodash: 4.18.1 - mjml-parser-xml: 5.0.0-alpha.4 - mjml-validator: 5.0.0-alpha.4 - postcss: 8.5.18 - prettier: 3.9.6 + mjml-parser-xml: 5.4.0 + mjml-validator: 5.4.0 + postcss: 8.5.26 transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25371,13 +25704,12 @@ snapshots: - uncss optional: true - mjml-divider@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-divider@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25387,13 +25719,12 @@ snapshots: - uncss optional: true - mjml-group@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-group@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25403,13 +25734,12 @@ snapshots: - uncss optional: true - mjml-head-attributes@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-attributes@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25419,13 +25749,12 @@ snapshots: - uncss optional: true - mjml-head-breakpoint@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-breakpoint@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25435,13 +25764,12 @@ snapshots: - uncss optional: true - mjml-head-font@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-font@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25451,13 +25779,12 @@ snapshots: - uncss optional: true - mjml-head-html-attributes@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-html-attributes@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25467,13 +25794,12 @@ snapshots: - uncss optional: true - mjml-head-preview@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-preview@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25483,13 +25809,12 @@ snapshots: - uncss optional: true - mjml-head-style@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-style@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25499,13 +25824,12 @@ snapshots: - uncss optional: true - mjml-head-title@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head-title@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25515,13 +25839,12 @@ snapshots: - uncss optional: true - mjml-head@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-head@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25531,13 +25854,12 @@ snapshots: - uncss optional: true - mjml-hero@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-hero@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25547,13 +25869,12 @@ snapshots: - uncss optional: true - mjml-image@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-image@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25563,13 +25884,12 @@ snapshots: - uncss optional: true - mjml-navbar@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-navbar@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25579,7 +25899,7 @@ snapshots: - uncss optional: true - mjml-parser-xml@5.0.0-alpha.4: + mjml-parser-xml@5.4.0: dependencies: '@babel/runtime': 7.29.7 detect-node: 2.1.0 @@ -25587,36 +25907,35 @@ snapshots: lodash: 4.18.1 optional: true - mjml-preset-core@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-preset-core@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 - mjml-accordion: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-body: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-button: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-carousel: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-column: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-divider: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-group: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-attributes: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-breakpoint: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-font: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-html-attributes: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-preview: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-style: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-head-title: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-hero: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-image: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-navbar: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-raw: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-section: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-social: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-spacer: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-table: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-text: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-wrapper: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-accordion: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-body: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-button: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-carousel: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-column: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-divider: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-group: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-attributes: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-breakpoint: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-font: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-html-attributes: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-preview: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-style: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-head-title: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-hero: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-image: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-navbar: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-raw: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-section: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-social: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-spacer: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-table: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-text: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-wrapper: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25626,13 +25945,12 @@ snapshots: - uncss optional: true - mjml-raw@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-raw@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25642,13 +25960,12 @@ snapshots: - uncss optional: true - mjml-section@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-section@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25658,13 +25975,12 @@ snapshots: - uncss optional: true - mjml-social@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-social@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25674,13 +25990,12 @@ snapshots: - uncss optional: true - mjml-spacer@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-spacer@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25690,13 +26005,12 @@ snapshots: - uncss optional: true - mjml-table@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-table@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25706,13 +26020,12 @@ snapshots: - uncss optional: true - mjml-text@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-text@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25722,19 +26035,18 @@ snapshots: - uncss optional: true - mjml-validator@5.0.0-alpha.4: + mjml-validator@5.4.0: dependencies: '@babel/runtime': 7.29.7 optional: true - mjml-wrapper@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml-wrapper@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 lodash: 4.18.1 - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-section: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-section: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25744,15 +26056,14 @@ snapshots: - uncss optional: true - mjml@5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3): + mjml@5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.7 - mjml-cli: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-preset-core: 5.0.0-alpha.4(terser@5.46.1)(typescript@5.9.3) - mjml-validator: 5.0.0-alpha.4 + mjml-cli: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-preset-core: 5.4.0(svgo@4.0.2)(terser@5.46.1)(typescript@5.9.3) + mjml-validator: 5.4.0 transitivePeerDependencies: - - encoding - purgecss - relateurl - srcset @@ -25766,12 +26077,12 @@ snapshots: mlly@1.8.2: dependencies: - acorn: 8.17.0 + acorn: 8.18.0 pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.6.3 - mocha@11.7.6: + mocha@11.8.0: dependencies: browser-stdout: 1.3.1 chokidar: 4.0.3 @@ -25782,7 +26093,7 @@ snapshots: glob: 10.5.0 he: 1.2.0 is-path-inside: 3.0.3 - js-yaml: 4.3.0 + js-yaml: 4.3.1 log-symbols: 4.1.0 minimatch: 9.0.9 ms: 2.1.3 @@ -25834,7 +26145,7 @@ snapshots: aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 long: 5.3.2 lru.min: 1.1.4 named-placeholders: 1.1.6 @@ -25851,9 +26162,7 @@ snapshots: dependencies: lru.min: 1.1.4 - nanoid@3.3.14: {} - - nanoid@3.3.16: {} + nanoid@3.3.18: {} napi-postinstall@0.3.4: {} @@ -25875,6 +26184,8 @@ snapshots: node-addon-api@8.7.0: {} + node-addon-api@8.9.2: {} + node-domexception@1.0.0: {} node-emoji@1.11.0: @@ -25907,12 +26218,17 @@ snapshots: node-releases@2.0.38: {} - node-releases@2.0.51: {} + node-releases@2.0.53: {} nodemailer@9.0.1: optional: true - nodemailer@9.0.3: {} + nodemailer@9.0.5: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + optional: true normalize-package-data@2.5.0: dependencies: @@ -26022,6 +26338,8 @@ snapshots: obug@2.1.3: {} + obug@2.1.4: {} + ohash@2.0.11: {} on-finished@2.4.1: @@ -26088,6 +26406,13 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-event@4.2.0: dependencies: p-timeout: 3.2.0 @@ -26139,7 +26464,7 @@ snapshots: pako@1.0.11: {} - papaparse@5.5.4: {} + papaparse@5.6.0: {} param-case@3.0.4: dependencies: @@ -26176,6 +26501,11 @@ snapshots: parse5: 7.3.0 optional: true + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + optional: true + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -26277,8 +26607,6 @@ snapshots: lru-cache: 11.2.7 minipass: 7.1.3 - path-to-regexp@8.4.0: {} - path-to-regexp@8.4.2: {} path-type@3.0.0: @@ -26318,12 +26646,14 @@ snapshots: pg-int8@1.0.1: {} - pg-pool@3.14.0(pg@8.22.0): + pg-pool@3.14.0(pg@8.23.0): dependencies: - pg: 8.22.0 + pg: 8.23.0 pg-protocol@1.15.0: {} + pg-protocol@1.16.0: {} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 @@ -26332,11 +26662,11 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.22.0: + pg@8.23.0: dependencies: pg-connection-string: 2.14.0 - pg-pool: 3.14.0(pg@8.22.0) - pg-protocol: 1.15.0 + pg-pool: 3.14.0(pg@8.23.0) + pg-protocol: 1.16.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: @@ -26352,6 +26682,8 @@ snapshots: picomatch@4.0.4: {} + picomatch@4.0.5: {} + pidtree@0.3.1: {} pify@2.3.0: {} @@ -26387,215 +26719,224 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@10.1.1(postcss@8.5.18): + postcss-calc@10.1.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 postcss-value-parser: 4.2.0 optional: true - postcss-colormin@7.0.6(postcss@8.5.18): + postcss-colormin@7.0.6(postcss@8.5.26): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.8 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-convert-values@7.0.9(postcss@8.5.18): + postcss-convert-values@7.0.9(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - postcss: 8.5.18 + browserslist: 4.28.8 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-discard-comments@7.0.6(postcss@8.5.18): + postcss-discard-comments@7.0.6(postcss@8.5.26): dependencies: - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true - postcss-discard-duplicates@7.0.2(postcss@8.5.18): + postcss-discard-comments@7.0.8(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true - postcss-discard-empty@7.0.1(postcss@8.5.18): + postcss-discard-duplicates@7.0.2(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 optional: true - postcss-discard-overridden@7.0.1(postcss@8.5.18): + postcss-discard-empty@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 optional: true - postcss-import@15.1.0(postcss@8.5.18): + postcss-discard-empty@7.0.3(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 + optional: true + + postcss-discard-overridden@7.0.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + optional: true + + postcss-import@15.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.11 - postcss-js@4.1.0(postcss@8.5.18): + postcss-js@4.1.0(postcss@8.5.26): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.18 + postcss: 8.5.26 - postcss-load-config@4.0.2(postcss@8.5.18)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.5.26)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.8.3 optionalDependencies: - postcss: 8.5.18 + postcss: 8.5.26 ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) - postcss-load-config@4.0.2(postcss@8.5.18)(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.5.26)(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.8.3 optionalDependencies: - postcss: 8.5.18 + postcss: 8.5.26 ts-node: 10.9.2(@types/node@25.9.3)(typescript@5.9.3) - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.18)(yaml@2.9.0): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.26)(yaml@2.9.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.6.1 - postcss: 8.5.18 + postcss: 8.5.26 yaml: 2.9.0 - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.20)(yaml@2.9.0): + postcss-merge-longhand@7.0.5(postcss@8.5.26): dependencies: - lilconfig: 3.1.3 - optionalDependencies: - jiti: 2.6.1 - postcss: 8.5.20 - yaml: 2.9.0 - - postcss-merge-longhand@7.0.5(postcss@8.5.18): - dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - stylehacks: 7.0.8(postcss@8.5.18) + stylehacks: 7.0.8(postcss@8.5.26) optional: true - postcss-merge-rules@7.0.8(postcss@8.5.18): + postcss-merge-rules@7.0.8(postcss@8.5.26): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.8 caniuse-api: 3.0.0 - cssnano-utils: 5.0.1(postcss@8.5.18) - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + cssnano-utils: 5.0.1(postcss@8.5.26) + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true - postcss-minify-font-values@7.0.1(postcss@8.5.18): + postcss-minify-font-values@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-minify-gradients@7.0.1(postcss@8.5.18): + postcss-minify-gradients@7.0.1(postcss@8.5.26): dependencies: colord: 2.9.3 - cssnano-utils: 5.0.1(postcss@8.5.18) - postcss: 8.5.18 + cssnano-utils: 5.0.1(postcss@8.5.26) + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-minify-params@7.0.6(postcss@8.5.18): + postcss-minify-params@7.0.6(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - cssnano-utils: 5.0.1(postcss@8.5.18) - postcss: 8.5.18 + browserslist: 4.28.8 + cssnano-utils: 5.0.1(postcss@8.5.26) + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-minify-selectors@7.0.6(postcss@8.5.18): + postcss-minify-selectors@7.0.6(postcss@8.5.26): dependencies: cssesc: 3.0.0 - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true - postcss-nested@6.2.0(postcss@8.5.18): + postcss-nested@6.2.0(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-selector-parser: 6.1.2 - postcss-normalize-charset@7.0.1(postcss@8.5.18): + postcss-normalize-charset@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 optional: true - postcss-normalize-display-values@7.0.1(postcss@8.5.18): + postcss-normalize-display-values@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-positions@7.0.1(postcss@8.5.18): + postcss-normalize-positions@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-repeat-style@7.0.1(postcss@8.5.18): + postcss-normalize-repeat-style@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-string@7.0.1(postcss@8.5.18): + postcss-normalize-string@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-timing-functions@7.0.1(postcss@8.5.18): + postcss-normalize-timing-functions@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-unicode@7.0.6(postcss@8.5.18): + postcss-normalize-unicode@7.0.6(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - postcss: 8.5.18 + browserslist: 4.28.8 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-url@7.0.1(postcss@8.5.18): + postcss-normalize-url@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-normalize-whitespace@7.0.1(postcss@8.5.18): + postcss-normalize-whitespace@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-ordered-values@7.0.2(postcss@8.5.18): + postcss-normalize-whitespace@7.0.3(postcss@8.5.26): dependencies: - cssnano-utils: 5.0.1(postcss@8.5.18) - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true - postcss-reduce-initial@7.0.6(postcss@8.5.18): + postcss-ordered-values@7.0.2(postcss@8.5.26): dependencies: - browserslist: 4.28.2 + cssnano-utils: 5.0.1(postcss@8.5.26) + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + optional: true + + postcss-reduce-initial@7.0.6(postcss@8.5.26): + dependencies: + browserslist: 4.28.8 caniuse-api: 3.0.0 - postcss: 8.5.18 + postcss: 8.5.26 optional: true - postcss-reduce-transforms@7.0.1(postcss@8.5.18): + postcss-reduce-transforms@7.0.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 optional: true @@ -26604,35 +26945,29 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.4: + postcss-selector-parser@7.1.5: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@7.1.1(postcss@8.5.18): + postcss-svgo@7.1.1(postcss@8.5.26): dependencies: - postcss: 8.5.18 + postcss: 8.5.26 postcss-value-parser: 4.2.0 svgo: 4.0.2 optional: true - postcss-unique-selectors@7.0.5(postcss@8.5.18): + postcss-unique-selectors@7.0.5(postcss@8.5.26): dependencies: - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true postcss-value-parser@4.2.0: {} - postcss@8.5.18: - dependencies: - nanoid: 3.3.14 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.20: + postcss@8.5.26: dependencies: - nanoid: 3.3.16 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -26650,9 +26985,9 @@ snapshots: postgres@3.4.7: {} - posthog-node@5.46.1(rxjs@7.8.2): + posthog-node@5.50.0(rxjs@7.8.2): dependencies: - '@posthog/core': 1.45.1 + '@posthog/core': 1.48.8 optionalDependencies: rxjs: 7.8.2 @@ -26672,14 +27007,14 @@ snapshots: posthtml-render: 3.0.0 optional: true - postman-collection@5.3.0: + postman-collection@5.3.1: dependencies: '@faker-js/faker': 5.5.3 file-type: 3.9.0 http-reasons: 0.1.0 iconv-lite: 0.6.3 liquid-json: 0.3.1 - lodash: 4.17.23 + lodash: 4.18.1 mime: 3.0.0 mime-format: 2.0.2 postman-url-encoder: 3.0.8 @@ -26702,16 +27037,10 @@ snapshots: dependencies: prettier: 3.9.6 - prettier-plugin-tailwindcss@0.7.2(prettier@3.8.5): - dependencies: - prettier: 3.8.5 - prettier-plugin-tailwindcss@0.7.2(prettier@3.9.6): dependencies: prettier: 3.9.6 - prettier@3.8.5: {} - prettier@3.9.6: {} pretty-bytes@5.6.0: {} @@ -26735,7 +27064,7 @@ snapshots: '@jest/schemas': 30.4.1 ansi-styles: 5.2.0 react-is-18: react-is@18.3.1 - react-is-19: react-is@19.2.7 + react-is-19: react-is@19.2.8 preview-email@3.1.1: dependencies: @@ -26749,14 +27078,14 @@ snapshots: p-event: 4.2.0 p-wait-for: 3.2.0 pug: 3.0.4 - uuid: 9.0.1 + uuid: 11.1.1 optional: true - prisma@7.9.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + prisma@7.9.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: - '@prisma/config': 7.9.0 - '@prisma/dev': 0.24.14(typescript@5.9.3) - '@prisma/engines': 7.9.0 + '@prisma/config': 7.9.1 + '@prisma/dev': 0.24.17(typescript@5.9.3) + '@prisma/engines': 7.9.1 '@prisma/studio-core': 0.33.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) mysql2: 3.15.3 postgres: 3.4.7 @@ -26783,6 +27112,9 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 + proto-list@1.2.4: + optional: true + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -26893,10 +27225,6 @@ snapshots: pvutils@1.1.5: {} - qs@6.15.0: - dependencies: - side-channel: 1.1.0 - qs@6.15.3: dependencies: es-define-property: 1.0.1 @@ -26949,7 +27277,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 unpipe: 1.0.0 rc9@3.0.1: @@ -26972,7 +27300,7 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.7: {} + react-is@19.2.8: {} react@19.2.4: {} @@ -27016,7 +27344,7 @@ snapshots: readdirp@4.1.2: {} - readdirp@5.0.0: {} + readdirp@5.1.1: {} redis-errors@1.2.0: optional: true @@ -27077,7 +27405,7 @@ snapshots: relay-runtime@12.0.0: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 fbjs: 3.0.5 invariant: 2.2.4 transitivePeerDependencies: @@ -27244,7 +27572,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.4.0 + path-to-regexp: 8.4.2 transitivePeerDependencies: - supports-color @@ -27304,13 +27632,13 @@ snapshots: safer-buffer@2.1.2: {} - sass@1.101.0: + sass@1.103.1: dependencies: chokidar: 5.0.0 - immutable: 5.1.6 + immutable: 5.1.9 source-map-js: 1.2.1 optionalDependencies: - '@parcel/watcher': 2.5.6 + '@parcel/watcher': 2.6.0 sax@1.6.0: {} @@ -27455,11 +27783,6 @@ snapshots: should-type-adaptors: 1.1.0 should-util: 1.0.1 - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -27480,14 +27803,6 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - side-channel@1.1.1: dependencies: es-errors: 1.3.0 @@ -27627,9 +27942,7 @@ snapshots: source-map@0.7.6: {} - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map@0.8.0: {} sourcemap-codec@1.4.8: {} @@ -27678,7 +27991,7 @@ snapshots: std-env@3.10.0: {} - std-env@4.1.0: {} + std-env@4.2.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -27833,11 +28146,11 @@ snapshots: style-mod@4.1.3: {} - stylehacks@7.0.8(postcss@8.5.18): + stylehacks@7.0.8(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - postcss: 8.5.18 - postcss-selector-parser: 7.1.4 + browserslist: 4.28.8 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 optional: true subscriptions-transport-ws@0.11.0(graphql@16.13.2): @@ -27937,7 +28250,7 @@ snapshots: transitivePeerDependencies: - openapi-types - swagger-ui-dist@5.32.8: + swagger-ui-dist@5.32.13: dependencies: '@scarf/scarf': 1.4.0 @@ -27995,11 +28308,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.18 - postcss-import: 15.1.0(postcss@8.5.18) - postcss-js: 4.1.0(postcss@8.5.18) - postcss-load-config: 4.0.2(postcss@8.5.18)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - postcss-nested: 6.2.0(postcss@8.5.18) + postcss: 8.5.26 + postcss-import: 15.1.0(postcss@8.5.26) + postcss-js: 4.1.0(postcss@8.5.26) + postcss-load-config: 4.0.2(postcss@8.5.26)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + postcss-nested: 6.2.0(postcss@8.5.26) postcss-selector-parser: 6.1.2 resolve: 1.22.11 sucrase: 3.35.1 @@ -28022,11 +28335,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.18 - postcss-import: 15.1.0(postcss@8.5.18) - postcss-js: 4.1.0(postcss@8.5.18) - postcss-load-config: 4.0.2(postcss@8.5.18)(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)) - postcss-nested: 6.2.0(postcss@8.5.18) + postcss: 8.5.26 + postcss-import: 15.1.0(postcss@8.5.26) + postcss-js: 4.1.0(postcss@8.5.26) + postcss-load-config: 4.0.2(postcss@8.5.26)(ts-node@10.9.2(@types/node@25.9.3)(typescript@5.9.3)) + postcss-nested: 6.2.0(postcss@8.5.26) postcss-selector-parser: 6.1.2 resolve: 1.22.11 sucrase: 3.35.1 @@ -28067,7 +28380,7 @@ snapshots: terser@5.46.1: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.17.0 + acorn: 8.18.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -28103,7 +28416,7 @@ snapshots: tinyexec@1.1.1: {} - tinyexec@1.2.4: {} + tinyexec@1.3.0: {} tinyglobby@0.2.15: dependencies: @@ -28112,10 +28425,10 @@ snapshots: tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} tippy.js@6.3.7: dependencies: @@ -28167,10 +28480,6 @@ snapshots: tr46@0.0.3: {} - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tr46@6.0.0: dependencies: punycode: 2.3.1 @@ -28243,7 +28552,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 24.10.1 - acorn: 8.16.0 + acorn: 8.18.0 acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 @@ -28262,7 +28571,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 25.9.3 - acorn: 8.16.0 + acorn: 8.18.0 acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 @@ -28296,35 +28605,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.18)(typescript@5.9.3)(yaml@2.9.0): - dependencies: - bundle-require: 5.1.0(esbuild@0.27.4) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.3(supports-color@8.1.1) - esbuild: 0.27.4 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.18)(yaml@2.9.0) - resolve-from: 5.0.0 - rollup: 4.59.0 - source-map: 0.7.6 - sucrase: 3.35.1 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.5.18 - typescript: 5.9.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.20)(typescript@5.9.3)(yaml@2.9.0): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.26)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.4) cac: 6.7.14 @@ -28335,7 +28616,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.20)(yaml@2.9.0) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.26)(yaml@2.9.0) resolve-from: 5.0.0 rollup: 4.59.0 source-map: 0.7.6 @@ -28344,7 +28625,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.5.20 + postcss: 8.5.26 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -28427,12 +28708,12 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.67.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -28472,6 +28753,9 @@ snapshots: undici-types@7.24.6: {} + undici@6.28.0: + optional: true + unhead@2.1.12: dependencies: hookable: 6.1.0 @@ -28515,19 +28799,19 @@ snapshots: unpipe@1.0.0: {} - unplugin-fonts@1.4.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + unplugin-fonts@1.4.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: fast-glob: 3.3.3 unplugin: 2.3.5 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - unplugin-fonts@1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + unplugin-fonts@1.4.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: fast-glob: 3.3.3 unplugin: 2.3.5 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.40)(svelte@3.59.2)(vue-template-compiler@2.7.16): + unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.41)(svelte@3.59.2)(vue-template-compiler@2.7.16): dependencies: '@antfu/install-pkg': 1.1.0 '@iconify/utils': 3.1.0 @@ -28535,7 +28819,7 @@ snapshots: local-pkg: 1.1.2 unplugin: 2.3.11 optionalDependencies: - '@vue/compiler-sfc': 3.5.40 + '@vue/compiler-sfc': 3.5.41 svelte: 3.59.2 vue-template-compiler: 2.7.16 transitivePeerDependencies: @@ -28546,7 +28830,7 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.4 - unplugin-vue-components@30.0.0(@babel/parser@7.29.7)(vue@3.5.40(typescript@5.9.3)): + unplugin-vue-components@30.0.0(@babel/parser@7.29.8)(vue@3.5.41(typescript@5.9.3)): dependencies: chokidar: 4.0.3 debug: 4.4.3(supports-color@8.1.1) @@ -28556,27 +28840,27 @@ snapshots: tinyglobby: 0.2.15 unplugin: 2.3.11 unplugin-utils: 0.3.1 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) optionalDependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 transitivePeerDependencies: - supports-color unplugin@2.2.2: dependencies: - acorn: 8.17.0 + acorn: 8.18.0 webpack-virtual-modules: 0.6.2 unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 - acorn: 8.17.0 + acorn: 8.18.0 picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 unplugin@2.3.5: dependencies: - acorn: 8.17.0 + acorn: 8.18.0 picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 @@ -28618,9 +28902,9 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.7): + update-browserslist-db@1.3.1(browserslist@4.28.8): dependencies: - browserslist: 4.28.7 + browserslist: 4.28.8 escalade: 3.2.0 picocolors: 1.1.1 @@ -28665,13 +28949,13 @@ snapshots: utils-merge@1.0.1: {} + uuid@11.1.1: + optional: true + uuid@13.0.0: {} uuid@8.3.2: {} - uuid@9.0.1: - optional: true - v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: @@ -28680,7 +28964,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - valibot@1.2.0(typescript@5.9.3): + valibot@1.4.2(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -28702,17 +28986,17 @@ snapshots: dependencies: zod: 3.25.32 - vite-dev-rpc@2.0.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-dev-rpc@2.0.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: birpc: 4.0.0 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vite-hot-client: 2.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vite-hot-client: 2.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) - vite-hot-client@2.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-hot-client@2.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-checker@0.12.0(eslint@9.39.2(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-tsc@1.8.8(typescript@5.9.3)): + vite-plugin-checker@0.12.0(eslint@9.39.2(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-tsc@1.8.8(typescript@5.9.3)): dependencies: '@babel/code-frame': 7.29.0 chokidar: 4.0.3 @@ -28721,7 +29005,7 @@ snapshots: picomatch: 4.0.4 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.39.2(jiti@2.6.1) @@ -28730,57 +29014,57 @@ snapshots: typescript: 5.9.3 vue-tsc: 1.8.8(typescript@5.9.3) - vite-plugin-eslint@1.8.1(eslint@10.7.0(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-eslint@1.8.1(eslint@10.8.1(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 4.2.1 '@types/eslint': 8.56.12 - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) rollup: 2.80.0 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-eslint@1.8.1(eslint@10.7.0(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-eslint@1.8.1(eslint@10.8.1(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 4.2.1 '@types/eslint': 8.56.12 - eslint: 10.7.0(jiti@2.6.1) + eslint: 10.8.1(jiti@2.6.1) rollup: 2.80.0 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-eslint@1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-eslint@1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 4.2.1 '@types/eslint': 8.56.12 eslint: 9.39.2(jiti@2.6.1) rollup: 2.80.0 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-eslint@1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-eslint@1.8.1(eslint@9.39.2(jiti@2.6.1))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 4.2.1 '@types/eslint': 8.56.12 eslint: 9.39.2(jiti@2.6.1) rollup: 2.80.0 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-fonts@0.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-fonts@0.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: fast-glob: 3.3.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-fonts@0.7.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-fonts@0.7.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: fast-glob: 3.3.3 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-html-config@2.0.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-html-config@2.0.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-html-config@2.0.2(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-html-config@2.0.2(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-inspect@11.4.1(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-inspect@11.4.1(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: ansis: 4.3.1 error-stack-parser-es: 1.0.5 @@ -28790,15 +29074,15 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vite-dev-rpc: 2.0.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) vite-plugin-pages-sitemap@1.7.1: dependencies: sitemap: 8.0.3 xml-formatter: 3.7.0 - vite-plugin-pages@0.33.2(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))): + vite-plugin-pages@0.33.2(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))): dependencies: '@types/debug': 4.1.13 debug: 4.4.3(supports-color@8.1.1) @@ -28809,15 +29093,15 @@ snapshots: micromatch: 4.0.8 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) yaml: 2.8.3 optionalDependencies: - '@vue/compiler-sfc': 3.5.40 - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + '@vue/compiler-sfc': 3.5.41 + vue-router: 4.6.4(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - supports-color - vite-plugin-pages@0.33.3(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))): + vite-plugin-pages@0.33.3(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))): dependencies: '@types/debug': 4.1.13 debug: 4.4.3(supports-color@8.1.1) @@ -28828,15 +29112,15 @@ snapshots: micromatch: 4.0.8 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) yaml: 2.8.3 optionalDependencies: - '@vue/compiler-sfc': 3.5.40 - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + '@vue/compiler-sfc': 3.5.41 + vue-router: 4.6.4(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - supports-color - vite-plugin-pages@0.33.3(@vue/compiler-sfc@3.5.40)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3))): + vite-plugin-pages@0.33.3(@vue/compiler-sfc@3.5.41)(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3))): dependencies: '@types/debug': 4.1.13 debug: 4.4.3(supports-color@8.1.1) @@ -28847,181 +29131,209 @@ snapshots: micromatch: 4.0.8 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) yaml: 2.8.3 optionalDependencies: - '@vue/compiler-sfc': 3.5.40 - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + '@vue/compiler-sfc': 3.5.41 + vue-router: 4.6.4(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - supports-color - vite-plugin-pwa@1.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1): + vite-plugin-pwa@1.3.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1): dependencies: debug: 4.4.3(supports-color@8.1.1) pretty-bytes: 6.1.1 - tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + tinyglobby: 0.2.17 + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) workbox-build: 7.4.0(@types/babel__core@7.20.5) workbox-window: 7.4.1 transitivePeerDependencies: - supports-color - vite-plugin-pwa@1.2.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1): + vite-plugin-pwa@1.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.1): dependencies: debug: 4.4.3(supports-color@8.1.1) pretty-bytes: 6.1.1 - tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + tinyglobby: 0.2.17 + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) workbox-build: 7.4.0(@types/babel__core@7.20.5) workbox-window: 7.4.1 transitivePeerDependencies: - supports-color - vite-plugin-static-copy@3.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vite-plugin-static-copy@3.3.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: chokidar: 3.6.0 p-map: 7.0.4 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) - vite-plugin-vue-layouts@0.11.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)): + vite-plugin-vue-layouts@0.11.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)): dependencies: debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue: 3.5.41(typescript@5.9.3) + vue-router: 4.6.4(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - supports-color - vite-plugin-vue-layouts@0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)): + vite-plugin-vue-layouts@0.11.0(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))(vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)))(vue@3.5.41(typescript@5.9.3)): dependencies: debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + vue: 3.5.41(typescript@5.9.3) + vue-router: 4.6.4(vue@3.5.41(typescript@5.9.3)) transitivePeerDependencies: - supports-color - vite@3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1): + vite@3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1): dependencies: esbuild: 0.15.18 - postcss: 8.5.18 + postcss: 8.5.26 resolve: 1.22.11 rollup: 2.80.0 optionalDependencies: '@types/node': 25.9.3 fsevents: 2.3.3 - sass: 1.101.0 + sass: 1.103.1 terser: 5.46.1 - vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0): + vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.18 + postcss: 8.5.26 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.1 fsevents: 2.3.3 jiti: 2.6.1 - sass: 1.101.0 + sass: 1.103.1 terser: 5.46.1 yaml: 2.9.0 - vite@7.3.2(@types/node@24.9.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0): + vite@7.3.2(@types/node@24.9.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.18 + postcss: 8.5.26 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.9.1 fsevents: 2.3.3 jiti: 2.6.1 - sass: 1.101.0 + sass: 1.103.1 terser: 5.46.1 yaml: 2.9.0 - vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0): + vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.18 + postcss: 8.5.26 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.9.3 fsevents: 2.3.3 jiti: 2.6.1 - sass: 1.101.0 + sass: 1.103.1 terser: 5.46.1 yaml: 2.9.0 - vitefu@0.2.5(vite@3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1)): + vitefu@0.2.5(vite@3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1)): optionalDependencies: - vite: 3.2.11(@types/node@25.9.3)(sass@1.101.0)(terser@5.46.1) + vite: 3.2.11(@types/node@25.9.3)(sass@1.103.1)(terser@5.46.1) - vitest@4.1.10(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): + vitest@4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.1.4 pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.9.3 + jsdom: 27.4.0(@noble/hashes@2.3.0) + transitivePeerDependencies: + - msw + + vitest@4.1.11(@types/node@24.10.1)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + tinyrainbow: 3.1.1 + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.1 - jsdom: 27.4.0(@noble/hashes@2.2.0) + jsdom: 27.4.0(@noble/hashes@2.3.0) transitivePeerDependencies: - msw - vitest@4.1.10(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.10 - '@vitest/runner': 4.1.10 - '@vitest/snapshot': 4.1.10 - '@vitest/spy': 4.1.10 - '@vitest/utils': 4.1.10 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 + vitest@4.1.11(@types/node@25.9.3)(jsdom@27.4.0(@noble/hashes@2.3.0))(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.1.4 pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.101.0)(terser@5.46.1)(yaml@2.9.0) + tinyrainbow: 3.1.1 + vite: 7.3.2(@types/node@25.9.3)(jiti@2.6.1)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.3 - jsdom: 27.4.0(@noble/hashes@2.2.0) + jsdom: 27.4.0(@noble/hashes@2.3.0) transitivePeerDependencies: - msw @@ -29032,9 +29344,9 @@ snapshots: vscode-uri@3.1.0: {} - vue-demi@0.14.10(vue@3.5.40(typescript@5.9.3)): + vue-demi@0.14.10(vue@3.5.41(typescript@5.9.3)): dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) vue-eslint-parser@10.4.1(eslint@9.39.2(jiti@2.6.1)): dependencies: @@ -29048,43 +29360,43 @@ snapshots: transitivePeerDependencies: - supports-color - vue-i18n@11.4.6(vue@3.5.40(typescript@5.9.3)): + vue-i18n@11.4.8(vue@3.5.41(typescript@5.9.3)): dependencies: - '@intlify/core-base': 11.4.6 - '@intlify/devtools-types': 11.4.6 - '@intlify/shared': 11.4.6 + '@intlify/core-base': 11.4.8 + '@intlify/devtools-types': 11.4.8 + '@intlify/shared': 11.4.8 '@vue/devtools-api': 6.6.4 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) - vue-json-pretty@2.6.0(vue@3.5.40(typescript@5.9.3)): + vue-json-pretty@2.6.0(vue@3.5.41(typescript@5.9.3)): dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) - vue-pdf-embed@2.1.5(vue@3.5.40(typescript@5.9.3)): + vue-pdf-embed@2.1.5(vue@3.5.41(typescript@5.9.3)): dependencies: pdfjs-dist: 5.7.284 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) vue-promise-modals@0.1.0(typescript@5.9.3): dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) transitivePeerDependencies: - typescript - vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)): + vue-router@4.6.4(vue@3.5.41(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) vue-template-compiler@2.7.16: dependencies: de-indent: 1.0.2 he: 1.2.0 - vue-tippy@6.7.1(vue@3.5.40(typescript@5.9.3)): + vue-tippy@6.7.1(vue@3.5.41(typescript@5.9.3)): dependencies: tippy.js: 6.3.7 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) vue-tsc@1.8.8(typescript@5.9.3): dependencies: @@ -29106,20 +29418,20 @@ snapshots: '@vue/language-core': 2.2.0(typescript@5.9.3) typescript: 5.9.3 - vue@3.5.40(typescript@5.9.3): + vue@3.5.41(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.40 - '@vue/compiler-sfc': 3.5.40 - '@vue/runtime-dom': 3.5.40 - '@vue/server-renderer': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-sfc': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/server-renderer': 3.5.41 + '@vue/shared': 3.5.41 optionalDependencies: typescript: 5.9.3 - vuedraggable-es@4.1.1(vue@3.5.40(typescript@5.9.3)): + vuedraggable-es@4.1.1(vue@3.5.41(typescript@5.9.3)): dependencies: sortablejs: 1.14.0 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.41(typescript@5.9.3) w3c-keyname@2.2.8: {} @@ -29140,16 +29452,13 @@ snapshots: dependencies: defaults: 1.0.4 - web-resource-inliner@6.0.1: + web-resource-inliner@8.0.0: dependencies: ansi-colors: 4.1.3 escape-goat: 3.0.0 - htmlparser2: 5.0.1 + htmlparser2: 9.1.0 mime: 2.6.0 - node-fetch: 2.7.0 valid-data-url: 3.0.1 - transitivePeerDependencies: - - encoding optional: true web-streams-polyfill@3.3.3: {} @@ -29164,8 +29473,6 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@4.0.2: {} - webidl-conversions@8.0.1: {} webpack-node-externals@3.0.0: {} @@ -29182,8 +29489,8 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.17.0 - acorn-import-phases: 1.0.4(acorn@8.17.0) + acorn: 8.18.0 + acorn-import-phases: 1.0.4(acorn@8.18.0) browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.20.1 @@ -29209,6 +29516,11 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + optional: true + whatwg-mimetype@4.0.0: {} whatwg-mimetype@5.0.0: {} @@ -29223,12 +29535,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -29297,8 +29603,8 @@ snapshots: with@7.0.2: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 assert-never: 1.4.0 babel-walk: 3.0.0-canary-5 optional: true @@ -29337,7 +29643,7 @@ snapshots: lodash: 4.18.1 pretty-bytes: 5.6.0 rollup: 2.80.0 - source-map: 0.8.0-beta.0 + source-map: 0.8.0 stringify-object: 3.3.0 strip-comments: 2.0.1 tempy: 0.6.0 @@ -29473,8 +29779,6 @@ snapshots: dependencies: xml-parser-xo: 4.1.5 - xml-name-validator@4.0.0: {} - xml-name-validator@5.0.0: {} xml-parser-xo@4.1.5: {} @@ -29489,7 +29793,7 @@ snapshots: '@oozcitak/dom': 2.0.2 '@oozcitak/infra': 2.0.2 '@oozcitak/util': 10.0.0 - js-yaml: 4.3.0 + js-yaml: 4.3.1 xmlbuilder@11.0.1: {} diff --git a/prod.Dockerfile b/prod.Dockerfile index 2a34f140a9b..97222261d12 100644 --- a/prod.Dockerfile +++ b/prod.Dockerfile @@ -5,14 +5,14 @@ FROM alpine:3.24.1 AS go_builder RUN apk add --no-cache curl git openssh-client ARG TARGETARCH -ENV GOLANG_VERSION=1.26.5 +ENV GOLANG_VERSION=1.26.7 # Download Go tarball RUN case "${TARGETARCH}" in amd64) GOARCH=amd64 ;; arm64) GOARCH=arm64 ;; *) echo "Unsupported arch: ${TARGETARCH}" && exit 1 ;; esac && \ curl -fsSL "https://go.dev/dl/go${GOLANG_VERSION}.linux-${GOARCH}.tar.gz" -o go.tar.gz # Checksum verification of Go tarball RUN case "${TARGETARCH}" in \ - amd64) expected="5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053" ;; \ - arm64) expected="fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd696d49" ;; \ + amd64) expected="ffb5f8de10c62550dfddab66b36b57030721e0a44a3218e9e1181d7b59f121ca" ;; \ + arm64) expected="5a4ec883379d51ee9ce1040d5e87f8d35e20387574dd8c947feb01eabc3c1b37" ;; \ esac && \ actual=$(sha256sum go.tar.gz | cut -d' ' -f1) && \ [ "$actual" = "$expected" ] && \ @@ -43,6 +43,10 @@ RUN tar -xzf /tmp/caddy-build/src.tar.gz && \ go get google.golang.org/grpc@v1.82.1 && \ # Fix CVE-2026-34986: upgrade go-jose v3 (HIGH - DoS via crafted JWE) go get github.com/go-jose/go-jose/v3@v3.0.5 && \ + # Fix CVE-2026-46600: upgrade golang.org/x/net v0.56.0 (HIGH - panic on invalid DNS RR) + go get golang.org/x/net@v0.56.0 && \ + # Fix CVE-2026-56852: upgrade golang.org/x/text v0.39.0 (HIGH - infinite loop on input) + go get golang.org/x/text@v0.39.0 && \ # Clean up any existing vendor directory and regenerate with updated deps rm -rf vendor && \ go mod tidy && \ @@ -67,15 +71,17 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o webapp-server . # Shared Node.js base with optimized NPM installation FROM alpine:3.24.1 AS node_base # Install dependencies +# Version floors for CVE-2026-11856 (curl) and the nodejs 24.18.1-r0 batch. The base +# tag ships older builds and this layer is cached, so the bound forces a re-resolve. RUN apk upgrade --no-cache && \ - apk add --no-cache nodejs curl bash tini ca-certificates + apk add --no-cache "nodejs>=24.18.1-r0" "curl>=8.21.0-r0" bash tini ca-certificates # Set working directory for NPM installation RUN mkdir -p /tmp/npm-install WORKDIR /tmp/npm-install # Download NPM tarball -RUN curl -fsSL https://registry.npmjs.org/npm/-/npm-11.18.0.tgz -o npm.tgz +RUN curl -fsSL https://registry.npmjs.org/npm/-/npm-11.19.0.tgz -o npm.tgz # Verify checksum -RUN expected="73f6155215ebabf4ed96dca1f567c2372cc713c33af2e5b9b62fde4e92373e2e" \ +RUN expected="31e9770f7dc71119a58509353b27917557aaf0ac9b5ef1a0465ee7d8ec67ae75" \ && actual=$(sha256sum npm.tgz | cut -d' ' -f1) \ && [ "$actual" = "$expected" ] \ && echo "✅ NPM Tarball Checksum OK" \ @@ -87,17 +93,20 @@ RUN tar -xzf npm.tgz && \ cd / && \ rm -rf /tmp/npm-install RUN mkdir -p /tmp/pnpm-install && cd /tmp/pnpm-install && \ - curl -fsSL https://registry.npmjs.org/pnpm/-/pnpm-10.34.2.tgz -o pnpm.tgz && \ + curl -fsSL https://registry.npmjs.org/pnpm/-/pnpm-10.34.5.tgz -o pnpm.tgz && \ curl -fsSL https://registry.npmjs.org/@import-meta-env/cli/-/cli-0.7.4.tgz -o cli.tgz && \ - echo "06e0108a4941de2d709e1c3bc841d3e90c45c6a26cecac76f62044fa02cac1a0 pnpm.tgz" | sha256sum -c - && \ + echo "ccb5c479cab1b00621325bfe7d4c9a8a8031e7a525d7249e275ecbec81b08db2 pnpm.tgz" | sha256sum -c - && \ echo "9edada700b616b4224ba69ce713e68c36e22cb2548be9134dd3af00c164d8ca0 cli.tgz" | sha256sum -c - && \ npm install -g ./pnpm.tgz ./cli.tgz && \ cd / && rm -rf /tmp/pnpm-install -# Fix CVE-2026-12151: replace vulnerable undici bundled in npm (ships 6.26.0, fix requires >=6.27.0) +# Fix the undici advisories that supersede CVE-2026-12151 (response desynchronization +# via the retry interceptor, CRLF injection via a blob-like body `type`, and cookie +# attribute injection). npm now bundles 6.27.0, which is still affected — all three +# fixes first land in 6.28.0, so replace the bundled copy with that. RUN mkdir -p /tmp/undici-fix && \ cd /tmp/undici-fix && \ - npm install undici@6.27.0 && \ + npm install undici@6.28.0 && \ rm -rf /usr/lib/node_modules/npm/node_modules/undici && \ cp -r node_modules/undici /usr/lib/node_modules/npm/node_modules/ && \ rm -rf /tmp/undici-fix @@ -118,13 +127,14 @@ RUN mkdir -p /tmp/serialize-fix && \ cp -r node_modules/serialize-javascript /usr/lib/node_modules/@import-meta-env/cli/node_modules/ && \ rm -rf /tmp/serialize-fix -# Fix CVE-2026-14257: brace-expansion <5.0.8 allows a DoS (unbounded expansion -# length → OOM crash). Every version below 5.0.8 is affected with no per-line -# backport, so replace all bundled/transitive copies (npm ships 5.0.7; the -# @import-meta-env/cli tree pulls an older copy) with the fixed 5.0.8. +# Fix the brace-expansion DoS chain: CVE-2026-14257 (unbounded expansion length → +# OOM crash) plus the follow-up HIGH advisory that 5.0.8 only partially mitigated — +# unbounded intermediate arrays still crash every version <5.0.9. No per-line +# backport exists, so replace all bundled/transitive copies (npm ships 5.0.7; the +# @import-meta-env/cli tree pulls an older copy) with the fixed 5.0.9. RUN mkdir -p /tmp/brace-fix && \ cd /tmp/brace-fix && \ - npm install brace-expansion@5.0.8 && \ + npm install brace-expansion@5.0.9 && \ find /usr/lib/node_modules -type d -name brace-expansion -not -path '*/brace-fix/*' | \ while read -r dir; do \ rm -rf "$dir" && \ @@ -132,9 +142,20 @@ RUN mkdir -p /tmp/brace-fix && \ done && \ rm -rf /tmp/brace-fix +# Fix CVE-2026-69192: npm and pnpm both bundle ip-address 10.2.0 (affected <=10.3.0) +RUN mkdir -p /tmp/ip-fix && \ + cd /tmp/ip-fix && \ + npm install ip-address@10.5.0 && \ + find /usr/lib/node_modules -type d -name ip-address -not -path '*/ip-fix/*' | \ + while read -r dir; do \ + rm -rf "$dir" && \ + cp -r /tmp/ip-fix/node_modules/ip-address "$dir"; \ + done && \ + rm -rf /tmp/ip-fix + # Fix multiple tar advisories (CVE-2026-59873 and the GHSA-r292-9mhp-454m family): -# every tar <7.5.22 is affected. Both the bundled npm (ships 7.5.19) and pnpm -# (ships 7.5.15) copies are vulnerable, so replace all bundled copies with the +# every tar <7.5.22 is affected. Both the bundled npm and pnpm copies still ship +# 7.5.19 and are vulnerable, so replace all bundled copies with the # fixed 7.5.22. tar 7.5.x is a patch line (identical deps, pure JS), so the swap # is a safe drop-in that keeps npm/pnpm working. RUN mkdir -p /tmp/tar-fix && \ From 823d8d6bec16967f8151190bad58ad38ea3bc98d Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:53:11 +0530 Subject: [PATCH 07/14] chore: bump version to `2026.8.0` --- packages/hoppscotch-backend/package.json | 2 +- packages/hoppscotch-common/package.json | 2 +- packages/hoppscotch-common/src/platform/instance.ts | 2 +- packages/hoppscotch-desktop/package.json | 2 +- packages/hoppscotch-desktop/src-tauri/Cargo.lock | 2 +- packages/hoppscotch-desktop/src-tauri/Cargo.toml | 2 +- packages/hoppscotch-desktop/src-tauri/tauri.conf.json | 2 +- .../src-tauri/tauri.portable.macos.conf.json | 2 +- .../src-tauri/tauri.portable.windows.conf.json | 2 +- .../src/composables/__tests__/useAppInitialization.spec.ts | 6 +++--- packages/hoppscotch-desktop/src/views/Home.vue | 2 +- packages/hoppscotch-selfhost-web/package.json | 2 +- .../webapp-server/internal/bundle/types.go | 2 +- packages/hoppscotch-sh-admin/package.json | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index ad8a7bacebd..25c961b1de0 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -1,6 +1,6 @@ { "name": "hoppscotch-backend", - "version": "2026.7.0", + "version": "2026.8.0", "description": "", "author": "", "private": true, diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index 6ddcf203b2f..ea9006adb7e 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -1,7 +1,7 @@ { "name": "@hoppscotch/common", "private": true, - "version": "2026.7.0", + "version": "2026.8.0", "scripts": { "dev": "pnpm exec npm-run-all -p -l dev:*", "test": "vitest --run", diff --git a/packages/hoppscotch-common/src/platform/instance.ts b/packages/hoppscotch-common/src/platform/instance.ts index 41116ad4ede..61f246c614d 100644 --- a/packages/hoppscotch-common/src/platform/instance.ts +++ b/packages/hoppscotch-common/src/platform/instance.ts @@ -18,7 +18,7 @@ export const VENDORED_INSTANCE_CONFIG: Instance = { kind: "vendored" as const, serverUrl: "app://hoppscotch", displayName: "Hoppscotch Desktop", - version: "26.7.0", + version: "26.8.0", lastUsed: new Date().toISOString(), bundleName: "Hoppscotch", } diff --git a/packages/hoppscotch-desktop/package.json b/packages/hoppscotch-desktop/package.json index 41fba6b02d2..0e3f55d06aa 100644 --- a/packages/hoppscotch-desktop/package.json +++ b/packages/hoppscotch-desktop/package.json @@ -1,7 +1,7 @@ { "name": "hoppscotch-desktop", "private": true, - "version": "26.7.0", + "version": "26.8.0", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/hoppscotch-desktop/src-tauri/Cargo.lock b/packages/hoppscotch-desktop/src-tauri/Cargo.lock index 37ad13acd36..782bd8dc503 100644 --- a/packages/hoppscotch-desktop/src-tauri/Cargo.lock +++ b/packages/hoppscotch-desktop/src-tauri/Cargo.lock @@ -2324,7 +2324,7 @@ dependencies = [ [[package]] name = "hoppscotch-desktop" -version = "26.7.0" +version = "26.8.0" dependencies = [ "axum", "dirs 6.0.0", diff --git a/packages/hoppscotch-desktop/src-tauri/Cargo.toml b/packages/hoppscotch-desktop/src-tauri/Cargo.toml index 7ea800346d3..a0e512ece1f 100644 --- a/packages/hoppscotch-desktop/src-tauri/Cargo.toml +++ b/packages/hoppscotch-desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hoppscotch-desktop" -version = "26.7.0" +version = "26.8.0" description = "Desktop App for hoppscotch.io" authors = ["CuriousCorrelation"] edition = "2021" diff --git a/packages/hoppscotch-desktop/src-tauri/tauri.conf.json b/packages/hoppscotch-desktop/src-tauri/tauri.conf.json index e1cab06a942..0425636202c 100644 --- a/packages/hoppscotch-desktop/src-tauri/tauri.conf.json +++ b/packages/hoppscotch-desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Hoppscotch", - "version": "26.7.0", + "version": "26.8.0", "identifier": "io.hoppscotch.desktop", "build": { "beforeDevCommand": "pnpm dev", diff --git a/packages/hoppscotch-desktop/src-tauri/tauri.portable.macos.conf.json b/packages/hoppscotch-desktop/src-tauri/tauri.portable.macos.conf.json index 20771adaabb..4f48520ca56 100644 --- a/packages/hoppscotch-desktop/src-tauri/tauri.portable.macos.conf.json +++ b/packages/hoppscotch-desktop/src-tauri/tauri.portable.macos.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Hoppscotch", - "version": "26.7.0", + "version": "26.8.0", "identifier": "io.hoppscotch.desktop", "build": { "beforeDevCommand": "pnpm dev", diff --git a/packages/hoppscotch-desktop/src-tauri/tauri.portable.windows.conf.json b/packages/hoppscotch-desktop/src-tauri/tauri.portable.windows.conf.json index 26cff21750b..9d375add4f5 100644 --- a/packages/hoppscotch-desktop/src-tauri/tauri.portable.windows.conf.json +++ b/packages/hoppscotch-desktop/src-tauri/tauri.portable.windows.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Hoppscotch", - "version": "26.7.0", + "version": "26.8.0", "identifier": "io.hoppscotch.desktop", "build": { "beforeDevCommand": "pnpm dev", diff --git a/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts b/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts index 814a7beaac5..9327e6b61e0 100644 --- a/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts +++ b/packages/hoppscotch-desktop/src/composables/__tests__/useAppInitialization.spec.ts @@ -10,7 +10,7 @@ const { load, download, close } = vi.hoisted(() => ({ >(async () => ({ success: true, windowLabel: "instance" })), download: vi.fn<(opts: { serverUrl: string }) => Promise>( async () => ({ - version: "26.7.0", + version: "26.8.0", bundleName: "acme", }) ), @@ -20,7 +20,7 @@ const { load, download, close } = vi.hoisted(() => ({ vi.mock("@hoppscotch/plugin-appload", () => ({ load, download, close })) vi.mock("@tauri-apps/api/app", () => ({ - getVersion: async () => "26.7.0", + getVersion: async () => "26.8.0", })) vi.mock("@tauri-apps/api/core", () => ({ @@ -77,7 +77,7 @@ const ORG_INSTANCE: Instance = { kind: "cloud-org", serverUrl: "https://acme.example.com", displayName: "Acme", - version: "26.7.0", + version: "26.8.0", lastUsed: "2026-08-20T00:00:00.000Z", bundleName: "Hoppscotch", } diff --git a/packages/hoppscotch-desktop/src/views/Home.vue b/packages/hoppscotch-desktop/src/views/Home.vue index 46d77b516b5..499d8422112 100644 --- a/packages/hoppscotch-desktop/src/views/Home.vue +++ b/packages/hoppscotch-desktop/src/views/Home.vue @@ -307,7 +307,7 @@ const loadVendored = async () => { const vendoredInstance: VendoredInstance = { type: "vendored", displayName: "Hoppscotch", - version: "26.7.0", + version: "26.8.0", } const connectionState: ConnectionState = { diff --git a/packages/hoppscotch-selfhost-web/package.json b/packages/hoppscotch-selfhost-web/package.json index 432613ca9a5..2d46c1e78dc 100644 --- a/packages/hoppscotch-selfhost-web/package.json +++ b/packages/hoppscotch-selfhost-web/package.json @@ -1,7 +1,7 @@ { "name": "@hoppscotch/selfhost-web", "private": true, - "version": "2026.7.0", + "version": "2026.8.0", "type": "module", "scripts": { "dev:vite": "vite", diff --git a/packages/hoppscotch-selfhost-web/webapp-server/internal/bundle/types.go b/packages/hoppscotch-selfhost-web/webapp-server/internal/bundle/types.go index 92380a90aa5..79750264670 100644 --- a/packages/hoppscotch-selfhost-web/webapp-server/internal/bundle/types.go +++ b/packages/hoppscotch-selfhost-web/webapp-server/internal/bundle/types.go @@ -3,7 +3,7 @@ package bundle import "time" const ( - Version = "2026.7.0" + Version = "2026.8.0" DefaultMaxSize = 50 * 1024 * 1024 diff --git a/packages/hoppscotch-sh-admin/package.json b/packages/hoppscotch-sh-admin/package.json index b18c66ec16e..71fadbe5e95 100644 --- a/packages/hoppscotch-sh-admin/package.json +++ b/packages/hoppscotch-sh-admin/package.json @@ -1,7 +1,7 @@ { "name": "hoppscotch-sh-admin", "private": true, - "version": "2026.7.0", + "version": "2026.8.0", "type": "module", "scripts": { "dev": "pnpm exec npm-run-all -p -l dev:*", From 8014beddae54ab3158c66d1ea38543e3e0952a19 Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:14:57 +0530 Subject: [PATCH 08/14] test(cli): temporarily disable echo-dependent e2e suite echo.hoppscotch.io is failing service-side; re-enable once it recovers. --- packages/hoppscotch-cli/vitest.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/hoppscotch-cli/vitest.config.ts b/packages/hoppscotch-cli/vitest.config.ts index bac92a0ec41..697aa8b9569 100644 --- a/packages/hoppscotch-cli/vitest.config.ts +++ b/packages/hoppscotch-cli/vitest.config.ts @@ -9,6 +9,8 @@ export default defineConfig({ "**/node_modules/**", "**/dist/**", "**/src/__tests__/functions/**/*.ts", + // echo.hoppscotch.io failing service-side, breaking these live e2e tests; re-enable once echo recovers. + "**/src/__tests__/e2e/**", ], }, }); From 469c90234af450be11ff99f82d84f983f06c0c0b Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:59:05 +0530 Subject: [PATCH 09/14] fix(common): preserve request tab description on documentation save (#6592) Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com> --- .../components/collections/SaveRequest.vue | 38 ++ .../collections/documentation/index.vue | 69 +++- .../src/helpers/import-export/import/index.ts | 8 + .../src/newstore/collections.ts | 17 + .../hoppscotch-common/src/pages/import.vue | 14 +- .../searchers/collections.searcher.ts | 36 +- .../tab/__tests__/rest-tab.service.spec.ts | 339 ++++++++++++++++++ .../src/services/tab/rest.ts | 79 ++-- 8 files changed, 555 insertions(+), 45 deletions(-) create mode 100644 packages/hoppscotch-common/src/services/tab/__tests__/rest-tab.service.spec.ts diff --git a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue index cc41478b809..cd9f991067d 100644 --- a/packages/hoppscotch-common/src/components/collections/SaveRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/SaveRequest.vue @@ -122,6 +122,7 @@ import { useToast } from "@composables/toast" import { HoppGQLRequest, HoppRESTRequest, + generateUniqueRefId, isHoppRESTRequest, } from "@hoppscotch/data" import { computedWithControl } from "@vueuse/core" @@ -144,6 +145,8 @@ import { cascadeParentCollectionForProperties, editGraphqlRequest, editRESTRequest, + navigateToFolderWithIndexPath, + restCollectionStore, saveGraphqlRequestAs, saveRESTRequestAs, } from "~/newstore/collections" @@ -338,6 +341,18 @@ const saveRequestAs = async () => { requestUpdated.name = requestName.value + // A copy is a new entry and needs its own `_ref_id` — equal ref ids are + // treated as the same request, which would bind the copy's tab to the source + if ( + isHoppRESTRequest(requestUpdated) && + (picked.value.pickedType === "my-collection" || + picked.value.pickedType === "my-folder" || + picked.value.pickedType === "teams-collection" || + picked.value.pickedType === "teams-folder") + ) { + requestUpdated._ref_id = generateUniqueRefId("req") + } + if (picked.value.pickedType === "my-collection") { if (!isHoppRESTRequest(requestUpdated)) throw new Error("requestUpdated is not a REST Request") @@ -412,6 +427,28 @@ const saveRequestAs = async () => { if (!isHoppRESTRequest(requestUpdated)) throw new Error("requestUpdated is not a REST Request") + // Overwriting replaces the target's content, not its identity — keep the + // target's own `_ref_id` and backend `id` so its tabs stay bound to it and + // the source doesn't share identity with the copy + const targetRequest = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + picked.value.folderPath.split("/").map((x) => parseInt(x)) + )?.requests[picked.value.requestIndex] + + // Delete rather than assign undefined — an explicit undefined key survives + // into the store and not every sync backend tolerates it + if (targetRequest && "_ref_id" in targetRequest && targetRequest._ref_id) { + requestUpdated._ref_id = targetRequest._ref_id + } else { + delete requestUpdated._ref_id + } + + if (targetRequest?.id) { + requestUpdated.id = targetRequest.id + } else { + delete requestUpdated.id + } + editRESTRequest( picked.value.folderPath, picked.value.requestIndex, @@ -426,6 +463,7 @@ const saveRequestAs = async () => { originLocation: "user-collection", folderPath: picked.value.folderPath, requestIndex: picked.value.requestIndex, + requestRefID: requestUpdated._ref_id ?? requestUpdated.id, }, } diff --git a/packages/hoppscotch-common/src/components/collections/documentation/index.vue b/packages/hoppscotch-common/src/components/collections/documentation/index.vue index 16dfdf72be9..49f23bd097f 100644 --- a/packages/hoppscotch-common/src/components/collections/documentation/index.vue +++ b/packages/hoppscotch-common/src/components/collections/documentation/index.vue @@ -278,6 +278,8 @@ import { } from "~/helpers/backend/helpers" import { GQLError } from "~/helpers/backend/GQLClient" import { getErrorMessage } from "~/helpers/backend/mutations/MockServer" +import { HoppRESTSaveContext } from "~/helpers/rest/document" +import { RESTTabService } from "~/services/tab/rest" import { DocumentationService, @@ -342,6 +344,35 @@ const props = withDefaults( ) const documentationService = useService(DocumentationService) +const restTabs = useService(RESTTabService) + +/** + * Mirrors a saved documentation description onto any open tabs of the request — + * otherwise a tab keeps its pre-edit copy and its next save writes the stale + * description back over the one just saved. + */ +const syncOpenRequestTabDescription = ( + saveContext: HoppRESTSaveContext, + description: string +) => { + const possibleTabs = restTabs.getTabsRefWithSaveContext(saveContext) + + for (const possibleTab of possibleTabs) { + // Hold the document, not the tab ref — the ref's getter throws once the + // tab is closed, and it's read again after a tick + const tabDocument = possibleTab.value.document + + if (tabDocument.type !== "request") continue + + const wasDirty = tabDocument.isDirty + tabDocument.request.description = description + + // The tab marks itself dirty on any request change; restore its prior state + nextTick(() => { + tabDocument.isDirty = wasDirty + }) + } +} const isLoadingTeamCollection = ref(false) const isSavingDocumentation = ref(false) @@ -774,9 +805,13 @@ const saveCollectionDocumentation = async () => { } const saveRequestDocumentation = async () => { + // The editor stays live while the team mutation is in flight — sync the tab + // with what was persisted, not whatever the editor holds when it resolves + const savedDescription = documentationDescription.value + const updatedRequest = { ...props.request!, - description: documentationDescription.value, + description: savedDescription, } if (props.isTeamCollection && props.requestID) { @@ -796,6 +831,13 @@ const saveRequestDocumentation = async () => { isSavingDocumentation.value = false }, () => { + syncOpenRequestTabDescription( + { + originLocation: "team-collection", + requestID: props.requestID!, + }, + savedDescription + ) toast.success(t("documentation.save_success")) isSavingDocumentation.value = false } @@ -804,6 +846,15 @@ const saveRequestDocumentation = async () => { } else { // Personal request editRESTRequest(props.folderPath!, props.requestIndex!, updatedRequest) + syncOpenRequestTabDescription( + { + originLocation: "user-collection", + folderPath: props.folderPath!, + requestIndex: props.requestIndex!, + requestRefID: updatedRequest._ref_id ?? updatedRequest.id, + }, + savedDescription + ) toast.success(t("documentation.save_success")) } } @@ -916,6 +967,13 @@ const saveRequestDocumentationById = async ( return false }, () => { + syncOpenRequestTabDescription( + { + originLocation: "team-collection", + requestID: item.requestID!, + }, + documentation + ) return true } ) @@ -939,6 +997,15 @@ const saveRequestDocumentationById = async ( try { editRESTRequest(folderPath, item.requestIndex, updatedRequest) + syncOpenRequestTabDescription( + { + originLocation: "user-collection", + folderPath, + requestIndex: item.requestIndex, + requestRefID: updatedRequest._ref_id ?? updatedRequest.id, + }, + documentation + ) return true } catch (e) { console.error(e) diff --git a/packages/hoppscotch-common/src/helpers/import-export/import/index.ts b/packages/hoppscotch-common/src/helpers/import-export/import/index.ts index 66c1ad1d1fd..2783d1503df 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/import/index.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/import/index.ts @@ -3,6 +3,7 @@ import type { Component } from "vue" import { StepsOutputList } from "../steps" import { HoppCollection, + generateUniqueRefId, makeCollection, translateToNewRESTCollection, } from "@hoppscotch/data" @@ -91,6 +92,13 @@ export const sanitizeCollection = ( return makeCollection({ ...rest, + // Requests carry identity too — an imported file may hold `_ref_id`/`id` + // values that already exist in the workspace, and matching treats equal + // identities as the same request + requests: rest.requests.map((request) => { + const { id: _requestId, ...requestRest } = request + return { ...requestRest, _ref_id: generateUniqueRefId("req") } + }), folders: rest.folders.map(sanitizeCollection), }) } diff --git a/packages/hoppscotch-common/src/newstore/collections.ts b/packages/hoppscotch-common/src/newstore/collections.ts index a6c2bec3bc0..38dbdc80b76 100644 --- a/packages/hoppscotch-common/src/newstore/collections.ts +++ b/packages/hoppscotch-common/src/newstore/collections.ts @@ -730,6 +730,16 @@ const restCollectionDispatchers = defineDispatchers({ _ref_id: generateUniqueRefId("coll"), } + // Copied requests need their own identity too — matching falls back to + // the backend `id`, so even a legacy request copied without a fresh + // `_ref_id` would fight the original for tabs + newCollection.requests = (newCollection.requests ?? []).map( + (request) => ({ + ...request, + _ref_id: generateUniqueRefId("req"), + }) + ) + newCollection.folders = (newCollection.folders ?? []).map((folder) => recursiveChangeRefIdToAvoidConflicts(folder) ) @@ -1446,6 +1456,13 @@ const gqlCollectionDispatchers = defineDispatchers({ ...coll, _ref_id: generateUniqueRefId("coll"), } + + // Copied requests need fresh `_ref_id`s too, else they alias the originals' tabs + next.requests = (next.requests ?? []).map((request) => ({ + ...request, + _ref_id: generateUniqueRefId("req"), + })) + next.folders = (next.folders ?? []).map( recursiveChangeRefIdToAvoidConflicts ) diff --git a/packages/hoppscotch-common/src/pages/import.vue b/packages/hoppscotch-common/src/pages/import.vue index 851fa1cbea9..3bab1536438 100644 --- a/packages/hoppscotch-common/src/pages/import.vue +++ b/packages/hoppscotch-common/src/pages/import.vue @@ -21,7 +21,10 @@ import { } from "~/helpers/clientLocalVariables" import { useI18n } from "@composables/i18n" import { useToast } from "@composables/toast" -import { IMPORTER_INVALID_FILE_FORMAT } from "~/helpers/import-export/import" +import { + IMPORTER_INVALID_FILE_FORMAT, + sanitizeCollection, +} from "~/helpers/import-export/import" import { OPENAPI_DEREF_ERROR } from "~/helpers/import-export/import/openapi" import { isOfType } from "~/helpers/functional/primtive" import { TELeftType } from "~/helpers/functional/taskEither" @@ -116,10 +119,11 @@ const handleImportFailure = (error: ImportCollectionsError) => { } const handleImportSuccess = (collections: HoppCollection[]) => { - // Mirror the modal-import path: stamp `_ref_id`s, persist any raw - // secret values to the local secret store, then append the stripped - // tree to newstore so localStorage / future syncs stay clean. - const withRefIds = collections.map(ensureRefIds) + // Mirror the modal-import path: sanitize file-carried identities, stamp + // `_ref_id`s, persist any raw secret values to the local secret store, then + // append the stripped tree to newstore so localStorage / future syncs stay + // clean. + const withRefIds = collections.map(sanitizeCollection).map(ensureRefIds) withRefIds.forEach(populateLocalStoresFromCollectionTree) appendRESTCollections(withRefIds.map(stripCollectionTreeForStore)) diff --git a/packages/hoppscotch-common/src/services/spotlight/searchers/collections.searcher.ts b/packages/hoppscotch-common/src/services/spotlight/searchers/collections.searcher.ts index d0884a4fa38..63a31ffde77 100644 --- a/packages/hoppscotch-common/src/services/spotlight/searchers/collections.searcher.ts +++ b/packages/hoppscotch-common/src/services/spotlight/searchers/collections.searcher.ts @@ -6,6 +6,7 @@ import { SpotlightSearcherSessionState, SpotlightService, } from "../" +import { cloneDeep } from "lodash-es" import { Ref, computed, effectScope, markRaw, ref, watch } from "vue" import { getI18n } from "~/modules/i18n" import MiniSearch from "minisearch" @@ -304,32 +305,35 @@ export class CollectionsSpotlightSearcherService }) } - const possibleTab = this.restTab.getTabRefWithSaveContext({ + const resolvedFolderPath = folderPath.join("/") + + const req = this.getRESTFolderFromFolderPath(resolvedFolderPath) + ?.requests[reqIndex] as HoppRESTRequest + + if (!req) return + + // Record `requestRefID` like the sidebar does — a tab opened without one + // isn't matched by lookups that supply it, and the request opens twice + const saveContext = { originLocation: "user-collection", - folderPath: folderPath.join("/"), + folderPath: resolvedFolderPath, requestIndex: reqIndex, - }) + requestRefID: req._ref_id ?? req.id, + } as const + + const possibleTab = this.restTab.getTabRefWithSaveContext(saveContext) if (possibleTab) { this.restTab.setActiveTab(possibleTab.value.id) } else { - const req = this.getRESTFolderFromFolderPath(folderPath.join("/")) - ?.requests[reqIndex] as HoppRESTRequest - - if (!req) return - this.restTab.createNewTab( { type: "request", - request: req, + request: cloneDeep(req), isDirty: false, - saveContext: { - originLocation: "user-collection", - folderPath: folderPath.join("/"), - requestIndex: reqIndex, - }, + saveContext, inheritedProperties: cascadeParentCollectionForProperties( - folderPath.join("/"), + resolvedFolderPath, "rest" ), }, @@ -352,7 +356,7 @@ export class CollectionsSpotlightSearcherService requestIndex: reqIndex, }, cursorPosition: 0, - request: req, + request: cloneDeep(req), isDirty: false, inheritedProperties: cascadeParentCollectionForProperties( folderPath.join("/"), diff --git a/packages/hoppscotch-common/src/services/tab/__tests__/rest-tab.service.spec.ts b/packages/hoppscotch-common/src/services/tab/__tests__/rest-tab.service.spec.ts new file mode 100644 index 00000000000..f0b080d9d2e --- /dev/null +++ b/packages/hoppscotch-common/src/services/tab/__tests__/rest-tab.service.spec.ts @@ -0,0 +1,339 @@ +import { describe, expect, it, vi } from "vitest" +import { TestContainer } from "dioc/testing" + +import { getDefaultRESTRequest } from "~/helpers/rest/default" +import { HoppRESTSaveContext } from "~/helpers/rest/document" +import { RESTTabService } from "../rest" + +// The persistence import drags in stores with top-level side effects that +// can't run in this bare container; matching never touches it (hoisted mock) +vi.mock("../../persistence", () => ({ + PersistenceService: class {}, + STORE_KEYS: {}, +})) + +const makeService = () => { + const container = new TestContainer() + return container.bind(RESTTabService) +} + +const openRequestTab = ( + service: RESTTabService, + saveContext: HoppRESTSaveContext, + requestFields?: { _ref_id?: string; id?: string } +) => + service.createNewTab({ + type: "request", + request: { ...getDefaultRESTRequest(), ...requestFields }, + isDirty: false, + saveContext, + }) + +describe("RESTTabService", () => { + describe("getTabRefWithSaveContext", () => { + it("matches positionally when neither side has a requestRefID", () => { + const service = makeService() + + const tab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("does not match a different position when neither side has a requestRefID", () => { + const service = makeService() + + openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + expect( + service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + }) + ).toBeNull() + }) + + it("matches on requestRefID even when the tab's index has drifted", () => { + const service = makeService() + + const tab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + requestRefID: "req_a", + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("does not match a tab holding a different request at the same position", () => { + const service = makeService() + + openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + requestRefID: "req_other", + }) + + expect( + service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + requestRefID: "req_a", + }) + ).toBeNull() + }) + + it("does not match an identity-less tab when the lookup names its request", () => { + const service = makeService() + + openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + expect( + service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + ).toBeNull() + }) + + it("identifies a tab without a context ref by the request it holds", () => { + const service = makeService() + + // Context predates ref ids, but the held request names itself — found + // even though its index has drifted + const tab = openRequestTab( + service, + { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 5, + }, + { _ref_id: "req_a" } + ) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("identifies a legacy tab by the backend id its request holds", () => { + const service = makeService() + + const tab = openRequestTab( + service, + { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 3, + }, + { id: "backend_1" } + ) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "backend_1", + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("rejects a stale tab holding a different request at reused coordinates", () => { + const service = makeService() + + openRequestTab( + service, + { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }, + { _ref_id: "req_other" } + ) + + expect( + service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + ).toBeNull() + }) + + it("falls back to position when the lookup has no requestRefID but the tab does", () => { + const service = makeService() + + const tab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("treats an empty-string requestRefID as missing on both sides", () => { + const service = makeService() + + // Some creation paths store "" when a request has neither `_ref_id` nor `id` + const tab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "", + }) + + const found = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "", + }) + + expect(found?.value.id).toEqual(tab.id) + }) + + it("scopes example lookups by exampleID", () => { + const service = makeService() + + const requestTab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + const exampleTab = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + exampleID: "0", + }) + + const foundRequest = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + }) + + const foundExample = service.getTabRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + exampleID: "0", + }) + + expect(foundRequest?.value.id).toEqual(requestTab.id) + expect(foundExample?.value.id).toEqual(exampleTab.id) + }) + + it("matches team tabs by requestID and exampleID", () => { + const service = makeService() + + const tab = openRequestTab(service, { + originLocation: "team-collection", + requestID: "team_req_1", + }) + + const found = service.getTabRefWithSaveContext({ + originLocation: "team-collection", + requestID: "team_req_1", + }) + + expect(found?.value.id).toEqual(tab.id) + + expect( + service.getTabRefWithSaveContext({ + originLocation: "team-collection", + requestID: "team_req_1", + exampleID: "0", + }) + ).toBeNull() + }) + }) + + describe("getTabsRefWithSaveContext", () => { + it("returns every tab matching the save context", () => { + const service = makeService() + + const first = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + + // A duplicate of the same request whose index has drifted + const second = openRequestTab(service, { + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + requestRefID: "req_a", + }) + + const found = service.getTabsRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 1, + requestRefID: "req_a", + }) + + expect(found.map((tab) => tab.value.id)).toEqual([first.id, second.id]) + }) + + it("returns an empty array when nothing matches", () => { + const service = makeService() + + expect( + service.getTabsRefWithSaveContext({ + originLocation: "user-collection", + folderPath: "0", + requestIndex: 0, + }) + ).toEqual([]) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/tab/rest.ts b/packages/hoppscotch-common/src/services/tab/rest.ts index 35e50d2041e..884491e7195 100644 --- a/packages/hoppscotch-common/src/services/tab/rest.ts +++ b/packages/hoppscotch-common/src/services/tab/rest.ts @@ -5,7 +5,7 @@ import { HoppRESTSaveContext, HoppTabDocument } from "~/helpers/rest/document" import { getService } from "~/modules/dioc" import { PersistenceService, STORE_KEYS } from "../persistence" import { TabService } from "./tab" -import { PersistableTabState } from "." +import { HoppTab, PersistableTabState } from "." export class RESTTabService extends TabService { public static readonly ID = "REST_TAB_SERVICE" @@ -70,30 +70,63 @@ export class RESTTabService extends TabService { return savedState } + private matchesSaveContext( + tab: HoppTab, + ctx: HoppRESTSaveContext + ) { + if (tab.document.type === "test-runner") return false + + // For `team-collection` request id can be considered unique + if (ctx?.originLocation === "team-collection") { + return ( + tab.document.saveContext?.originLocation === "team-collection" && + tab.document.saveContext.requestID === ctx.requestID && + tab.document.saveContext.exampleID === ctx.exampleID + ) + } + + const tabCtx = tab.document.saveContext + + if (tabCtx?.originLocation !== "user-collection") return false + if (tabCtx.exampleID !== ctx?.exampleID) return false + + // A tab whose context predates ref ids is still identified by the request + // it holds. Truthiness, not a null check: some creation paths store `""`, + // which is not an identity. + const tabRefID = + tabCtx.requestRefID || + (tab.document.type === "request" + ? tab.document.request._ref_id || tab.document.request.id + : undefined) + + // A lookup that names its request is authoritative — a tab with a + // different or missing identity is not that request, even at matching + // coordinates, which may have been reused since the tab was bound. + if (ctx?.requestRefID) { + return tabRefID === ctx.requestRefID + } + + // Position is all that's left when the lookup carries no identity + return ( + tabCtx.folderPath === ctx?.folderPath && + tabCtx.requestIndex === ctx?.requestIndex + ) + } + + /** + * Returns every tab matching the save context — a request can be open in + * more than one tab, and a write-back that skips one leaves it holding + * stale content that its next save persists. + */ + public getTabsRefWithSaveContext(ctx: HoppRESTSaveContext) { + return Array.from(this.tabMap.values()) + .filter((tab) => this.matchesSaveContext(tab, ctx)) + .map((tab) => this.getTabRef(tab.id)) + } + public getTabRefWithSaveContext(ctx: HoppRESTSaveContext) { for (const tab of this.tabMap.values()) { - // For `team-collection` request id can be considered unique - if (tab.document.type === "test-runner") continue - - if (ctx?.originLocation === "team-collection") { - if ( - tab.document.saveContext?.originLocation === "team-collection" && - tab.document.saveContext.requestID === ctx.requestID && - tab.document.saveContext.exampleID === ctx.exampleID - ) { - return this.getTabRef(tab.id) - } - } else if ( - tab.document.saveContext?.originLocation === "user-collection" && - tab.document.saveContext.folderPath === ctx?.folderPath && - tab.document.saveContext.requestIndex === ctx?.requestIndex && - tab.document.saveContext.exampleID === ctx?.exampleID && - (ctx?.requestRefID != null - ? tab.document.saveContext.requestRefID === ctx.requestRefID - : true) - ) { - return this.getTabRef(tab.id) - } + if (this.matchesSaveContext(tab, ctx)) return this.getTabRef(tab.id) } return null From 755d622bf3b9b9c650a4a51e355719385d9b3cd1 Mon Sep 17 00:00:00 2001 From: Prajjwol Date: Thu, 27 Aug 2026 09:05:53 -0400 Subject: [PATCH 10/14] feat(common): data-driven collection runs with datasets (#6513) Co-authored-by: nivedin --- packages/hoppscotch-common/locales/en.json | 34 + packages/hoppscotch-common/package.json | 2 + .../hoppscotch-common/src/components.d.ts | 1 + .../src/components/http/test/Response.vue | 3 + .../src/components/http/test/ResultFolder.vue | 9 - .../components/http/test/ResultRequest.vue | 94 ++- .../src/components/http/test/Runner.vue | 419 +++++++++-- .../src/components/http/test/RunnerModal.vue | 679 +++++++++++++++--- .../http/test/RunnerRequestSelector.vue | 187 +++++ .../src/components/http/test/RunnerResult.vue | 313 +++++--- .../src/components/http/test/TestResult.vue | 4 + .../lenses/ResponseBodyRenderer.vue | 6 +- .../src/helpers/RequestRunner.ts | 59 +- .../src/helpers/fixBrokenRequestVersion.ts | 25 +- .../export/__tests__/runnerResults.spec.ts | 163 +++++ .../import-export/export/runnerResults.ts | 402 +++++++++++ .../src/helpers/rest/document.ts | 51 +- .../helpers/runner/__tests__/dataset.spec.ts | 215 ++++++ .../runner/__tests__/iteration-vars.spec.ts | 137 ++++ .../runner/__tests__/selection.spec.ts | 70 ++ .../runner/__tests__/temp_envs.spec.ts | 51 ++ .../src/helpers/runner/dataset.ts | 139 ++++ .../src/helpers/runner/iteration-vars.ts | 60 ++ .../src/helpers/runner/selection.ts | 47 ++ .../src/helpers/runner/temp_envs.ts | 15 + .../inheritedCollectionVarTransformer.spec.ts | 286 ++++++++ .../inheritedCollectionVarTransformer.ts | 46 +- .../collections-inherited-props.spec.ts | 173 +++++ .../src/newstore/collections.ts | 25 +- .../src/services/persistence/index.ts | 15 +- .../testRunnerResultCollection.spec.ts | 116 +++ .../persistence/validation-schemas/index.ts | 86 ++- .../src/services/tab/rest.ts | 27 + .../__tests__/plan-collection.spec.ts | 239 ++++++ .../test-runner/test-runner.service.ts | 589 ++++++++++----- pnpm-lock.yaml | 6 + 36 files changed, 4333 insertions(+), 460 deletions(-) create mode 100644 packages/hoppscotch-common/src/components/http/test/RunnerRequestSelector.vue create mode 100644 packages/hoppscotch-common/src/helpers/import-export/export/__tests__/runnerResults.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/import-export/export/runnerResults.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/__tests__/dataset.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/__tests__/iteration-vars.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/__tests__/selection.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/__tests__/temp_envs.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/dataset.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/iteration-vars.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/selection.ts create mode 100644 packages/hoppscotch-common/src/helpers/utils/__tests__/inheritedCollectionVarTransformer.spec.ts create mode 100644 packages/hoppscotch-common/src/newstore/__tests__/collections-inherited-props.spec.ts create mode 100644 packages/hoppscotch-common/src/services/persistence/validation-schemas/__tests__/testRunnerResultCollection.spec.ts create mode 100644 packages/hoppscotch-common/src/services/test-runner/__tests__/plan-collection.spec.ts diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index b928b959dd7..4dd9e298878 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -1998,6 +1998,39 @@ "cli_comming_soon_for_personal_collection": "Collection Runner for personal collections in CLI is coming soon.", "delay": "Delay", "negative_delay": "Delay cannot be negative", + "iterations": "Iterations", + "time": "time", + "times": "times", + "invalid_iterations": "Iterations must be at least 1", + "data_feed": "Data Feed", + "data_feed_help": "Import a CSV or JSON file to run one iteration per row.", + "select_data_file": "Select CSV / JSON File", + "data_preview": "Data Preview", + "dataset_row": "{count} row", + "dataset_rows": "{count} rows", + "dataset_preview_truncated": "Showing the first {count} rows of the dataset.", + "dataset_iterations_info": "Dataset contains {rows} rows, running {total} iterations as per your configuration.", + "iterations_locked_to_dataset": "Iterations match the dataset row count. Remove the data file to set this manually.", + "jump_to_iteration": "Jump to iteration", + "previous_iteration": "Previous iteration", + "next_iteration": "Next iteration", + "invalid_data_file": "Invalid data file", + "empty_data_file": "Data file is empty", + "data_file_size_limit_exceeded": "The data file exceeds the {sizeLimit} MB limit", + "collection_load_failed": "Could not load this collection.", + "filter_requests": "Filter requests", + "no_requests_match_filter": "No requests match the filter", + "selection_reset": "The collection changed, so all requests will run.", + "selection_partially_reset": "Some selected requests no longer exist and were removed from the selection.", + "remove_file": "Remove file", + "run_sequence": "Run Sequence", + "reset_run_order": "Reset", + "export_results": "Export results", + "select_all": "Select all", + "deselect_all": "Deselect all", + "selected_requests_count": "{selected} of {total} selected", + "no_requests_selected": "Select at least one request to run", + "iteration": "Iteration {count}", "ui": "Runner", "running_collection": "Running collection", "run_config": "Run Configuration", @@ -2014,6 +2047,7 @@ "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", "run_collection": "Run collection", + "results_not_restored": "Run results aren't kept after a reload. Run the collection again to see them.", "no_passed_tests": "No tests passed", "no_failed_tests": "No tests failed" }, diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index ea9006adb7e..664c5b05b90 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -87,6 +87,7 @@ "monaco-editor": "0.55.1", "nprogress": "0.2.0", "paho-mqtt": "1.1.0", + "papaparse": "5.5.4", "path": "0.12.7", "postman-collection": "5.3.1", "process": "0.11.10", @@ -147,6 +148,7 @@ "@types/lodash-es": "4.17.12", "@types/nprogress": "0.2.3", "@types/paho-mqtt": "1.0.10", + "@types/papaparse": "5.5.2", "@types/postman-collection": "3.5.11", "@types/qs": "6.15.1", "@types/splitpanes": "2.2.6", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 46b4f3976a9..05bb83ac4d3 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -233,6 +233,7 @@ declare module 'vue' { HttpTestRunner: typeof import('./components/http/test/Runner.vue')['default'] HttpTestRunnerMeta: typeof import('./components/http/test/RunnerMeta.vue')['default'] HttpTestRunnerModal: typeof import('./components/http/test/RunnerModal.vue')['default'] + HttpTestRunnerRequestSelector: typeof import('./components/http/test/RunnerRequestSelector.vue')['default'] HttpTestRunnerResult: typeof import('./components/http/test/RunnerResult.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default'] HttpTestTestResult: typeof import('./components/http/test/TestResult.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/http/test/Response.vue b/packages/hoppscotch-common/src/components/http/test/Response.vue index b8cb8f0b839..dc7a7ece0ff 100644 --- a/packages/hoppscotch-common/src/components/http/test/Response.vue +++ b/packages/hoppscotch-common/src/components/http/test/Response.vue @@ -29,6 +29,7 @@ :is-editable="false" :is-test-runner="true" :show-response="showResponse" + :tab-id="tabId" /> () const emit = defineEmits<{ diff --git a/packages/hoppscotch-common/src/components/http/test/ResultFolder.vue b/packages/hoppscotch-common/src/components/http/test/ResultFolder.vue index 180f62ba7b7..5c448d56e0b 100644 --- a/packages/hoppscotch-common/src/components/http/test/ResultFolder.vue +++ b/packages/hoppscotch-common/src/components/http/test/ResultFolder.vue @@ -12,11 +12,6 @@ - (), { id: "", parentID: null, folderType: "collection", isOpen: false, - isSelected: false, exportLoading: false, hasNoTeamAccess: false, isLastItem: false, - showSelection: false, } ) diff --git a/packages/hoppscotch-common/src/components/http/test/ResultRequest.vue b/packages/hoppscotch-common/src/components/http/test/ResultRequest.vue index 24803789b95..9ddb69762b1 100644 --- a/packages/hoppscotch-common/src/components/http/test/ResultRequest.vue +++ b/packages/hoppscotch-common/src/components/http/test/ResultRequest.vue @@ -6,6 +6,18 @@ @click="selectRequest()" >
+ + + + {{ request.name }} - - {{ `${request.response?.statusCode}` }} - - - - + + + +
+ + {{ request.response.statusCode }} + + + {{ `${responseDuration} ms` }} + + + {{ responseSize }} + + + + +

@@ -38,7 +67,11 @@

{{ request.error }}
@@ -54,7 +87,10 @@ import { computed } from "vue" import findStatusGroup from "~/helpers/findStatusGroup" import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring" +import { getStatusCodePhrase } from "~/helpers/utils/statusCodes" import { TestRunnerRequest } from "~/services/test-runner/test-runner.service" +import IconChevronRight from "~icons/lucide/chevron-right" +import IconFolder from "~icons/lucide/folder" const props = withDefaults( defineProps<{ @@ -63,14 +99,12 @@ const props = withDefaults( parentID: string | null isActive?: boolean isSelected?: boolean - showSelection?: boolean showTestType: "all" | "passed" | "failed" }>(), { parentID: null, isActive: false, isSelected: false, - showSelection: false, requestID: "", } ) @@ -89,11 +123,39 @@ const statusCategory = computed(() => { ) return { name: "error", - className: "text-red-500", + className: "critical-error-response", } return findStatusGroup(props.request?.response.statusCode) }) +// Only success/fail responses carry meta (duration + size). +const responseMeta = computed(() => { + const response = props.request?.response + if (response?.type === "success" || response?.type === "fail") + return response.meta + return null +}) + +const responseDuration = computed( + () => responseMeta.value?.responseDuration ?? null +) + +// The badge stays a bare code; the reason phrase rides along on hover. +const statusTooltip = computed(() => { + const response = props.request?.response + if (response?.type !== "success" && response?.type !== "fail") return "" + + return getStatusCodePhrase(response.statusCode, response.statusText) +}) + +const responseSize = computed(() => { + const size = responseMeta.value?.responseSize + if (size === undefined) return null + if (size >= 100000) return `${(size / 1000000).toFixed(2)} MB` + if (size >= 1000) return `${(size / 1000).toFixed(2)} KB` + return `${size} B` +}) + const emit = defineEmits<{ (event: "select-request"): void }>() diff --git a/packages/hoppscotch-common/src/components/http/test/Runner.vue b/packages/hoppscotch-common/src/components/http/test/Runner.vue index 9290cae1373..65ff47407d0 100644 --- a/packages/hoppscotch-common/src/components/http/test/Runner.vue +++ b/packages/hoppscotch-common/src/components/http/test/Runner.vue @@ -2,63 +2,153 @@ diff --git a/packages/hoppscotch-common/src/components/http/test/RunnerResult.vue b/packages/hoppscotch-common/src/components/http/test/RunnerResult.vue index 00e2448f278..f1c4f2651a1 100644 --- a/packages/hoppscotch-common/src/components/http/test/RunnerResult.vue +++ b/packages/hoppscotch-common/src/components/http/test/RunnerResult.vue @@ -1,94 +1,118 @@ diff --git a/packages/hoppscotch-common/src/components/http/test/TestResult.vue b/packages/hoppscotch-common/src/components/http/test/TestResult.vue index 34b54973fd6..542c2f6e9f1 100644 --- a/packages/hoppscotch-common/src/components/http/test/TestResult.vue +++ b/packages/hoppscotch-common/src/components/http/test/TestResult.vue @@ -191,6 +191,10 @@ import { useI18n } from "@composables/i18n" import { useReadonlyStream, useStream } from "@composables/stream" import { isEqual } from "lodash-es" import { computed, ref } from "vue" +// Explicit import: auto-resolution inside `http/test/` maps the name to the +// missing `http/test/ResultEntry.vue` (directoryAsNamespace) and fails at +// runtime, so nested test entries never rendered. +import HttpTestResultEntry from "~/components/http/TestResultEntry.vue" import { HoppTestData, HoppTestResult } from "~/helpers/types/HoppTestResult" import { globalEnv$, diff --git a/packages/hoppscotch-common/src/components/lenses/ResponseBodyRenderer.vue b/packages/hoppscotch-common/src/components/lenses/ResponseBodyRenderer.vue index 1598a5072ee..1918396a1b4 100644 --- a/packages/hoppscotch-common/src/components/lenses/ResponseBodyRenderer.vue +++ b/packages/hoppscotch-common/src/components/lenses/ResponseBodyRenderer.vue @@ -96,9 +96,9 @@ const EXPERIMENTAL_SCRIPTING_SANDBOX = useSetting( "EXPERIMENTAL_SCRIPTING_SANDBOX" ) -const isSavable = computed(() => { - return doc.value.response?.type === "success" && doc.value.saveContext -}) +const isSavable = computed(() => + Boolean(doc.value.response?.type === "success" && doc.value.saveContext) +) const showIndicator = computed(() => { if (!doc.value.testResults) return false diff --git a/packages/hoppscotch-common/src/helpers/RequestRunner.ts b/packages/hoppscotch-common/src/helpers/RequestRunner.ts index 887431493a2..94221a6f44e 100644 --- a/packages/hoppscotch-common/src/helpers/RequestRunner.ts +++ b/packages/hoppscotch-common/src/helpers/RequestRunner.ts @@ -57,8 +57,10 @@ import { HoppTab } from "~/services/tab" import { updateTeamEnvironment } from "./backend/mutations/TeamEnvironment" import { createRESTNetworkRequestStream } from "./network" import { HoppRequestDocument } from "./rest/document" +import { stripIterationVarsFromEnvs } from "./runner/iteration-vars" import { getTemporaryVariables, + scriptEnvsToTemporaryVariables, setTemporaryVariables, } from "./runner/temp_envs" import { HoppRESTResponse } from "./types/HoppRESTResponse" @@ -870,7 +872,8 @@ export async function runTestRunnerRequest( inheritedVariables: HoppCollectionVariable[] = [], initialEnvironmentState: InitialEnvironmentState, inheritedPreRequestScripts: string[] = [], - inheritedTestScripts: string[] = [] + inheritedTestScripts: string[] = [], + iterationVars: Environment["variables"] = [] ): Promise< | E.Left<"script_fail"> | E.Right<{ @@ -892,13 +895,23 @@ export async function runTestRunnerRequest( initialEnvsForComparison, } = initialEnvironmentState + const iterationVarKeys = new Set(iterationVars.map(({ key }) => key)) + // Injected into `selected` only — the sandbox env shape has no temp scope; + // template resolution gets the iteration values via the effective request. + const initialEnvsWithIterationData = { + ...initialEnvs, + selected: [...iterationVars, ...initialEnvs.selected], + } + const stripIterationVars = (envs: TestResult["envs"]): TestResult["envs"] => + stripIterationVarsFromEnvs(envs, iterationVarKeys, initialEnvs.selected) + // Wait for browser to paint the loading state (Send -> Cancel button) // Adds ~32ms latency but ensures immediate visual feedback await waitForBrowserPaint() return delegatePreRequestScriptRunner( request, - initialEnvs, + initialEnvsWithIterationData, cookieJarEntries, inheritedPreRequestScripts ).then(async (preRequestScriptResult) => { @@ -928,16 +941,24 @@ export async function runTestRunnerRequest( id: "env-id", v: 2, name: "Env", - variables: filterNonEmptyEnvironmentVariables( - combineEnvVariables({ + variables: filterNonEmptyEnvironmentVariables([ + // Data-file iteration values take precedence over every other scope + // (request, collection, environment) for that iteration, matching + // Postman's data-variable semantics: the Data scope outranks the + // Environment scope that a pre-request script writes to, so a + // `pm.environment.set` on a data-column key does not shadow it. + // Prepend the iteration values once, then drop those keys from the + // combined scopes so each appears exactly once and stays authoritative. + ...iterationVars, + ...combineEnvVariables({ environments: { ...preRequestScriptResult.right.updatedEnvs, temp: !persistEnv ? getTemporaryVariables() : [], }, requestVariables: finalRequestVariables, collectionVariables: inheritedVariables, - }) - ), + }).filter(({ key }) => !iterationVarKeys.has(key)), + ]), }) const [stream] = createRESTNetworkRequestStream(effectiveRequest) @@ -964,9 +985,17 @@ export async function runTestRunnerRequest( ) if (E.isRight(postRequestScriptResult)) { + // Iteration values are injected into the environment for the + // duration of the request only; strip them back out so a data run + // never persists data-file columns as environment variables. + const filteredPostRequestScriptResult = { + ...postRequestScriptResult.right, + envs: stripIterationVars(postRequestScriptResult.right.envs), + } + // Combine console entries from pre and post request scripts const combinedResult = { - ...postRequestScriptResult.right, + ...filteredPostRequestScriptResult, consoleEntries: [ ...(preRequestScriptResult.right.consoleEntries ?? []), ...(postRequestScriptResult.right.consoleEntries ?? []), @@ -984,11 +1013,11 @@ export async function runTestRunnerRequest( if ( hasEnvironmentChanges( initialEnvsForComparison, // Initial script environment when requests started - postRequestScriptResult.right.envs // Final script environment after test script execution + filteredPostRequestScriptResult.envs // Final script environment after test script execution ) ) { updateEnvsAfterTestScript( - postRequestScriptResult, + E.right(filteredPostRequestScriptResult), initialEnvironmentIndex, initialEnvName, initialEnvsForComparison, @@ -996,13 +1025,11 @@ export async function runTestRunnerRequest( ) } } else { - // Combine global and selected environment changes - const allChanges = [ - ...postRequestScriptResult.right.envs.global, - ...postRequestScriptResult.right.envs.selected, - ] - - setTemporaryVariables(allChanges) + setTemporaryVariables( + scriptEnvsToTemporaryVariables( + filteredPostRequestScriptResult.envs + ) + ) } return E.right({ diff --git a/packages/hoppscotch-common/src/helpers/fixBrokenRequestVersion.ts b/packages/hoppscotch-common/src/helpers/fixBrokenRequestVersion.ts index c9dee8f238d..0145a7475d1 100644 --- a/packages/hoppscotch-common/src/helpers/fixBrokenRequestVersion.ts +++ b/packages/hoppscotch-common/src/helpers/fixBrokenRequestVersion.ts @@ -35,10 +35,20 @@ export const fixBrokenRequestVersion = ( } if (x.doc.type === "test-runner") { - x.doc.request = safelyExtractRESTRequest( - x.doc.request, - getDefaultRESTRequest() - ) + // Runner docs persist `request: null` deliberately. Resurrecting the + // null into a default request lets a runner doc with an invalid + // collection satisfy the tab-state union's request-tab branch and + // silently morph into a blank request tab — only sanitize a request + // that actually exists. A missing key is normalized to null: the tab + // schema accepts null but not undefined. + if (x.doc.request === null || x.doc.request === undefined) { + x.doc.request = null + } else { + x.doc.request = safelyExtractRESTRequest( + x.doc.request, + getDefaultRESTRequest() + ) + } if (x.doc.resultCollection) { x.doc.resultCollection.requests = x.doc.resultCollection?.requests.map( @@ -47,6 +57,13 @@ export const fixBrokenRequestVersion = ( } ) } + + // Run results are no longer persisted, but an earlier build's state can + // still carry per-iteration result trees whose stale requests would + // fail schema validation and take the whole tab state down — drop them. + if ("iterationResults" in x.doc) { + x.doc.iterationResults = undefined + } } return x diff --git a/packages/hoppscotch-common/src/helpers/import-export/export/__tests__/runnerResults.spec.ts b/packages/hoppscotch-common/src/helpers/import-export/export/__tests__/runnerResults.spec.ts new file mode 100644 index 00000000000..f04457da6aa --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/import-export/export/__tests__/runnerResults.spec.ts @@ -0,0 +1,163 @@ +import { describe, expect, test } from "vitest" + +import { HoppTestRunnerDocument } from "~/helpers/rest/document" +import { buildRunnerResultReport } from "../runnerResults" + +// Minimal document fixture: a single iteration with one request whose response +// carries the given kernel response `type`. Cast through `unknown` so the +// fixture only has to specify the fields the report builder actually reads. +const makeDocument = ( + responseType: "success" | "fail" +): HoppTestRunnerDocument => { + const response = { + type: responseType, + statusCode: responseType === "fail" ? 500 : 200, + statusText: responseType === "fail" ? "Internal Server Error" : "OK", + headers: [{ key: "content-type", value: "application/json" }], + body: new TextEncoder().encode(`{"error":${responseType === "fail"}}`) + .buffer, + meta: { responseSize: 17, responseDuration: 42 }, + req: {}, + } + + const request = { + name: "req", + method: "GET", + endpoint: "https://example.com", + passedTests: 0, + failedTests: 0, + error: undefined, + testResults: null, + response, + } + + const meta = { + totalRequests: 1, + completedRequests: 1, + totalTests: 0, + passedTests: 0, + failedTests: 0, + totalTime: 42, + } + + return { + collectionID: "coll-id", + collection: { name: "My Collection" }, + collectionType: "my-collections", + environmentName: "Env", + status: "idle", + selectedIteration: 0, + config: { + iterations: 1, + delay: 0, + stopOnError: false, + persistResponses: true, + keepVariableValues: false, + }, + testRunnerMeta: meta, + iterationResults: [ + { + iteration: 0, + meta, + resultCollection: { + name: "My Collection", + folders: [], + requests: [request], + }, + }, + ], + } as unknown as HoppTestRunnerDocument +} + +describe("buildRunnerResultReport", () => { + test("preserves status/headers/body/meta for failed (4xx/5xx) responses", () => { + const report = buildRunnerResultReport( + makeDocument("fail"), + "all", + "base64" + ) + const exported = report.iterationResults[0].requests[0].response + + // Regression guard: error responses carry kernel type "fail" (not "failure"), + // so they must be serialized with full detail rather than falling through to + // the generic { type, error } branch that drops everything else. + expect(exported?.type).toBe("fail") + expect(exported?.statusCode).toBe(500) + expect(exported?.statusText).toBe("Internal Server Error") + expect(exported?.durationInMs).toBe(42) + expect(exported?.sizeInBytes).toBe(17) + expect(exported?.headers).toEqual([ + { key: "content-type", value: "application/json" }, + ]) + expect(exported?.body).toBeDefined() + }) + + test("serializes successful responses with full detail", () => { + const report = buildRunnerResultReport( + makeDocument("success"), + "all", + "base64" + ) + const exported = report.iterationResults[0].requests[0].response + + expect(exported?.type).toBe("success") + expect(exported?.statusCode).toBe(200) + expect(exported?.durationInMs).toBe(42) + }) + + test("derives a failed outcome from an errored assertion (no plain failures)", () => { + const request = { + name: "req", + method: "GET", + endpoint: "https://example.com", + passedTests: 1, + failedTests: 0, + error: undefined, + testResults: { + description: "", + scriptError: false, + expectResults: [{ status: "error", message: "assertion threw" }], + tests: [], + }, + response: null, + } + + const document = { + collectionID: "coll-id", + collection: { name: "My Collection" }, + collectionType: "my-collections", + status: "idle", + selectedIteration: 0, + config: { iterations: 1 }, + testRunnerMeta: { + totalRequests: 1, + completedRequests: 1, + totalTests: 1, + passedTests: 1, + failedTests: 0, + totalTime: 0, + }, + iterationResults: [ + { + iteration: 0, + meta: { + totalRequests: 1, + completedRequests: 1, + totalTests: 1, + passedTests: 1, + failedTests: 0, + totalTime: 0, + }, + resultCollection: { + name: "My Collection", + folders: [], + requests: [request], + }, + }, + ], + } as unknown as HoppTestRunnerDocument + + const report = buildRunnerResultReport(document, "all", "none") + expect(report.outcome).toBe("failed") + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/import-export/export/runnerResults.ts b/packages/hoppscotch-common/src/helpers/import-export/export/runnerResults.ts new file mode 100644 index 00000000000..b952c8be6f4 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/import-export/export/runnerResults.ts @@ -0,0 +1,402 @@ +import { HoppCollection } from "@hoppscotch/data" +import * as E from "fp-ts/Either" +import { + HoppTestRunnerDocument, + TestRunnerIterationResult, + TestRunnerMeta, +} from "~/helpers/rest/document" +import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse" +import { HoppTestData, HoppTestResult } from "~/helpers/types/HoppTestResult" +import { TestRunnerRequest } from "~/services/test-runner/test-runner.service" +import { platform } from "~/platform" + +/** + * Controls how response bodies are serialized into the report: + * - "base64": binary-safe, capped at BODY_CAP_BYTES (used for the DB report) + * - "readable": UTF-8 string for text bodies, base64 fallback for binary (used for the export file) + * - "none": bodies omitted entirely (used for the local metadata-only cache) + */ +export type RunnerBodyMode = "base64" | "readable" | "none" + +const BODY_CAP_BYTES = 300_000 + +export type ExportedAssertion = { + description: string + status: "pass" | "fail" | "error" + message: string +} + +export type ExportedResponse = { + type: HoppRESTResponse["type"] + statusCode?: number + statusText?: string + durationInMs?: number + sizeInBytes?: number + headers?: { key: string; value: string }[] + body?: string + bodyEncoding?: "utf-8" | "base64" + bodyOmittedReason?: "too-large" | "not-persisted" + error?: string +} + +export type ExportedRequest = { + name: string + method: string + endpoint: string + passedTests: number + failedTests: number + error?: string + assertions: ExportedAssertion[] + response?: ExportedResponse +} + +export type ExportedSummary = { + totalRequests: number + completedRequests: number + totalTests: number + passedTests: number + failedTests: number + totalTimeInMs: number +} + +export type ExportedIteration = { + iteration: number + summary: ExportedSummary + requests: ExportedRequest[] +} + +export type CollectionRunType = "PERSONAL" | "SHARED" + +export type CollectionRunOutcome = "passed" | "failed" | "errored" + +const COLLECTION_RUN_TYPE_LABELS: Record = { + PERSONAL: "Personal Collection", + SHARED: "Shared Collection", +} + +const collectionRunTypeLabel = (type: CollectionRunType): string => + COLLECTION_RUN_TYPE_LABELS[type] ?? type + +export type RunnerResultReport = { + collectionID: string + collectionName: string + collectionType: CollectionRunType + environment: string + outcome: CollectionRunOutcome + exportedAt: string + config: { + iterations: number + delayInMs: number + stopOnError: boolean + persistResponses: boolean + keepVariableValues: boolean + dataFile?: string + } + summary: ExportedSummary + iterationResults: ExportedIteration[] +} + +const toSummary = (meta: TestRunnerMeta): ExportedSummary => ({ + totalRequests: meta.totalRequests, + completedRequests: meta.completedRequests, + totalTests: meta.totalTests, + passedTests: meta.passedTests, + failedTests: meta.failedTests, + totalTimeInMs: meta.totalTime, +}) + +const arrayBufferToBase64 = (buffer: ArrayBuffer): string => { + const bytes = new Uint8Array(buffer) + const chunkSize = 0x8000 + let binary = "" + for (let i = 0; i < bytes.length; i += chunkSize) { + binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize)) + } + return btoa(binary) +} + +const getContentType = (headers: { key: string; value: string }[]): string => + headers.find((header) => header.key.toLowerCase() === "content-type") + ?.value ?? "" + +const isTextContentType = (contentType: string): boolean => + /\b(json|xml|html|javascript|csv)\b/i.test(contentType) || + /^text\//i.test(contentType) + +const serializeBody = ( + body: ArrayBuffer, + headers: { key: string; value: string }[], + mode: RunnerBodyMode +): Pick => { + if (mode === "none") return {} + + if (mode === "base64") { + if (body.byteLength > BODY_CAP_BYTES) + return { bodyOmittedReason: "too-large" } + return { body: arrayBufferToBase64(body), bodyEncoding: "base64" } + } + + // mode === "readable" + if (isTextContentType(getContentType(headers))) { + return { body: new TextDecoder().decode(body), bodyEncoding: "utf-8" } + } + return { body: arrayBufferToBase64(body), bodyEncoding: "base64" } +} + +const flattenAssertions = (tests: HoppTestData[]): ExportedAssertion[] => + tests.flatMap((test) => [ + ...test.expectResults.map((result) => ({ + description: test.description, + status: result.status, + message: result.message, + })), + ...flattenAssertions(test.tests), + ]) + +const collectAssertions = ( + testResults: HoppTestResult | null | undefined +): ExportedAssertion[] => { + if (!testResults) return [] + return [ + ...testResults.expectResults.map((result) => ({ + description: testResults.description, + status: result.status, + message: result.message, + })), + ...flattenAssertions(testResults.tests), + ] +} + +const exportResponse = ( + response: HoppRESTResponse | null | undefined, + bodyMode: RunnerBodyMode +): ExportedResponse | undefined => { + if (!response) return undefined + + // The kernel REST layer emits `"fail"` for HTTP error responses (4xx/5xx), + // even though the legacy `HoppRESTResponse` union still calls it `"failure"`. + // Match `"fail"` so error iterations keep their status/body/meta in the export + // (every other runner code path keys off `"fail"` too). + if (response.type === "success" || response.type === "fail") { + return { + type: response.type, + statusCode: response.statusCode, + statusText: response.statusText, + durationInMs: response.meta.responseDuration, + sizeInBytes: response.meta.responseSize, + headers: response.headers, + ...serializeBody(response.body, response.headers, bodyMode), + } + } + + return { + type: response.type, + error: + "error" in response + ? String((response as { error: unknown }).error) + : undefined, + } +} + +const exportRequest = ( + request: TestRunnerRequest, + bodyMode: RunnerBodyMode +): ExportedRequest => ({ + name: request.name, + method: request.method, + endpoint: request.endpoint, + passedTests: request.passedTests, + failedTests: request.failedTests, + error: request.error, + assertions: collectAssertions(request.testResults), + response: exportResponse(request.response, bodyMode), +}) + +const exportCollectionRequests = ( + collection: HoppCollection, + bodyMode: RunnerBodyMode +): ExportedRequest[] => [ + ...collection.requests.map((request) => + exportRequest(request as TestRunnerRequest, bodyMode) + ), + ...collection.folders.flatMap((folder) => + exportCollectionRequests(folder, bodyMode) + ), +] + +const exportIteration = ( + iterationResult: TestRunnerIterationResult, + bodyMode: RunnerBodyMode +): ExportedIteration => ({ + iteration: iterationResult.iteration, + summary: toSummary(iterationResult.meta), + // A restored iteration keeps only its summary — no rows to export. + requests: iterationResult.resultCollection + ? exportCollectionRequests(iterationResult.resultCollection, bodyMode) + : [], +}) + +const collectionHasRequestError = (collection: HoppCollection): boolean => + collection.requests.some((request) => + Boolean((request as TestRunnerRequest).error) + ) || collection.folders.some(collectionHasRequestError) + +const hasErrorAssertion = (tests: HoppTestData[]): boolean => + tests.some( + (test) => + test.expectResults.some((result) => result.status === "error") || + hasErrorAssertion(test.tests) + ) + +const testResultHasError = ( + testResults: HoppTestResult | null | undefined +): boolean => { + if (!testResults) return false + return ( + testResults.scriptError || + testResults.expectResults.some((result) => result.status === "error") || + hasErrorAssertion(testResults.tests) + ) +} + +const collectionHasTestError = (collection: HoppCollection): boolean => + collection.requests.some((request) => + testResultHasError((request as TestRunnerRequest).testResults) + ) || collection.folders.some(collectionHasTestError) + +const deriveOutcome = ( + document: HoppTestRunnerDocument +): "passed" | "failed" | "errored" => { + if (document.status === "error") return "errored" + + // A test-script error or an assertion with `status: "error"` does not bump + // failedTests, so fold those in explicitly — otherwise a run with an errored + // assertion (but no plain failures) would be reported as passed. + const hasError = (document.iterationResults ?? []).some( + (iteration) => + iteration.resultCollection !== undefined && + (collectionHasRequestError(iteration.resultCollection) || + collectionHasTestError(iteration.resultCollection)) + ) + + return document.testRunnerMeta.failedTests > 0 || hasError + ? "failed" + : "passed" +} + +export type RunnerExportScope = "all" | "current" + +export const buildRunnerResultReport = ( + document: HoppTestRunnerDocument, + scope: RunnerExportScope, + bodyMode: RunnerBodyMode +): RunnerResultReport => { + const allIterations = document.iterationResults ?? [] + const selectedIndex = document.selectedIteration ?? 0 + + const iterations = + scope === "current" + ? allIterations.filter((_, index) => index === selectedIndex) + : allIterations + + return { + collectionID: document.collectionID, + collectionName: document.collection.name, + collectionType: + document.collectionType === "my-collections" ? "PERSONAL" : "SHARED", + environment: document.environmentName ?? "Global", + outcome: deriveOutcome(document), + exportedAt: new Date().toISOString(), + config: { + iterations: document.config.iterations, + delayInMs: document.config.delay, + stopOnError: document.config.stopOnError, + persistResponses: document.config.persistResponses, + keepVariableValues: document.config.keepVariableValues, + dataFile: document.config.dataset?.fileName, + }, + summary: toSummary(document.testRunnerMeta), + iterationResults: iterations.map((iteration) => + exportIteration(iteration, bodyMode) + ), + } +} + +const decodeBase64Text = (base64: string): string | null => { + try { + const binary = atob(base64) + const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0)) + return new TextDecoder().decode(bytes) + } catch { + return null + } +} + +const base64ToReadableResponse = (response: ExportedResponse): void => { + if (response.bodyEncoding !== "base64" || response.body === undefined) return + if (!isTextContentType(getContentType(response.headers ?? []))) return + + const decoded = decodeBase64Text(response.body) + if (decoded === null) return + + response.body = decoded + response.bodyEncoding = "utf-8" +} + +/** + * The exported file uses a human-readable collectionType label while the + * stored report keeps a stable code. + */ +type ExportedReport = Omit & { + collectionType: string +} + +/** + * Re-encodes a stored (base64-body) report into the readable export format so + * exported files never contain base64 text bodies. Binary bodies stay base64. + */ +const reencodeReportForExport = ( + report: RunnerResultReport +): ExportedReport => { + const clone: RunnerResultReport = JSON.parse(JSON.stringify(report)) + clone.iterationResults.forEach((iteration) => + iteration.requests.forEach((request) => { + if (request.response) base64ToReadableResponse(request.response) + }) + ) + return { + ...clone, + collectionType: collectionRunTypeLabel(clone.collectionType), + } +} + +const saveReport = async (report: ExportedReport, filename: string) => { + const result = await platform.kernelIO.saveFileWithDialog({ + data: JSON.stringify(report, null, 2), + contentType: "application/json", + suggestedFilename: filename, + filters: [ + { + name: "Hoppscotch Collection Run Results JSON file", + extensions: ["json"], + }, + ], + }) + + if (result.type === "unknown" || result.type === "saved") { + return E.right("state.download_started") + } + + return E.left("state.download_failed") +} + +export const exportRunnerResults = async ( + document: HoppTestRunnerDocument, + scope: RunnerExportScope +) => + saveReport( + reencodeReportForExport( + buildRunnerResultReport(document, scope, "readable") + ), + `${document.collection.name || "collection"}-run.json` + ) diff --git a/packages/hoppscotch-common/src/helpers/rest/document.ts b/packages/hoppscotch-common/src/helpers/rest/document.ts index 95c65589484..8558934de2c 100644 --- a/packages/hoppscotch-common/src/helpers/rest/document.ts +++ b/packages/hoppscotch-common/src/helpers/rest/document.ts @@ -8,6 +8,7 @@ import { HoppInheritedProperty } from "../types/HoppInheritedProperties" import { HoppRESTResponse } from "../types/HoppRESTResponse" import { HoppTestResult } from "../types/HoppTestResult" import { TestRunnerRequest } from "~/services/test-runner/test-runner.service" +import { TestRunnerDataset } from "../runner/dataset" export type HoppRESTSaveContext = | { @@ -101,6 +102,22 @@ export type TestRunnerConfig = { stopOnError: boolean persistResponses: boolean keepVariableValues: boolean + dataset?: TestRunnerDataset +} + +export type TestRunnerMeta = { + totalRequests: number + completedRequests: number + totalTests: number + passedTests: number + failedTests: number + totalTime: number +} + +export type TestRunnerIterationResult = { + iteration: number + resultCollection?: HoppCollection + meta: TestRunnerMeta } export type HoppTestRunnerDocument = { @@ -146,17 +163,35 @@ export type HoppTestRunnerDocument = { */ resultCollection?: HoppCollection + /** + * Results grouped by iteration. + */ + iterationResults?: TestRunnerIterationResult[] + + /** + * Selected result iteration in the UI. + */ + selectedIteration?: number + + /** + * Requests selected to run. `undefined` runs the full collection; a non-empty + * array runs only those requests. An empty array means "no selection" and is + * rejected by the runner (the UI never produces it — it uses `undefined` for + * "run all"). + */ + selectedRequestRefIds?: string[] + + /** + * Name of the environment active when the run started ("Global" when none + * was selected). Captured at run start so it is stable across later env + * switches. + */ + environmentName?: string + /** * The test runner meta information */ - testRunnerMeta: { - totalRequests: number - completedRequests: number - totalTests: number - passedTests: number - failedTests: number - totalTime: number - } + testRunnerMeta: TestRunnerMeta /** * Selected test runner request diff --git a/packages/hoppscotch-common/src/helpers/runner/__tests__/dataset.spec.ts b/packages/hoppscotch-common/src/helpers/runner/__tests__/dataset.spec.ts new file mode 100644 index 00000000000..bde592570cb --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/__tests__/dataset.spec.ts @@ -0,0 +1,215 @@ +import * as E from "fp-ts/Either" +import { describe, expect, test } from "vitest" +import { datasetRowToTempVars, parseDatasetFile } from "../dataset" + +const file = (name: string, body: string) => new File([body], name) + +describe("collection runner dataset parsing", () => { + test("parses CSV headers as variable names and rows as iteration data", async () => { + const result = await parseDatasetFile( + file( + "testing.csv", + " title ,body,userId\nfirst,im first one,1\nsecond,im second one,2" + ) + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right).toEqual({ + type: "csv", + fileName: "testing.csv", + rows: [ + { title: "first", body: "im first one", userId: "1" }, + { title: "second", body: "im second one", userId: "2" }, + ], + }) + }) + + // PapaParse emits UndetectableDelimiter on single-column files even though + // the parse succeeded; it must not be treated as fatal. + test("accepts a single-column CSV", async () => { + const result = await parseDatasetFile( + file("testing.csv", "userId\n1\n2\n3") + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([ + { userId: "1" }, + { userId: "2" }, + { userId: "3" }, + ]) + }) + + test("accepts a single-column CSV prefixed with a UTF-8 BOM", async () => { + const result = await parseDatasetFile(file("testing.csv", "userId\n1\n2")) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([{ userId: "1" }, { userId: "2" }]) + }) + + test("ignores trailing whitespace-only lines", async () => { + const result = await parseDatasetFile( + file("testing.csv", "a,b\n1,2\n \n") + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([{ a: "1", b: "2" }]) + }) + + test("reports the spreadsheet line number for a malformed row", async () => { + const result = await parseDatasetFile( + file("testing.csv", "a,b\n1,2\n3\n4,5") + ) + + expect(E.isLeft(result)).toBe(true) + if (E.isRight(result)) return + + // Header is line 1, so the short row (parsed row index 1) is line 3. + expect(result.left).toContain("Line 3") + }) + + test("reports the spreadsheet line number for an unterminated quote", async () => { + const result = await parseDatasetFile( + file("testing.csv", 'user,pass\nbob,x\nalice,"unclosed') + ) + + expect(E.isLeft(result)).toBe(true) + if (E.isRight(result)) return + + // Quotes errors count the header itself as row 0 (PapaParse reports + // row 2 here), so the bad row is file line 3 — not line 4. + expect(result.left).toContain("Line 3") + }) + + // skipEmptyLines: "greedy" tests the parsed values with quotes already + // stripped, so a row whose every field is quoted whitespace is dropped + // exactly like a bare whitespace-only line. Accepted trade-off; a row with + // any non-blank field survives, and its quoted whitespace is preserved. + test("drops a row whose only field values are quoted whitespace", async () => { + const result = await parseDatasetFile( + file("testing.csv", 'user\nalice\n" "\nbob\n') + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([{ user: "alice" }, { user: "bob" }]) + }) + + test("keeps a quoted-whitespace field when another field has content", async () => { + const result = await parseDatasetFile( + file("testing.csv", 'a,b\n1,2\nx," "\n3,4') + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([ + { a: "1", b: "2" }, + { a: "x", b: " " }, + { a: "3", b: "4" }, + ]) + }) + + test("parses JSON rows and stringifies non-string values", async () => { + const result = await parseDatasetFile( + file( + "testing.json", + JSON.stringify([ + { + title: "first", + active: true, + count: 1, + nullable: null, + ids: [1, 2], + meta: { role: "admin" }, + }, + ]) + ) + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([ + { + title: "first", + active: "true", + count: "1", + nullable: "", + ids: "[1,2]", + meta: '{"role":"admin"}', + }, + ]) + }) + + test("preserves empty JSON rows as iterations with no variables", async () => { + const result = await parseDatasetFile( + file( + "testing.json", + JSON.stringify([{ title: "first" }, {}, { title: "third" }]) + ) + ) + + expect(E.isRight(result)).toBe(true) + if (E.isLeft(result)) return + + expect(result.right.rows).toEqual([ + { title: "first" }, + {}, + { title: "third" }, + ]) + }) + + test("returns a Left when the file cannot be read", async () => { + // A non-Blob makes FileReader.readAsText throw; the rejection must surface + // as a Left rather than an unhandled promise rejection. + const result = await parseDatasetFile({ + name: "unreadable.json", + } as unknown as File) + + expect(E.isLeft(result)).toBe(true) + }) + + test("rejects JSON files that are not arrays of objects", async () => { + const primitiveArray = await parseDatasetFile( + file("testing.json", JSON.stringify(["first", "second"])) + ) + const nestedArray = await parseDatasetFile( + file("testing.json", JSON.stringify([[1], [2]])) + ) + const object = await parseDatasetFile( + file("testing.json", JSON.stringify({ title: "first" })) + ) + + expect(E.isLeft(primitiveArray)).toBe(true) + expect(E.isLeft(nestedArray)).toBe(true) + expect(E.isLeft(object)).toBe(true) + }) + + test("converts a dataset row to temporary environment variables", () => { + expect( + datasetRowToTempVars({ title: "first", body: "im first one" }) + ).toEqual([ + { + key: "title", + initialValue: "first", + currentValue: "first", + secret: false, + }, + { + key: "body", + initialValue: "im first one", + currentValue: "im first one", + secret: false, + }, + ]) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/runner/__tests__/iteration-vars.spec.ts b/packages/hoppscotch-common/src/helpers/runner/__tests__/iteration-vars.spec.ts new file mode 100644 index 00000000000..312344a8e12 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/__tests__/iteration-vars.spec.ts @@ -0,0 +1,137 @@ +import { describe, expect, test } from "vitest" +import { stripIterationVarsFromEnvs } from "../iteration-vars" + +const v = (key: string, value: string) => ({ + key, + currentValue: value, + initialValue: value, + secret: false, +}) + +const keys = (...names: string[]) => new Set(names) + +const selectedOf = (result: { selected: { key: string }[] }) => + result.selected.map(({ key, ...rest }) => [ + key, + (rest as { currentValue: string }).currentValue, + ]) + +describe("stripIterationVarsFromEnvs", () => { + test("returns the envs untouched when no iteration keys were injected", () => { + const envs = { global: [v("g", "1")], selected: [v("a", "1")] } + expect(stripIterationVarsFromEnvs(envs, new Set(), [v("a", "0")])).toBe( + envs + ) + }) + + test("restores a shadowed env var and drops an unshadowed injected key", () => { + const initial = [v("token", "real"), v("host", "prod")] + // Injected: token (shadows) and userId (new). Post-script selected carries + // the injected values first, then the initial scope. + const envs = { + global: [], + selected: [ + v("token", "row-token"), + v("userId", "42"), + v("token", "real"), + v("host", "prod"), + ], + } + + const result = stripIterationVarsFromEnvs( + envs, + keys("token", "userId"), + initial + ) + + expect(selectedOf(result)).toEqual([ + ["token", "real"], + ["host", "prod"], + ]) + }) + + test("keeps a script update to a non-iteration key, at its original position", () => { + const initial = [v("a", "1"), v("b", "2")] + const envs = { + global: [], + selected: [v("col", "row"), v("a", "1"), v("b", "scripted")], + } + + const result = stripIterationVarsFromEnvs(envs, keys("col"), initial) + + expect(selectedOf(result)).toEqual([ + ["a", "1"], + ["b", "scripted"], + ]) + }) + + test("a script deletion of a non-iteration key stays deleted", () => { + const initial = [v("a", "1"), v("b", "2")] + const envs = { global: [], selected: [v("col", "row"), v("b", "2")] } + + const result = stripIterationVarsFromEnvs(envs, keys("col"), initial) + + expect(selectedOf(result)).toEqual([["b", "2"]]) + }) + + test("script-added keys append after the initial scope", () => { + const initial = [v("a", "1")] + const envs = { + global: [], + selected: [v("col", "row"), v("a", "1"), v("added", "new")], + } + + const result = stripIterationVarsFromEnvs(envs, keys("col"), initial) + + expect(selectedOf(result)).toEqual([ + ["a", "1"], + ["added", "new"], + ]) + }) + + test("does not collapse duplicate non-iteration keys onto one entry", () => { + // The env editor allows the same key on several rows; a flat last-wins + // lookup would collapse [A:"1", A:"2"] into [A:"2", A:"2"]. + const initial = [v("A", "1"), v("A", "2"), v("b", "x")] + const envs = { + global: [], + selected: [v("col", "row"), v("A", "1"), v("A", "2"), v("b", "x")], + } + + const result = stripIterationVarsFromEnvs(envs, keys("col"), initial) + + expect(selectedOf(result)).toEqual([ + ["A", "1"], + ["A", "2"], + ["b", "x"], + ]) + }) + + test("pairs duplicate keys by occurrence when a script updates the first", () => { + const initial = [v("A", "1"), v("A", "2")] + // Sandbox setEnv mutates the first matching occurrence in place. + const envs = { + global: [], + selected: [v("col", "row"), v("A", "9"), v("A", "2")], + } + + const result = stripIterationVarsFromEnvs(envs, keys("col"), initial) + + expect(selectedOf(result)).toEqual([ + ["A", "9"], + ["A", "2"], + ]) + }) + + test("passes global through untouched", () => { + const globalVars = [v("col", "script-wrote-this")] + const result = stripIterationVarsFromEnvs( + { global: globalVars, selected: [v("col", "row")] }, + keys("col"), + [] + ) + + expect(result.global).toBe(globalVars) + expect(result.selected).toEqual([]) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/runner/__tests__/selection.spec.ts b/packages/hoppscotch-common/src/helpers/runner/__tests__/selection.spec.ts new file mode 100644 index 00000000000..9240e3a7014 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/__tests__/selection.spec.ts @@ -0,0 +1,70 @@ +import { HoppCollection } from "@hoppscotch/data" +import { describe, expect, test } from "vitest" +import { applyRunOrder, collectRequestIDs } from "../selection" + +const request = (name: string) => ({ _ref_id: name, name }) as any + +const collection = ( + name: string, + requests: string[], + folders: HoppCollection[] = [] +) => ({ name, requests: requests.map(request), folders }) as any + +const items = (...ids: string[]) => ids.map((id) => ({ id })) +const idsOf = (list: { id: string }[]) => list.map(({ id }) => id) +const order = (...ids: string[]) => new Map(ids.map((id, index) => [id, index])) + +// `collectRequestIDs`, `RunnerRequestSelector.flatten` and `planCollection` +// flatten independently and must agree on the order: folders (depth-first) +// before a node's own requests. +describe("collectRequestIDs ordering contract", () => { + test("descends into folders before a collection's own requests", () => { + const tree = collection( + "root", + ["a", "b"], + [ + collection("f1", ["c"], [collection("f1a", ["d"])]), + collection("f2", ["e"]), + ] + ) + + expect(collectRequestIDs(tree)).toEqual(["d", "c", "e", "a", "b"]) + }) + + test("falls back to a positional id when a request has no _ref_id", () => { + const tree = { + name: "root", + requests: [{ name: "no-ref" }], + folders: [{ name: "f", requests: [{ name: "nested" }], folders: [] }], + } as any + + expect(collectRequestIDs(tree)).toEqual(["path:0/0", "path:0"]) + }) +}) + +describe("applyRunOrder", () => { + test("runs requests in the sequence the user set", () => { + const result = applyRunOrder(items("a", "b", "c"), order("c", "a", "b")) + expect(idsOf(result)).toEqual(["c", "a", "b"]) + }) + + test("keeps collection order when no sequence is set", () => { + const result = applyRunOrder(items("a", "b", "c"), new Map()) + expect(idsOf(result)).toEqual(["a", "b", "c"]) + }) + + test("appends requests the sequence does not mention, in collection order", () => { + const result = applyRunOrder(items("a", "b", "c", "d"), order("c", "a")) + expect(idsOf(result)).toEqual(["c", "a", "b", "d"]) + }) + + test("ignores sequence entries whose request no longer exists", () => { + const result = applyRunOrder(items("a", "b"), order("deleted", "b", "a")) + expect(idsOf(result)).toEqual(["b", "a"]) + }) + + test("is stable for items sharing a position", () => { + const result = applyRunOrder(items("a", "b", "c"), new Map([["c", 0]])) + expect(idsOf(result)).toEqual(["c", "a", "b"]) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/runner/__tests__/temp_envs.spec.ts b/packages/hoppscotch-common/src/helpers/runner/__tests__/temp_envs.spec.ts new file mode 100644 index 00000000000..aa481dc6eb2 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/__tests__/temp_envs.spec.ts @@ -0,0 +1,51 @@ +import { describe, expect, test } from "vitest" +import { filterNonEmptyEnvironmentVariables } from "~/helpers/utils/environments" +import { scriptEnvsToTemporaryVariables } from "../temp_envs" + +const v = (key: string, value: string) => ({ + key, + currentValue: value, + initialValue: value, + secret: false, +}) + +describe("scriptEnvsToTemporaryVariables", () => { + test("orders selected before global", () => { + const result = scriptEnvsToTemporaryVariables({ + global: [v("G", "g"), v("K", "global")], + selected: [v("K", "selected"), v("S", "s")], + }) + + expect(result.map(({ key }) => key)).toEqual(["K", "S", "G", "K"]) + expect(result[0]).toEqual( + expect.objectContaining({ key: "K", currentValue: "selected" }) + ) + }) + + // A key present in BOTH scopes must resolve to the same value on every + // request: request 1 resolves with an empty temp store, request 2+ through + // the temp store written from the previous request's script envs. + test("keeps a both-scopes key resolving to its selected value across requests", () => { + const selected = [v("K", "selected-value")] + const global = [v("K", "global-value")] + + const resolveRequest = ( + temp: ReturnType + ) => + filterNonEmptyEnvironmentVariables([ + ...temp, + ...selected, + ...global, + ]).find(({ key }) => key === "K") + + // Request 1: no temp store yet. + const request1 = resolveRequest([]) + // Request 2+: temp store carries the previous request's script envs. + const request2 = resolveRequest( + scriptEnvsToTemporaryVariables({ global, selected }) + ) + + expect(request1?.currentValue).toBe("selected-value") + expect(request2?.currentValue).toBe("selected-value") + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/runner/dataset.ts b/packages/hoppscotch-common/src/helpers/runner/dataset.ts new file mode 100644 index 00000000000..ff0e6896156 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/dataset.ts @@ -0,0 +1,139 @@ +import { Environment } from "@hoppscotch/data" +import * as E from "fp-ts/Either" +import Papa from "papaparse" + +export type DatasetFileType = "csv" | "json" + +export type DatasetRow = Record + +export type TestRunnerDataset = { + fileName: string + type: DatasetFileType + rows: DatasetRow[] +} + +const getDatasetFileType = (fileName: string): DatasetFileType | null => { + const extension = fileName.split(".").pop()?.toLowerCase() + + if (extension === "csv" || extension === "json") return extension + return null +} + +const isPlainObject = (value: unknown): value is Record => + typeof value === "object" && value !== null && !Array.isArray(value) + +export const stringifyDatasetValue = (value: unknown) => { + if (value === null || value === undefined) return "" + if (typeof value === "object") return JSON.stringify(value) + + return String(value) +} + +const normalizeRow = (row: Record): DatasetRow => + Object.fromEntries( + Object.entries(row) + .filter(([key]) => key.trim().length > 0) + .map(([key, value]) => [key.trim(), stringifyDatasetValue(value)]) + ) + +// PapaParse reports notices through the same `errors` array as real failures. +// `UndetectableDelimiter` fires on every single-column file even though the +// parse succeeded, so `Delimiter` errors are never fatal. +const isFatalParseError = (error: Papa.ParseError) => error.type !== "Delimiter" + +// PapaParse's `row` base differs by error type: FieldMismatch counts 0-based +// data rows (header excluded, so +2 is the file line), but Quotes errors come +// from the core parser where the header itself is row 0 (so +1). +const formatParseError = (error: Papa.ParseError) => + typeof error.row === "number" + ? `Line ${error.row + (error.type === "Quotes" ? 1 : 2)}: ${error.message}` + : error.message + +const parseCSV = (contents: string): E.Either => { + const parsed = Papa.parse>(contents, { + header: true, + // "greedy" also drops whitespace-only lines, which plain `true` parses as + // one-field rows that fail the file with TooFewFields. Trade-off: greedy + // tests the parsed values (quotes already stripped), so a row whose every + // field is quoted whitespace (e.g. `" "`) is dropped too. + skipEmptyLines: "greedy", + transformHeader: (header) => header.trim(), + }) + + const fatalErrors = parsed.errors.filter(isFatalParseError) + + if (fatalErrors.length > 0) { + return E.left(fatalErrors.map(formatParseError).join(", ")) + } + + // A row with no data columns is still a valid iteration. + return E.right(parsed.data.map(normalizeRow)) +} + +const parseJSON = (contents: string): E.Either => { + try { + const parsed = JSON.parse(contents) + + if (!Array.isArray(parsed)) { + return E.left("JSON data file must be an array of objects") + } + + if (!parsed.every(isPlainObject)) { + return E.left("JSON data file must contain only objects") + } + + // An empty object is still a valid iteration; don't drop it. + return E.right(parsed.map(normalizeRow)) + } catch (error) { + return E.left( + error instanceof Error ? error.message : "Invalid JSON data file" + ) + } +} + +const readFileAsText = (file: File) => + new Promise((resolve, reject) => { + const reader = new FileReader() + + reader.onload = () => resolve(String(reader.result ?? "")) + reader.onerror = () => + reject(reader.error ?? new Error("Failed to read data file")) + reader.readAsText(file) + }) + +export const parseDatasetFile = async ( + file: File +): Promise> => { + const type = getDatasetFileType(file.name) + + if (!type) return E.left("Unsupported data file type") + + let contents: string + try { + contents = await readFileAsText(file) + } catch (error) { + return E.left( + error instanceof Error ? error.message : "Failed to read data file" + ) + } + + const parsedRows = type === "csv" ? parseCSV(contents) : parseJSON(contents) + + if (E.isLeft(parsedRows)) return parsedRows + + return E.right({ + fileName: file.name, + type, + rows: parsedRows.right, + }) +} + +export const datasetRowToTempVars = ( + row: DatasetRow +): Environment["variables"] => + Object.entries(row).map(([key, value]) => ({ + key, + initialValue: value, + currentValue: value, + secret: false, + })) diff --git a/packages/hoppscotch-common/src/helpers/runner/iteration-vars.ts b/packages/hoppscotch-common/src/helpers/runner/iteration-vars.ts new file mode 100644 index 00000000000..09c7c9ce9ea --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/iteration-vars.ts @@ -0,0 +1,60 @@ +import { Environment } from "@hoppscotch/data" +import { TestResult } from "@hoppscotch/js-sandbox" + +/** + * Strips injected data-file iteration variables back out of a post-script env + * so a data-driven run stays ephemeral on writeback, restoring any selected + * variable a dataset column shadowed. + * + * The initial scope is walked in its original order because + * `updateEnvsAfterTestScript` persists `selected` wholesale — rebuilding in a + * different order would reshuffle the user's environment. Lookups are + * occurrence-paired rather than key-flat: the env editor allows duplicate + * keys and sandbox writes mutate the first matching occurrence. Script-added + * keys are appended; `global` is left alone (iteration values are never + * injected into it). + * + * Known limit: if a script DELETES an occurrence of a key duplicated in the + * initial scope, the surviving occurrence keeps the deleted row's position + * (initial-order walk) instead of its post-script one. Values stay correct; + * only the relative order of that pair can differ from the script's view. + */ +export const stripIterationVarsFromEnvs = ( + envs: TestResult["envs"], + iterationVarKeys: Set, + initialSelected: Environment["variables"] +): TestResult["envs"] => { + if (iterationVarKeys.size === 0) return envs + + // key → queue of final entries, consumed one per initial occurrence. + const finalByKey = new Map() + for (const env of envs.selected) { + const queue = finalByKey.get(env.key) + if (queue) queue.push(env) + else finalByKey.set(env.key, [env]) + } + + const initialKeys = new Set(initialSelected.map(({ key }) => key)) + + const rebuilt: TestResult["envs"]["selected"] = [] + for (const initialVar of initialSelected) { + if (iterationVarKeys.has(initialVar.key)) { + rebuilt.push(initialVar) + continue + } + + // No final occurrence left → a script deleted this row. + const final = finalByKey.get(initialVar.key)?.shift() + if (final) rebuilt.push(final) + } + + return { + global: envs.global, + selected: [ + ...rebuilt, + ...envs.selected.filter( + ({ key }) => !initialKeys.has(key) && !iterationVarKeys.has(key) + ), + ], + } +} diff --git a/packages/hoppscotch-common/src/helpers/runner/selection.ts b/packages/hoppscotch-common/src/helpers/runner/selection.ts new file mode 100644 index 00000000000..8139e3d0a87 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/runner/selection.ts @@ -0,0 +1,47 @@ +import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" + +export const getRequestSelectionID = ( + request: HoppRESTRequest, + path: number[] +) => request._ref_id || `path:${path.join("/")}` + +/** + * Applies a run sequence to a list of items keyed by selection ID. + * + * Items named by `runOrder` come first, in the position it gives them; the + * rest keep their original relative order behind them, so a request added + * after the sequence was saved still runs. + */ +export const applyRunOrder = ( + items: T[], + runOrder: Map +): T[] => { + const positionOf = (item: T) => + runOrder.get(item.id) ?? Number.MAX_SAFE_INTEGER + + return items + .map((item, index) => ({ item, index })) + .sort( + (a, b) => positionOf(a.item) - positionOf(b.item) || a.index - b.index + ) + .map(({ item }) => item) +} + +/** + * Recursively collects the selection IDs of every request in a collection + * tree, in the runner's execution order: folders (depth-first) before a + * node's own requests. `RunnerRequestSelector` and `planCollection` flatten + * with the same rule — the three must agree or the displayed run sequence + * stops matching the executed one. + */ +export const collectRequestIDs = ( + collection: HoppCollection, + parentPath: number[] = [] +): string[] => [ + ...collection.folders.flatMap((folder, index) => + collectRequestIDs(folder, [...parentPath, index]) + ), + ...collection.requests.map((request, index) => + getRequestSelectionID(request as HoppRESTRequest, [...parentPath, index]) + ), +] diff --git a/packages/hoppscotch-common/src/helpers/runner/temp_envs.ts b/packages/hoppscotch-common/src/helpers/runner/temp_envs.ts index 88772573f1b..a34b782d15a 100644 --- a/packages/hoppscotch-common/src/helpers/runner/temp_envs.ts +++ b/packages/hoppscotch-common/src/helpers/runner/temp_envs.ts @@ -18,3 +18,18 @@ export function clearTemporaryVariables() { export function addTemporaryVariable(variable: GlobalEnvironmentVariable) { temporaryVariables.value.push(variable) } + +/** + * Shapes post-script env changes for the temporary-variable store + * (keepVariableValues = false runs). + * + * Selected must precede global: the temp scope outranks both real scopes and + * is deduped first-occurrence-wins, so this order keeps a key present in both + * scopes resolving to its selected value on every request of a run. + */ +export function scriptEnvsToTemporaryVariables(envs: { + global: GlobalEnvironmentVariable[] + selected: GlobalEnvironmentVariable[] +}): GlobalEnvironmentVariable[] { + return [...envs.selected, ...envs.global] +} diff --git a/packages/hoppscotch-common/src/helpers/utils/__tests__/inheritedCollectionVarTransformer.spec.ts b/packages/hoppscotch-common/src/helpers/utils/__tests__/inheritedCollectionVarTransformer.spec.ts new file mode 100644 index 00000000000..84c786a363b --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/utils/__tests__/inheritedCollectionVarTransformer.spec.ts @@ -0,0 +1,286 @@ +import { afterEach, describe, expect, test } from "vitest" +import { getService } from "~/modules/dioc" +import { CurrentValueService } from "~/services/current-environment-value.service" +import { SecretEnvironmentService } from "~/services/secret-environment.service" +import { + populateValuesInInheritedCollectionVars, + resolveInheritedVariables, +} from "../inheritedCollectionVarTransformer" + +const currentValues = getService(CurrentValueService) +const secretEnvs = getService(SecretEnvironmentService) + +// The services are module-scoped singletons — drop everything a test stored +// so no state leaks into the next one. +afterEach(() => { + currentValues.environments.clear() + secretEnvs.secretEnvironments.clear() +}) + +const collectionVar = (key: string, currentValue = "") => ({ + key, + currentValue, + initialValue: "", + secret: false, +}) + +const secretCollectionVar = (key: string) => ({ + key, + // Secret values never live on the collection JSON — only in + // SecretEnvironmentService. + currentValue: "", + initialValue: "", + secret: true, +}) + +const storedSecret = (key: string, value: string, varIndex: number) => ({ + key, + value, + varIndex, +}) + +const stored = (key: string, currentValue: string, varIndex: number) => ({ + key, + currentValue, + varIndex, + isSecret: false, +}) + +describe("resolveInheritedVariables", () => { + test("resolves each collection's variables under its own ID", () => { + currentValues.addEnvironment("coll-c", [stored("A", "a-current", 0)]) + currentValues.addEnvironment("coll-f", [stored("B", "b-current", 0)]) + + const levelC = resolveInheritedVariables([], [collectionVar("A")], "coll-c") + const levelF = resolveInheritedVariables( + levelC, + [collectionVar("B")], + "coll-f" + ) + const levelG = resolveInheritedVariables(levelF, [], "coll-g") + + expect(levelG).toEqual([ + expect.objectContaining({ key: "A", currentValue: "a-current" }), + expect.objectContaining({ key: "B", currentValue: "b-current" }), + ]) + }) + + // Re-resolving the MERGED array under one ID reads by index collision: + // A (merged index 0) would be looked up at (F.id, 0) — B's storage slot. + test("a folder's stored value is never assigned to an ancestor's variable", () => { + // Only F has a stored current value. + currentValues.addEnvironment("coll-f", [stored("B", "b-current", 0)]) + + const levelC = resolveInheritedVariables( + [], + [collectionVar("A", "a-own")], + "coll-c" + ) + const levelF = resolveInheritedVariables( + levelC, + [collectionVar("B")], + "coll-f" + ) + const levelG = resolveInheritedVariables(levelF, [], "coll-g") + + const a = levelG.find(({ key }) => key === "A") + const b = levelG.find(({ key }) => key === "B") + + expect(a?.currentValue).toBe("a-own") + expect(a?.currentValue).not.toBe("b-current") + expect(b?.currentValue).toBe("b-current") + }) + + test("parents pass through untouched, by reference", () => { + const parents = [collectionVar("A", "resolved-upstream")] + const result = resolveInheritedVariables( + parents, + [collectionVar("B")], + "coll-x" + ) + + expect(result[0]).toBe(parents[0]) + }) +}) + +describe("populateValuesInInheritedCollectionVars", () => { + test("returns [] without an owning collection ID", () => { + expect( + populateValuesInInheritedCollectionVars([collectionVar("A")], undefined) + ).toEqual([]) + }) + + test("falls back to the variable's own currentValue when nothing is stored", () => { + expect( + populateValuesInInheritedCollectionVars( + [collectionVar("A", "fallback")], + "coll-empty" + ) + ).toEqual([expect.objectContaining({ key: "A", currentValue: "fallback" })]) + }) + + // Team collections store client-local values under the server `id`, while + // the fetched tree regenerates `_ref_id` on every load. + test("resolves via the server-id fallback when the primary ref misses (team key scheme)", () => { + currentValues.addEnvironment("server-id-1", [ + { key: "T", currentValue: "team-CURRENT", varIndex: 0, isSecret: false }, + ]) + + expect( + populateValuesInInheritedCollectionVars( + [collectionVar("T")], + "coll_regenerated_random_ref", // fresh _ref_id: no stored entries + "server-id-1" + ) + ).toEqual([ + expect.objectContaining({ key: "T", currentValue: "team-CURRENT" }), + ]) + }) + + test("the primary key wins over the fallback when both have entries", () => { + currentValues.addEnvironment("primary-ref", [ + { + key: "P", + currentValue: "primary-CURRENT", + varIndex: 0, + isSecret: false, + }, + ]) + currentValues.addEnvironment("fallback-id", [ + { + key: "P", + currentValue: "fallback-CURRENT", + varIndex: 0, + isSecret: false, + }, + ]) + + expect( + populateValuesInInheritedCollectionVars( + [collectionVar("P")], + "primary-ref", + "fallback-id" + ) + ).toEqual([ + expect.objectContaining({ key: "P", currentValue: "primary-CURRENT" }), + ]) + }) +}) + +// Secret values are stored in SecretEnvironmentService, NOT +// CurrentValueService — without `showSecret` the lookup reads the wrong store +// and secrets silently resolve to "" in the runner. +describe("populateValuesInInheritedCollectionVars — secret variables", () => { + test("resolves a secret's value from SecretEnvironmentService when showSecret is true", () => { + secretEnvs.addSecretEnvironment("sec-coll", [ + storedSecret("TOKEN", "s3cret-CURRENT", 0), + ]) + + expect( + populateValuesInInheritedCollectionVars( + [secretCollectionVar("TOKEN")], + "sec-coll", + undefined, + true + ) + ).toEqual([ + expect.objectContaining({ key: "TOKEN", currentValue: "s3cret-CURRENT" }), + ]) + }) + + test("resolves a secret via the server-id fallback when the primary ref misses (team key scheme)", () => { + secretEnvs.addSecretEnvironment("sec-srv-1", [ + storedSecret("TEAM_TOKEN", "team-s3cret", 0), + ]) + + expect( + populateValuesInInheritedCollectionVars( + [secretCollectionVar("TEAM_TOKEN")], + "sec_regenerated_random_ref", // fresh _ref_id: no stored entries + "sec-srv-1", + true + ) + ).toEqual([ + expect.objectContaining({ + key: "TEAM_TOKEN", + currentValue: "team-s3cret", + }), + ]) + }) + + test("keeps a secret masked (empty) when showSecret is false — the default", () => { + secretEnvs.addSecretEnvironment("sec-masked", [ + storedSecret("TOKEN", "must-not-appear", 0), + ]) + + expect( + populateValuesInInheritedCollectionVars( + [secretCollectionVar("TOKEN")], + "sec-masked" + ) + ).toEqual([expect.objectContaining({ key: "TOKEN", currentValue: "" })]) + }) + + // Both stores key by the variable's index in the FULL variable list, so a + // secret sitting after a non-secret must read its own slot in each store. + test("mixed secret and non-secret variables each resolve from their own store", () => { + currentValues.addEnvironment("sec-mixed", [ + stored("plain", "plain-CURRENT", 0), + ]) + secretEnvs.addSecretEnvironment("sec-mixed", [ + storedSecret("TOKEN", "mixed-s3cret", 1), + ]) + + expect( + populateValuesInInheritedCollectionVars( + [collectionVar("plain"), secretCollectionVar("TOKEN")], + "sec-mixed", + undefined, + true + ) + ).toEqual([ + expect.objectContaining({ key: "plain", currentValue: "plain-CURRENT" }), + expect.objectContaining({ key: "TOKEN", currentValue: "mixed-s3cret" }), + ]) + }) +}) + +describe("resolveInheritedVariables — secret variables", () => { + test("threads showSecret through to the own-level resolution", () => { + secretEnvs.addSecretEnvironment("sec-own", [ + storedSecret("OWN_TOKEN", "own-s3cret", 0), + ]) + + const resolved = resolveInheritedVariables( + [collectionVar("A", "resolved-upstream")], + [secretCollectionVar("OWN_TOKEN")], + "sec-own", + undefined, + true + ) + + expect(resolved).toEqual([ + expect.objectContaining({ key: "A", currentValue: "resolved-upstream" }), + expect.objectContaining({ + key: "OWN_TOKEN", + currentValue: "own-s3cret", + }), + ]) + }) + + test("defaults to masked secrets when showSecret is omitted", () => { + secretEnvs.addSecretEnvironment("sec-default", [ + storedSecret("TOKEN", "must-not-appear", 0), + ]) + + const resolved = resolveInheritedVariables( + [], + [secretCollectionVar("TOKEN")], + "sec-default" + ) + + expect(resolved).toEqual([ + expect.objectContaining({ key: "TOKEN", currentValue: "" }), + ]) + }) +}) diff --git a/packages/hoppscotch-common/src/helpers/utils/inheritedCollectionVarTransformer.ts b/packages/hoppscotch-common/src/helpers/utils/inheritedCollectionVarTransformer.ts index 53e47de0eea..61284c5c112 100644 --- a/packages/hoppscotch-common/src/helpers/utils/inheritedCollectionVarTransformer.ts +++ b/packages/hoppscotch-common/src/helpers/utils/inheritedCollectionVarTransformer.ts @@ -71,13 +71,55 @@ export const transformInheritedCollectionVariablesToAggregateEnv = ( */ export const populateValuesInInheritedCollectionVars = ( variables: HoppCollectionVariable[], - parentID?: string + parentID?: string, + /** + * Second storage key to try when `parentID` misses. Client-local values are + * stored under `_ref_id ?? id` for personal collections but under the + * server `id` for team collections, whose `_ref_id` is regenerated on every + * fetch — passing the server id here serves both key schemes. + */ + fallbackID?: string, + /** + * Secret values live in `SecretEnvironmentService`, not + * `CurrentValueService` — pass `true` from execution paths (the collection + * runner) so secret variables resolve. Leave `false` anywhere the output + * is rendered or persisted. + */ + showSecret: boolean = false ): HoppCollectionVariable[] => parentID ? variables.map((variable, index) => ({ ...variable, currentValue: - getCurrentValue(variable.secret, index, parentID) ?? + getCurrentValue(variable.secret, index, parentID, showSecret) ?? + (fallbackID && fallbackID !== parentID + ? getCurrentValue(variable.secret, index, fallbackID, showSecret) + : undefined) ?? variable.currentValue, })) : [] + +/** + * Resolves one level of collection-variable inheritance for the runner walk. + * + * Parents arrive already resolved and pass through untouched; only the + * current collection's own variables are populated here, under its own ID + * with indices into its own list — the `(collectionID, varIndex)` key shape + * `CurrentValueService` stores. Never re-resolve the merged array under a + * single ID: indices collide across owners and read other variables' slots. + */ +export const resolveInheritedVariables = ( + parentVariables: HoppCollectionVariable[], + ownVariables: HoppCollectionVariable[], + ownCollectionID?: string, + ownCollectionFallbackID?: string, + showSecret: boolean = false +): HoppCollectionVariable[] => [ + ...parentVariables, + ...populateValuesInInheritedCollectionVars( + ownVariables, + ownCollectionID, + ownCollectionFallbackID, + showSecret + ), +] diff --git a/packages/hoppscotch-common/src/newstore/__tests__/collections-inherited-props.spec.ts b/packages/hoppscotch-common/src/newstore/__tests__/collections-inherited-props.spec.ts new file mode 100644 index 00000000000..e607e67695c --- /dev/null +++ b/packages/hoppscotch-common/src/newstore/__tests__/collections-inherited-props.spec.ts @@ -0,0 +1,173 @@ +import { afterEach, describe, expect, test, vi } from "vitest" + +// newstore/collections sits on an import cycle (collections → services/tab/rest +// → services/persistence → collections); stub the cycle edge so the module +// loads under vitest. +vi.mock("~/services/tab/rest", () => ({ + RESTTabService: class MockRESTTabService { + static ID = "REST_TAB_SERVICE" + }, +})) +vi.mock("~/modules/i18n", () => ({ getI18n: () => (k: string) => k })) + +import { getService } from "~/modules/dioc" +import { CurrentValueService } from "~/services/current-environment-value.service" +import { SecretEnvironmentService } from "~/services/secret-environment.service" + +// The value stores are module-scoped singletons — drop everything a test +// stored so no state leaks into the next one. +afterEach(() => { + getService(CurrentValueService).environments.clear() + getService(SecretEnvironmentService).secretEnvironments.clear() +}) + +const collectionVar = (key: string, initialValue: string) => ({ + key, + initialValue, + // Current values live in CurrentValueService, not on the collection JSON. + currentValue: "", + secret: false, +}) + +const node = ( + refId: string, + name: string, + variables: ReturnType[], + folders: unknown[] = [] +) => ({ + v: 12, + _ref_id: refId, + name, + folders, + requests: [], + headers: [], + variables, + auth: { authType: "inherit", authActive: true }, + description: null, + preRequestScript: "", + testScript: "", +}) + +describe("getRESTCollectionInheritedProps — collection variable values", () => { + test("resolves every ancestor level's CURRENT value, not just the top one", async () => { + const { getRESTCollectionInheritedProps } = await import("../collections") + + const currentValues = getService(CurrentValueService) + currentValues.addEnvironment("ref-root", [ + { + key: "rootVar", + currentValue: "root-CURRENT", + varIndex: 0, + isSecret: false, + }, + ]) + currentValues.addEnvironment("ref-parent", [ + { + key: "parentVar", + currentValue: "parent-CURRENT", + varIndex: 0, + isSecret: false, + }, + ]) + + // root → parent folder → grandchild (the run target) + const tree = node( + "ref-root", + "Root", + [collectionVar("rootVar", "root-initial")], + [ + node( + "ref-parent", + "Parent", + [collectionVar("parentVar", "parent-initial")], + [node("ref-child", "Child", [])] + ), + ] + ) + + const props = getRESTCollectionInheritedProps( + "ref-child", + [tree] as any, + "my-collections" + ) + + expect(props).not.toBeNull() + expect(props!.ancestorVariables).toEqual([ + expect.objectContaining({ key: "rootVar", currentValue: "root-CURRENT" }), + expect.objectContaining({ + key: "parentVar", + currentValue: "parent-CURRENT", + }), + ]) + }) + + test("running a folder: ancestors resolved, own variables excluded from ancestors", async () => { + const { getRESTCollectionInheritedProps } = await import("../collections") + + const currentValues = getService(CurrentValueService) + currentValues.addEnvironment("ref-root2", [ + { key: "a", currentValue: "a-CURRENT", varIndex: 0, isSecret: false }, + ]) + currentValues.addEnvironment("ref-f2", [ + { key: "b", currentValue: "b-CURRENT", varIndex: 0, isSecret: false }, + ]) + + const tree = node( + "ref-root2", + "Root", + [collectionVar("a", "a-initial")], + [node("ref-f2", "F", [collectionVar("b", "b-initial")])] + ) + + const props = getRESTCollectionInheritedProps( + "ref-f2", + [tree] as any, + "my-collections" + ) + + expect(props!.ancestorVariables).toEqual([ + expect.objectContaining({ key: "a", currentValue: "a-CURRENT" }), + ]) + // The target's own variables stay out of the ancestor list — the runner + // resolves them itself from the raw collection. + expect(props!.ancestorVariables).toHaveLength(1) + }) + + test("running the root: no ancestors", async () => { + const { getRESTCollectionInheritedProps } = await import("../collections") + const tree = node("ref-solo", "Solo", [collectionVar("x", "x-initial")]) + const props = getRESTCollectionInheritedProps( + "ref-solo", + [tree] as any, + "my-collections" + ) + expect(props!.ancestorVariables).toEqual([]) + }) + + // Secret values live in SecretEnvironmentService, not CurrentValueService — + // this output feeds the runner's execution path, so secrets must resolve. + test("resolves an ancestor's SECRET value for the runner", async () => { + const { getRESTCollectionInheritedProps } = await import("../collections") + + getService(SecretEnvironmentService).addSecretEnvironment("ref-sec-root", [ + { key: "TOKEN", value: "root-s3cret", varIndex: 0 }, + ]) + + const tree = node( + "ref-sec-root", + "Root", + [{ key: "TOKEN", initialValue: "", currentValue: "", secret: true }], + [node("ref-sec-child", "Child", [])] + ) + + const props = getRESTCollectionInheritedProps( + "ref-sec-child", + [tree] as any, + "my-collections" + ) + + expect(props!.ancestorVariables).toEqual([ + expect.objectContaining({ key: "TOKEN", currentValue: "root-s3cret" }), + ]) + }) +}) diff --git a/packages/hoppscotch-common/src/newstore/collections.ts b/packages/hoppscotch-common/src/newstore/collections.ts index 38dbdc80b76..9e7a073c02f 100644 --- a/packages/hoppscotch-common/src/newstore/collections.ts +++ b/packages/hoppscotch-common/src/newstore/collections.ts @@ -21,6 +21,7 @@ import { RESTTabService } from "~/services/tab/rest" import DispatchingStore, { defineDispatchers } from "./DispatchingStore" import { SecretEnvironmentService } from "~/services/secret-environment.service" import { CurrentValueService } from "~/services/current-environment-value.service" +import { populateValuesInInheritedCollectionVars } from "~/helpers/utils/inheritedCollectionVarTransformer" //collection variables current value and secret value const secretEnvironmentService = getService(SecretEnvironmentService) @@ -1713,7 +1714,15 @@ export function getRESTCollection(collectionIndex: number) { export type RESTCollectionInheritedProps = { auth: HoppRESTAuth headers: HoppRESTHeaders - variables: HoppCollectionVariable[] + /** + * Ancestor collection variables only (root → target's parent), each level + * resolved under its OWNING collection's ID — resolved for EXECUTION + * (secret values included; never render or persist them). The target's own + * variables are deliberately NOT merged in: the runner resolves those + * itself, and re-resolving a merged array under one ID reads other + * variables' slots by index collision. + */ + ancestorVariables: HoppCollectionVariable[] // Ancestor scripts for partial-scope runs (root → target's parent). // Empty when running from the topmost collection. ancestorPreRequestScripts: string[] @@ -1741,9 +1750,19 @@ function computeCollectionInheritedProps( ...collection.headers, ] + // Each level's own variables are resolved under the level's OWN ID — the + // `(collectionID, varIndex)` key shape current values are stored with. + // Consumers must never re-resolve the merged array under a single ID. + // Secrets resolve (`showSecret`) because this only feeds the collection + // runner's execution path — the output is never rendered or persisted. const inheritedVariables = [ ...(parentVariables ?? []), - ...collection.variables, + ...populateValuesInInheritedCollectionVars( + collection.variables, + collection._ref_id || collection.id, + collection.id, + true + ), ] // Check if the current collection matches the target reference ID @@ -1756,7 +1775,7 @@ function computeCollectionInheritedProps( return { auth: inheritedAuth, headers: inheritedHeaders, - variables: inheritedVariables, + ancestorVariables: parentVariables ?? [], ancestorPreRequestScripts: parentPreRequestScripts, ancestorTestScripts: parentTestScripts, } diff --git a/packages/hoppscotch-common/src/services/persistence/index.ts b/packages/hoppscotch-common/src/services/persistence/index.ts index 7561f657ffc..714b0ca26c3 100644 --- a/packages/hoppscotch-common/src/services/persistence/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/index.ts @@ -1112,7 +1112,20 @@ export class PersistenceService extends Service { watchDebounced( this.restTabService.persistableTabState, async (newData) => { - await Store.set(STORE_NAMESPACE, STORE_KEYS.REST_TABS, newData) + const result = await Store.set( + STORE_NAMESPACE, + STORE_KEYS.REST_TABS, + newData + ) + + // A failed write (e.g. QuotaExceededError) silently freezes the + // persisted state for every REST tab at the last write that fit. + if (E.isLeft(result)) { + console.error( + `Failed persisting ${STORE_KEYS.REST_TABS}:`, + result.left + ) + } }, { debounce: 500, deep: true } ) diff --git a/packages/hoppscotch-common/src/services/persistence/validation-schemas/__tests__/testRunnerResultCollection.spec.ts b/packages/hoppscotch-common/src/services/persistence/validation-schemas/__tests__/testRunnerResultCollection.spec.ts new file mode 100644 index 00000000000..a2adde879e1 --- /dev/null +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/__tests__/testRunnerResultCollection.spec.ts @@ -0,0 +1,116 @@ +import { getDefaultRESTRequest } from "@hoppscotch/data" +import { describe, expect, test } from "vitest" + +import { TestRunnerResultCollectionSchema } from "../index" + +// A HoppRESTRequest augmented with the runner-only result fields, as it lives +// inside a persisted test-runner result collection. +const makeRunnerRequest = (name: string) => ({ + ...getDefaultRESTRequest(), + name, + type: "test-response" as const, + passedTests: 2, + failedTests: 1, + runnerRequestID: `rid-${name}`, + error: undefined, + isLoading: false, + renderResults: true, + testResults: { + tests: [], + expectResults: [], + description: "", + scriptError: false, + envDiff: { + global: { additions: [], updations: [], deletions: [] }, + selected: { additions: [], updations: [], deletions: [] }, + }, + consoleEntries: [], + }, +}) + +const makeResultCollection = () => ({ + v: 12, + name: "Result Collection", + auth: { authType: "inherit", authActive: true }, + headers: [], + variables: [], + description: null, + preRequestScript: "", + testScript: "", + requests: [makeRunnerRequest("top")], + folders: [ + { + v: 12, + name: "Nested", + auth: { authType: "inherit", authActive: true }, + headers: [], + variables: [], + description: null, + preRequestScript: "", + testScript: "", + requests: [makeRunnerRequest("nested")], + folders: [], + }, + ], +}) + +describe("TestRunnerResultCollectionSchema", () => { + test("preserves runner result fields on requests through a persist round-trip", () => { + const parsed = TestRunnerResultCollectionSchema.safeParse( + makeResultCollection() + ) + + expect(parsed.success).toBe(true) + if (!parsed.success) return + + const data = parsed.data as ReturnType + const topRequest = data.requests[0] + + // The runner-only fields must survive (the bug was HoppRESTCollectionSchema + // stripping them, blanking restored results and JSON export). + expect(topRequest.passedTests).toBe(2) + expect(topRequest.failedTests).toBe(1) + expect(topRequest.runnerRequestID).toBe("rid-top") + expect(topRequest.testResults).toBeDefined() + expect(topRequest.testResults?.scriptError).toBe(false) + + // ...and recursively for folder-nested requests. + const nestedRequest = data.folders[0].requests[0] + expect(nestedRequest.passedTests).toBe(2) + expect(nestedRequest.runnerRequestID).toBe("rid-nested") + expect(nestedRequest.testResults).toBeDefined() + }) + + test("migrates an older-version collection while keeping runner fields", () => { + // A v2 collection (pre-dates variables/preRequestScript/testScript). It must + // be migrated to the latest version — not rejected — and still keep the + // runner result fields on its requests. + const legacyCollection = { + v: 2, + name: "Legacy Collection", + auth: { authType: "inherit", authActive: true }, + headers: [], + requests: [makeRunnerRequest("legacy")], + folders: [], + } + + const parsed = TestRunnerResultCollectionSchema.safeParse(legacyCollection) + + expect(parsed.success).toBe(true) + if (!parsed.success) return + + const data = parsed.data as ReturnType & { + v: number + } + + // Migrated up: version bumped and the newer collection fields were filled. + expect(data.v).toBeGreaterThan(2) + expect(data.variables).toBeDefined() + expect(data.preRequestScript).toBeDefined() + + // ...and the runner result fields survived the migration. + expect(data.requests[0].passedTests).toBe(2) + expect(data.requests[0].runnerRequestID).toBe("rid-legacy") + expect(data.requests[0].testResults).toBeDefined() + }) +}) diff --git a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts index d9e3f694d2f..e8d9523ed3b 100644 --- a/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts +++ b/packages/hoppscotch-common/src/services/persistence/validation-schemas/index.ts @@ -487,6 +487,25 @@ const HoppTestResultSchema = z }) .strict() +const TestRunnerDatasetSchema = z + .object({ + fileName: z.string(), + type: z.enum(["csv", "json"]), + rows: z.array(z.record(z.string(), z.string())), + }) + .strict() + +const TestRunnerMetaSchema = z + .object({ + totalRequests: z.number(), + completedRequests: z.number(), + totalTests: z.number(), + passedTests: z.number(), + failedTests: z.number(), + totalTime: z.number(), + }) + .strict() + const HoppRESTResponseHeaderSchema = z .object({ key: z.string(), @@ -584,6 +603,41 @@ const validRestOperations = [ "requestVariables", ] as const +// The runner-only result fields that live on a request inside a test-runner +// result collection (`TestRunnerRequest`). All optional and lax — its only job +// is to re-capture the fields `entityReference` strips during migration. +const TestRunnerRequestResultFieldsSchema = z.object({ + type: z.optional(z.literal("test-response")), + response: z.optional(z.nullable(HoppRESTResponseSchema)), + testResults: z.optional(z.nullable(HoppTestResultSchema)), + isLoading: z.optional(z.boolean()), + error: z.optional(z.string()), + renderResults: z.optional(z.boolean()), + passedTests: z.optional(z.number()), + failedTests: z.optional(z.number()), + runnerRequestID: z.optional(z.string()), +}) + +// Mirrors the collection's requests/folders tree, capturing only the runner +// result fields on each request so it can be merged back onto the migrated +// collection. +const TestRunnerResultOverlaySchema: z.ZodType = z.lazy(() => + z.object({ + requests: z.array(TestRunnerRequestResultFieldsSchema), + folders: z.array(TestRunnerResultOverlaySchema), + }) +) + +// A test-runner result collection: the version-migrated HoppCollection (via +// entityReference, which strips runner fields) intersected with the overlay +// that re-captures them. z.intersection element-wise-merges the two, so an +// older-version persisted collection is still migrated AND the runner result +// fields survive the round-trip. +export const TestRunnerResultCollectionSchema = z.intersection( + HoppRESTCollectionSchema, + TestRunnerResultOverlaySchema +) + export const REST_TAB_STATE_SCHEMA = z .object({ lastActiveTabID: z.string(), @@ -599,20 +653,34 @@ export const REST_TAB_STATE_SCHEMA = z keepVariableValues: z.boolean(), persistResponses: z.boolean(), stopOnError: z.boolean(), + dataset: z.optional(TestRunnerDatasetSchema), }), status: z.enum(["idle", "running", "stopped", "error"]), collection: HoppRESTCollectionSchema, collectionType: z.enum(["my-collections", "team-collections"]), collectionID: z.optional(z.string()), - resultCollection: z.optional(HoppRESTCollectionSchema), - testRunnerMeta: z.object({ - totalRequests: z.number(), - completedRequests: z.number(), - totalTests: z.number(), - passedTests: z.number(), - failedTests: z.number(), - totalTime: z.number(), - }), + resultCollection: z.optional(TestRunnerResultCollectionSchema), + iterationResults: z.optional( + z.array( + z + .object({ + iteration: z.number(), + // Current builds persist no iterations at all (see + // `persistableTabState`); optional so states written by + // earlier builds still validate — and when they carry + // result trees, the runner fields survive the parse. + resultCollection: z.optional( + TestRunnerResultCollectionSchema + ), + meta: TestRunnerMetaSchema, + }) + .strict() + ) + ), + selectedIteration: z.optional(z.number()), + selectedRequestRefIds: z.optional(z.array(z.string())), + environmentName: z.optional(z.string()), + testRunnerMeta: TestRunnerMetaSchema, request: z.nullable(entityReference(HoppRESTRequest)), response: z.nullable(HoppRESTResponseSchema), testResults: z.optional(z.nullable(HoppTestResultSchema)), diff --git a/packages/hoppscotch-common/src/services/tab/rest.ts b/packages/hoppscotch-common/src/services/tab/rest.ts index 884491e7195..c420e5792c2 100644 --- a/packages/hoppscotch-common/src/services/tab/rest.ts +++ b/packages/hoppscotch-common/src/services/tab/rest.ts @@ -42,12 +42,39 @@ export class RESTTabService extends TabService { } if (tab.document.type === "test-runner") { + // Run results are deliberately not persisted: the collection schema + // strips `response`/`testResults`/counters so restored rows come back + // empty anyway, and per-iteration trees can blow the localStorage + // quota. The whole run is dropped; the tab restores ready to run. return { tabID: tab.id, doc: { ...tab.document, + // The debounced persist fires during a run, so a tab killed + // mid-run would restore as "running". Normalize to "stopped", + // never "idle" — an idle runner tab auto-runs on mount. + status: + tab.document.status === "running" + ? ("stopped" as const) + : tab.document.status, request: null, response: null, + // Drop the run outright rather than half of it: an iteration + // list without its result trees restores a summary over an + // empty table. + resultCollection: undefined, + iterationResults: undefined, + selectedIteration: 0, + // Meaningless without the result rows it points into. + selectedRequestPath: undefined, + testRunnerMeta: { + totalRequests: 0, + completedRequests: 0, + totalTests: 0, + passedTests: 0, + failedTests: 0, + totalTime: 0, + }, }, } } diff --git a/packages/hoppscotch-common/src/services/test-runner/__tests__/plan-collection.spec.ts b/packages/hoppscotch-common/src/services/test-runner/__tests__/plan-collection.spec.ts new file mode 100644 index 00000000000..1157850dee6 --- /dev/null +++ b/packages/hoppscotch-common/src/services/test-runner/__tests__/plan-collection.spec.ts @@ -0,0 +1,239 @@ +import { afterEach, describe, expect, test, vi } from "vitest" + +// RequestRunner drags in the network/kernel stack; the plan walk never +// touches it. +vi.mock("~/helpers/RequestRunner", () => ({ + captureInitialEnvironmentState: vi.fn(), + runTestRunnerRequest: vi.fn(), +})) + +import { getService } from "~/modules/dioc" +import { CurrentValueService } from "~/services/current-environment-value.service" +import { SecretEnvironmentService } from "~/services/secret-environment.service" +import { TestRunnerService } from "../test-runner.service" + +const currentValues = getService(CurrentValueService) +const secretEnvs = getService(SecretEnvironmentService) +const service = getService(TestRunnerService) + +// The value stores are module-scoped singletons — drop everything a test +// stored so no state leaks into the next one. +afterEach(() => { + currentValues.environments.clear() + secretEnvs.secretEnvironments.clear() +}) + +const planCollection = (collection: unknown, parentVariables: unknown[] = []) => + (service as any).planCollection( + collection, + new Set(), + false, + [], + [], + undefined, + undefined, + parentVariables + ) + +const collectionVar = (key: string, currentValue = "") => ({ + key, + currentValue, + initialValue: "", + secret: false, +}) + +const request = (name: string) => ({ + name, + _ref_id: `req-${name}`, + method: "GET", + endpoint: "https://example.com", + headers: [], + params: [], + auth: { authType: "inherit", authActive: true }, + preRequestScript: "", + testScript: "", +}) + +const node = ( + ids: { refId?: string; id?: string }, + variables: ReturnType[], + requests: ReturnType[] = [], + folders: unknown[] = [] +) => ({ + v: 12, + _ref_id: ids.refId, + id: ids.id, + name: ids.refId ?? ids.id ?? "node", + folders, + requests, + headers: [], + variables, + auth: { authType: "inherit", authActive: true }, + preRequestScript: "", + testScript: "", +}) + +const stored = (key: string, currentValue: string, varIndex: number) => ({ + key, + currentValue, + varIndex, + isSecret: false, +}) + +describe("TestRunnerService.planCollection — inherited variable resolution", () => { + test("resolves each level's own variables under that level's ID", () => { + currentValues.addEnvironment("plan-c", [stored("A", "a-current", 0)]) + currentValues.addEnvironment("plan-f", [stored("B", "b-current", 0)]) + + const tree = node( + { refId: "plan-c" }, + [collectionVar("A")], + [], + [node({ refId: "plan-f" }, [collectionVar("B")], [request("leaf")])] + ) + + const [planned] = planCollection(tree) + + // The index-collision guard: A (owned by the root) must never read B's + // storage slot at (plan-f, 0). + expect(planned.inheritedVariables).toEqual([ + expect.objectContaining({ key: "A", currentValue: "a-current" }), + expect.objectContaining({ key: "B", currentValue: "b-current" }), + ]) + }) + + test("falls back to the server id when _ref_id misses (team key scheme)", () => { + currentValues.addEnvironment("srv-1", [stored("T", "team-current", 0)]) + + const tree = node( + { refId: "regenerated-on-fetch", id: "srv-1" }, + [collectionVar("T")], + [request("team-leaf")] + ) + + const [planned] = planCollection(tree) + + expect(planned.inheritedVariables).toEqual([ + expect.objectContaining({ key: "T", currentValue: "team-current" }), + ]) + }) + + test("ancestor variables pass through pre-resolved; only the root's own raw list is resolved", () => { + currentValues.addEnvironment("plan-root", [stored("own", "own-current", 0)]) + // A stored value under the root at the ancestor's merged index — the bug + // shape: re-resolving a merged array under the root ID would hand this + // value to the ancestor variable. + const ancestor = { + key: "anc", + currentValue: "anc-RESOLVED-UPSTREAM", + initialValue: "", + secret: false, + } + + const tree = node( + { refId: "plan-root" }, + [collectionVar("own")], + [request("root-leaf")] + ) + + const [planned] = planCollection(tree, [ancestor]) + + expect(planned.inheritedVariables[0]).toBe(ancestor) + expect(planned.inheritedVariables).toEqual([ + expect.objectContaining({ + key: "anc", + currentValue: "anc-RESOLVED-UPSTREAM", + }), + expect.objectContaining({ key: "own", currentValue: "own-current" }), + ]) + }) + + // Secret values live in SecretEnvironmentService, not CurrentValueService; + // the plan walk must resolve them (showSecret) or every secret collection + // variable executes as "". + test("resolves secret variables from SecretEnvironmentService", () => { + secretEnvs.addSecretEnvironment("plan-sec", [ + { key: "TOKEN", value: "plan-s3cret", varIndex: 1 }, + ]) + currentValues.addEnvironment("plan-sec", [ + stored("plain", "plain-current", 0), + ]) + + const tree = node( + { refId: "plan-sec" }, + [ + collectionVar("plain"), + { key: "TOKEN", currentValue: "", initialValue: "", secret: true }, + ], + [request("secret-leaf")] + ) + + const [planned] = planCollection(tree) + + expect(planned.inheritedVariables).toEqual([ + expect.objectContaining({ key: "plain", currentValue: "plain-current" }), + expect.objectContaining({ key: "TOKEN", currentValue: "plan-s3cret" }), + ]) + }) + + test("resolves secret variables via the server-id fallback (team key scheme)", () => { + secretEnvs.addSecretEnvironment("srv-sec-1", [ + { key: "TEAM_TOKEN", value: "team-s3cret", varIndex: 0 }, + ]) + + const tree = node( + { refId: "regenerated-on-fetch-sec", id: "srv-sec-1" }, + [{ key: "TEAM_TOKEN", currentValue: "", initialValue: "", secret: true }], + [request("team-secret-leaf")] + ) + + const [planned] = planCollection(tree) + + expect(planned.inheritedVariables).toEqual([ + expect.objectContaining({ + key: "TEAM_TOKEN", + currentValue: "team-s3cret", + }), + ]) + }) +}) + +describe("TestRunnerService.getTestResultInfo — pass/fail counting", () => { + const count = (result: unknown) => (service as any).getTestResultInfo(result) + + test("counts error-status expectations as failures", () => { + expect( + count({ + expectResults: [ + { status: "pass", message: "" }, + { status: "fail", message: "" }, + { status: "error", message: "" }, + ], + tests: [], + }) + ).toEqual({ passed: 1, failed: 2 }) + }) + + test("counts a script error as one failure", () => { + expect(count({ scriptError: true, expectResults: [], tests: [] })).toEqual({ + passed: 0, + failed: 1, + }) + }) + + test("accumulates nested test blocks", () => { + expect( + count({ + scriptError: false, + expectResults: [{ status: "pass", message: "" }], + tests: [ + { + description: "", + expectResults: [{ status: "error", message: "" }], + tests: [], + }, + ], + }) + ).toEqual({ passed: 1, failed: 1 }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/test-runner/test-runner.service.ts b/packages/hoppscotch-common/src/services/test-runner/test-runner.service.ts index d2e8f6052fd..5a92fac53eb 100644 --- a/packages/hoppscotch-common/src/services/test-runner/test-runner.service.ts +++ b/packages/hoppscotch-common/src/services/test-runner/test-runner.service.ts @@ -1,6 +1,7 @@ import { HoppCollection, HoppCollectionVariable, + Environment, HoppRESTHeaders, HoppRESTRequest, } from "@hoppscotch/data" @@ -8,24 +9,56 @@ import { Service } from "dioc" import { hasActualScript } from "@hoppscotch/js-sandbox/scripting" import * as E from "fp-ts/Either" import { cloneDeep } from "lodash-es" -import { nextTick, Ref } from "vue" +import { Ref } from "vue" import { captureInitialEnvironmentState, runTestRunnerRequest, + type InitialEnvironmentState, } from "~/helpers/RequestRunner" import { HoppTestRunnerDocument, + TestRunnerMeta, TestRunnerConfig, } from "~/helpers/rest/document" import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse" import { HoppTestData, HoppTestResult } from "~/helpers/types/HoppTestResult" import { HoppTab } from "../tab" -import { populateValuesInInheritedCollectionVars } from "~/helpers/utils/inheritedCollectionVarTransformer" +import { resolveInheritedVariables } from "~/helpers/utils/inheritedCollectionVarTransformer" +import { datasetRowToTempVars } from "~/helpers/runner/dataset" +import { + applyRunOrder, + getRequestSelectionID, +} from "~/helpers/runner/selection" +import { clearTemporaryVariables } from "~/helpers/runner/temp_envs" + +// Sentinel errors that unwind the runner: "Test execution stopped" is a user +// cancel, "…stopped due to error" a stop-on-error halt. Both are normal +// terminations, so unwinding catches must recognize either form. +const STOP_SIGNAL_PREFIX = "Test execution stopped" +const isStopSignal = (error: unknown): error is Error => + error instanceof Error && error.message.startsWith(STOP_SIGNAL_PREFIX) export type TestRunnerOptions = { stopRef: Ref } & TestRunnerConfig +/** + * One request resolved against its ancestry and bound to its slot in the + * result tree. + */ +type PlannedRequest = { + /** Selection ID — how the run sequence refers to this request. */ + id: string + request: TestRunnerRequest + /** Owning collection/folder, for the test-script context. */ + collection: HoppCollection + /** Folder names from the run root down to this request's parent. */ + folderPath: string[] + inheritedVariables: HoppCollectionVariable[] + inheritedPreRequestScripts: string[] + inheritedTestScripts: string[] +} + export type TestRunnerRequest = HoppRESTRequest & { type: "test-response" response?: HoppRESTResponse | null @@ -35,6 +68,9 @@ export type TestRunnerRequest = HoppRESTRequest & { renderResults?: boolean passedTests: number failedTests: number + runnerRequestID?: string + /** Folder names from the run root down to this request's parent. */ + folderPath?: string[] } function delay(timeMS: number) { @@ -50,16 +86,19 @@ function delay(timeMS: number) { export class TestRunnerService extends Service { public static readonly ID = "TEST_RUNNER_SERVICE" - public runTests( - tab: Ref>, - collection: HoppCollection, - options: TestRunnerOptions, - ancestorPreRequestScripts: string[] = [], - ancestorTestScripts: string[] = [] - ) { - // Reset the result collection - tab.value.document.status = "running" - tab.value.document.resultCollection = { + private createEmptyMeta(): TestRunnerMeta { + return { + totalRequests: 0, + completedRequests: 0, + totalTests: 0, + passedTests: 0, + failedTests: 0, + totalTime: 0, + } + } + + private createResultCollection(collection: HoppCollection): HoppCollection { + return { v: collection.v, id: collection.id, name: collection.name, @@ -72,27 +111,114 @@ export class TestRunnerService extends Service { preRequestScript: collection.preRequestScript ?? "", testScript: collection.testScript ?? "", } + } - this.runTestCollection( + private shouldRunRequest( + request: HoppRESTRequest, + path: number[], + selectedIDs: Set, + selectionActive: boolean + ) { + return ( + !selectionActive || selectedIDs.has(getRequestSelectionID(request, path)) + ) + } + + private collectionHasSelectedRequest( + collection: HoppCollection, + parentPath: number[], + selectedIDs: Set, + selectionActive: boolean + ): boolean { + if (!selectionActive) return true + + return ( + collection.requests.some((request, index) => + this.shouldRunRequest( + request as HoppRESTRequest, + [...parentPath, index], + selectedIDs, + selectionActive + ) + ) || + collection.folders.some((folder, index) => + this.collectionHasSelectedRequest( + folder, + [...parentPath, index], + selectedIDs, + selectionActive + ) + ) + ) + } + + public runTests( + tab: Ref>, + collection: HoppCollection, + options: TestRunnerOptions, + ancestorPreRequestScripts: string[] = [], + ancestorTestScripts: string[] = [], + // Pre-resolved under their owning collections; the run root's own + // variables stay raw on `collection.variables` for the plan walk. + ancestorVariables: HoppCollectionVariable[] = [] + ) { + // `undefined` runs the full collection; an array runs that subset. + const selection = tab.value.document.selectedRequestRefIds + const selectionActive = Array.isArray(selection) + const selectedIDs = new Set(selection ?? []) + + // A selection can resolve to zero requests: an explicitly empty array + // (the UI sends `undefined` for "run all"), or IDs that stopped resolving + // after a refetch. Fail loudly rather than report a successful empty run. + if ( + selectionActive && + !this.collectionHasSelectedRequest(collection, [], selectedIDs, true) + ) { + tab.value.document.status = "error" + console.error( + "[Test Runner] The request selection matches no requests in this " + + "collection. Provide at least one request that exists in the tree, " + + "or omit the selection to run the full collection." + ) + return + } + + // Reset the result collection + tab.value.document.status = "running" + tab.value.document.resultCollection = undefined + tab.value.document.iterationResults = [] + tab.value.document.selectedIteration = 0 + tab.value.document.testRunnerMeta = this.createEmptyMeta() + clearTemporaryVariables() + + // One run per dataset row when a data file is attached; config.iterations + // only drives dataset-less runs (persisted/external state can diverge + // from the UI's lock). + const resolvedIterations = options.dataset?.rows.length + ? options.dataset.rows.length + : Math.max(1, Math.floor(Number(options.iterations)) || 1) + + // The selection array doubles as the run order; anything it doesn't + // mention keeps collection order, after everything it does. + const runOrder = new Map((selection ?? []).map((id, index) => [id, index])) + + this.runTestIterations( tab, collection, options, - [], - undefined, - undefined, - [], - undefined, + resolvedIterations, + selectedIDs, + selectionActive, + runOrder, ancestorPreRequestScripts, - ancestorTestScripts + ancestorTestScripts, + ancestorVariables ) .then(() => { tab.value.document.status = "stopped" }) .catch((error) => { - if ( - error instanceof Error && - error.message === "Test execution stopped" - ) { + if (isStopSignal(error)) { tab.value.document.status = "stopped" } else { tab.value.document.status = "error" @@ -100,129 +226,272 @@ export class TestRunnerService extends Service { } }) .finally(() => { - tab.value.document.status = "stopped" + if (tab.value.document.status !== "error") { + tab.value.document.status = "stopped" + } }) } - private async runTestCollection( + private async runTestIterations( tab: Ref>, collection: HoppCollection, options: TestRunnerOptions, - parentPath: number[] = [], + resolvedIterations: number, + selectedIDs: Set, + selectionActive: boolean, + runOrder: Map, + ancestorPreRequestScripts: string[] = [], + ancestorTestScripts: string[] = [], + ancestorVariables: HoppCollectionVariable[] = [] + ) { + for ( + let iterationIndex = 0; + iterationIndex < resolvedIterations; + iterationIndex++ + ) { + if (options.stopRef?.value) { + tab.value.document.status = "stopped" + throw new Error("Test execution stopped") + } + + if (!options.keepVariableValues) clearTemporaryVariables() + + // Without persisted values the env stores are never written back + // mid-iteration, so one snapshot serves the whole iteration. With + // keepVariableValues on, each request re-captures (left undefined here). + const iterationEnvState = options.keepVariableValues + ? undefined + : captureInitialEnvironmentState() + + const resultCollection = this.createResultCollection(collection) + const meta = this.createEmptyMeta() + // The UI locks the iteration count to the dataset length; if the two + // ever diverge, reuse the last row rather than read out of bounds. + const iterationVars = options.dataset?.rows.length + ? datasetRowToTempVars( + options.dataset.rows[ + Math.min(iterationIndex, options.dataset.rows.length - 1) + ] + ) + : [] + + tab.value.document.iterationResults?.push({ + iteration: iterationIndex + 1, + resultCollection, + meta, + }) + // `selectedIteration` is the iteration the user is VIEWING — owned by + // the jump control and scroll tracking, not the run. Advancing it here + // left a finished run's counter parked on the last iteration while the + // viewport still showed the first. + tab.value.document.resultCollection = resultCollection + + // Read the collection back off the document: the assignment above stores + // the raw object, and mutating a raw object never notifies Vue — rows + // must be appended through the reactive view. + const liveResultCollection = tab.value.document.resultCollection! + + const orderedPlan = applyRunOrder( + this.planCollection( + collection, + selectedIDs, + selectionActive, + [], + [], + undefined, + undefined, + ancestorVariables, + ancestorPreRequestScripts, + ancestorTestScripts + ), + runOrder + ) + + // Results are a flat list in run order; each row carries its folder + // path instead of nesting back under folders. + orderedPlan.forEach((entry) => + this.addRequestToPath(liveResultCollection, [], { + ...cloneDeep(entry.request), + runnerRequestID: entry.id, + folderPath: entry.folderPath, + passedTests: 0, + failedTests: 0, + }) + ) + + tab.value.document.testRunnerMeta.totalRequests += orderedPlan.length + meta.totalRequests += orderedPlan.length + + await this.runPlan( + tab, + orderedPlan, + options, + meta, + iterationVars, + iterationEnvState + ) + } + } + + /** + * Walks the collection and returns the requests to run, each with inherited + * auth/headers/variables/scripts resolved against its own ancestry. + * + * Planning is separate from execution so the run sequence can reorder + * across folders; result slots are allocated after ordering so results + * read in executed order. + */ + private planCollection( + collection: HoppCollection, + selectedIDs: Set, + selectionActive: boolean, + sourceParentPath: number[] = [], + folderPath: string[] = [], parentHeaders?: HoppRESTHeaders, parentAuth?: HoppRESTRequest["auth"], parentVariables: HoppCollection["variables"] = [], - parentID?: string, parentPreRequestScripts: string[] = [], parentTestScripts: string[] = [] - ) { - try { - // Compute inherited auth and headers for this collection - const inheritedAuth = - collection.auth?.authType === "inherit" && collection.auth.authActive - ? parentAuth || { authType: "none", authActive: false } - : collection.auth || { authType: "none", authActive: false } - - const inheritedHeaders: HoppRESTHeaders = [ - ...(parentHeaders || []), - ...collection.headers, - ] - - const inheritedVariables = [ - ...(populateValuesInInheritedCollectionVars( - parentVariables, - parentID || collection._ref_id || collection.id - ) || []), - ...(populateValuesInInheritedCollectionVars( - collection.variables, - collection._ref_id || collection.id - ) || []), - ] - - const inheritedPreRequestScripts = [ - ...parentPreRequestScripts, - ...(hasActualScript(collection.preRequestScript) - ? [collection.preRequestScript] - : []), - ] - const inheritedTestScripts = [ - ...parentTestScripts, - ...(hasActualScript(collection.testScript) - ? [collection.testScript] - : []), - ] - - // Process folders progressively - for (let i = 0; i < collection.folders.length; i++) { - if (options.stopRef?.value) { - tab.value.document.status = "stopped" - throw new Error("Test execution stopped") - } + ): PlannedRequest[] { + const inheritedAuth = + collection.auth?.authType === "inherit" && collection.auth.authActive + ? parentAuth || { authType: "none", authActive: false } + : collection.auth || { authType: "none", authActive: false } + + const inheritedHeaders: HoppRESTHeaders = [ + ...(parentHeaders || []), + ...collection.headers, + ] + + // Parents pass through already resolved; only this collection's own + // variables are populated here, under its own ID. The server `id` + // fallback mirrors the save-side keying for team collections, whose + // `_ref_id` is regenerated on every fetch. `showSecret` is true because + // this feeds execution only — planned requests are never persisted. + const inheritedVariables = resolveInheritedVariables( + parentVariables, + collection.variables, + collection._ref_id || collection.id, + collection.id, + true + ) - const folder = collection.folders[i] - const currentPath = [...parentPath, i] - - // Add folder to the result collection - this.addFolderToPath( - tab.value.document.resultCollection!, - currentPath, - { - ...cloneDeep(folder), - folders: [], - requests: [], - } + const inheritedPreRequestScripts = [ + ...parentPreRequestScripts, + ...(hasActualScript(collection.preRequestScript) + ? [collection.preRequestScript] + : []), + ] + const inheritedTestScripts = [ + ...parentTestScripts, + ...(hasActualScript(collection.testScript) + ? [collection.testScript] + : []), + ] + + const planned: PlannedRequest[] = [] + + // Folders (depth-first) before a node's own requests — must match + // `collectRequestIDs` and the run-sequence UI's flatten. + for (let i = 0; i < collection.folders.length; i++) { + const folder = collection.folders[i] + const sourcePath = [...sourceParentPath, i] + + if ( + !this.collectionHasSelectedRequest( + folder, + sourcePath, + selectedIDs, + selectionActive ) + ) { + continue + } - await this.runTestCollection( - tab, + planned.push( + ...this.planCollection( folder, - options, - currentPath, + selectedIDs, + selectionActive, + sourcePath, + [...folderPath, folder.name], inheritedHeaders, inheritedAuth, inheritedVariables, - collection._ref_id || collection.id, inheritedPreRequestScripts, inheritedTestScripts ) - } - - // Process requests progressively - for (let i = 0; i < collection.requests.length; i++) { - if (options.stopRef?.value) { - tab.value.document.status = "stopped" - throw new Error("Test execution stopped") - } + ) + } - const request = collection.requests[i] as TestRunnerRequest - const currentPath = [...parentPath, i] + for (let i = 0; i < collection.requests.length; i++) { + const request = collection.requests[i] as TestRunnerRequest + const sourcePath = [...sourceParentPath, i] - // Add request to the result collection before execution - this.addRequestToPath( - tab.value.document.resultCollection!, - currentPath, - cloneDeep(request) + if ( + !this.shouldRunRequest( + request, + sourcePath, + selectedIDs, + selectionActive ) + ) { + continue + } - // Update the request with inherited headers and auth before execution - const finalRequest = { + planned.push({ + id: getRequestSelectionID(request, sourcePath), + request: { ...request, auth: request.auth.authType === "inherit" && request.auth.authActive ? inheritedAuth : request.auth, headers: [...inheritedHeaders, ...request.headers], + }, + collection, + folderPath, + inheritedVariables, + inheritedPreRequestScripts, + inheritedTestScripts, + }) + } + + return planned + } + + /** + * Runs a plan in the given order, which is the user's run sequence when they + * set one and plain collection order otherwise. + */ + private async runPlan( + tab: Ref>, + plan: PlannedRequest[], + options: TestRunnerOptions, + iterationMeta: TestRunnerMeta, + iterationVars: Environment["variables"], + iterationEnvState?: InitialEnvironmentState + ) { + try { + for (const [index, entry] of plan.entries()) { + if (options.stopRef?.value) { + tab.value.document.status = "stopped" + throw new Error("Test execution stopped") } await this.runTestRequest( tab, - finalRequest, - collection, + entry.request, + entry.collection, options, - currentPath, - inheritedVariables, - inheritedPreRequestScripts, - inheritedTestScripts + // Result rows are allocated in this same order: plan index = row. + [index], + iterationMeta, + iterationVars, + entry.inheritedVariables, + entry.inheritedPreRequestScripts, + entry.inheritedTestScripts, + iterationEnvState ) if (options.delay && options.delay > 0) { @@ -237,10 +506,7 @@ export class TestRunnerService extends Service { } } } catch (error) { - if ( - error instanceof Error && - error.message === "Test execution stopped" - ) { + if (isStopSignal(error)) { throw error } tab.value.document.status = "error" @@ -249,40 +515,20 @@ export class TestRunnerService extends Service { } } - private addFolderToPath( - collection: HoppCollection, - path: number[], - folder: HoppCollection - ) { - let current = collection - - // Navigate to the parent folder - for (let i = 0; i < path.length - 1; i++) { - current = current.folders[path[i]] - } - - // Add the folder at the specified index - if (path.length > 0) { - current.folders[path[path.length - 1]] = folder - } - } - private addRequestToPath( collection: HoppCollection, - path: number[], + parentPath: number[], request: TestRunnerRequest ) { let current = collection // Navigate to the parent folder - for (let i = 0; i < path.length - 1; i++) { - current = current.folders[path[i]] + for (let i = 0; i < parentPath.length; i++) { + current = current.folders[parentPath[i]] } - // Add the request at the specified index - if (path.length > 0) { - current.requests[path[path.length - 1]] = request - } + current.requests.push(request) + return current.requests.length - 1 } private updateRequestAtPath( @@ -297,13 +543,13 @@ export class TestRunnerService extends Service { current = current.folders[path[i]] } - // Update the request at the specified index + // Mutate in place: selecting a request stores a reference to this object + // on the tab (`document.request`); replacing it would orphan that + // reference and the response would never reach the selected view. if (path.length > 0) { const index = path[path.length - 1] - current.requests[index] = { - ...current.requests[index], - ...updates, - } as TestRunnerRequest + const target = current.requests[index] + if (target) Object.assign(target, updates) } } @@ -313,9 +559,12 @@ export class TestRunnerService extends Service { collection: HoppCollection, options: TestRunnerOptions, path: number[], + iterationMeta: TestRunnerMeta, + iterationVars: Environment["variables"], inheritedVariables: HoppCollectionVariable[] = [], inheritedPreRequestScripts: string[] = [], - inheritedTestScripts: string[] = [] + inheritedTestScripts: string[] = [], + iterationEnvState?: InitialEnvironmentState ) { if (options.stopRef?.value) { throw new Error("Test execution stopped") @@ -328,14 +577,11 @@ export class TestRunnerService extends Service { error: undefined, }) - // Force Vue to flush DOM updates before starting async work. - // This ensures components consuming the isLoading state (such as those rendering the Send/Cancel button) update immediately. - // Performance impact: nextTick() waits for microtask queue drain (actual latency varies based on pending microtasks) - // but is necessary to prevent UI flicker and ensure loading indicators appear before long-running network requests. - await nextTick() - - // Capture the initial environment state for a test run so that it remains consistent and unchanged when current environment changes - const initialEnvironmentState = captureInitialEnvironmentState() + // Reuse the per-iteration snapshot when variable values aren't + // persisted; otherwise re-capture so this request sees env changes + // persisted by earlier requests in the run. + const initialEnvironmentState = + iterationEnvState ?? captureInitialEnvironmentState() const results = await runTestRunnerRequest( request, @@ -343,10 +589,16 @@ export class TestRunnerService extends Service { inheritedVariables, initialEnvironmentState, inheritedPreRequestScripts, - inheritedTestScripts + inheritedTestScripts, + iterationVars ) if (options.stopRef?.value) { + // Clear the loading flag so a stop taken mid-flight doesn't leave a + // permanent spinner on the row. + this.updateRequestAtPath(tab.value.document.resultCollection!, path, { + isLoading: false, + }) throw new Error("Test execution stopped") } @@ -357,11 +609,16 @@ export class TestRunnerService extends Service { tab.value.document.testRunnerMeta.totalTests += passed + failed tab.value.document.testRunnerMeta.passedTests += passed tab.value.document.testRunnerMeta.failedTests += failed + iterationMeta.totalTests += passed + failed + iterationMeta.passedTests += passed + iterationMeta.failedTests += failed // Update request with results and propagate pre-request script changes in the result collection this.updateRequestAtPath(tab.value.document.resultCollection!, path, { ...updatedRequest, testResults: testResult, + passedTests: passed, + failedTests: failed, response: options.persistResponses ? response : null, isLoading: false, }) @@ -370,6 +627,16 @@ export class TestRunnerService extends Service { tab.value.document.testRunnerMeta.totalTime += response.meta.responseDuration tab.value.document.testRunnerMeta.completedRequests += 1 + iterationMeta.totalTime += response.meta.responseDuration + iterationMeta.completedRequests += 1 + } + + // A post-request script failure arrives as a Right with `scriptError` + // set, so the Left/stop-on-error branch below never sees it. Halt + // here after the row and meta have recorded the request. + if (options.stopOnError && testResult.scriptError) { + tab.value.document.status = "stopped" + throw new Error("Test execution stopped due to error") } } else { const errorMsg = "Request execution failed" @@ -391,10 +658,7 @@ export class TestRunnerService extends Service { } } } catch (error) { - if ( - error instanceof Error && - error.message === "Test execution stopped" - ) { + if (isStopSignal(error)) { throw error } @@ -414,14 +678,17 @@ export class TestRunnerService extends Service { } } - private getTestResultInfo(testResult: HoppTestData) { + private getTestResultInfo(testResult: HoppTestData | HoppTestResult) { let passed = 0 - let failed = 0 + // A failed script means the request's assertions never ran — count it as + // one failure so the meta counters and the run outcome reflect it. + // (`scriptError` exists only on the top-level `HoppTestResult`.) + let failed = "scriptError" in testResult && testResult.scriptError ? 1 : 0 for (const result of testResult.expectResults) { if (result.status === "pass") { passed++ - } else if (result.status === "fail") { + } else if (result.status === "fail" || result.status === "error") { failed++ } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32760ebc287..a8deae90cbe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -709,6 +709,9 @@ importers: paho-mqtt: specifier: 1.1.0 version: 1.1.0 + papaparse: + specifier: 5.5.4 + version: 5.5.4 path: specifier: 0.12.7 version: 0.12.7 @@ -884,6 +887,9 @@ importers: '@types/paho-mqtt': specifier: 1.0.10 version: 1.0.10 + '@types/papaparse': + specifier: 5.5.2 + version: 5.5.2 '@types/postman-collection': specifier: 3.5.11 version: 3.5.11 From 6e18f28aa8759579a35dbb1a8bb8cbe5ece5522f Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:37:59 +0530 Subject: [PATCH 11/14] test(cli): re-enable e2e test suite --- packages/hoppscotch-cli/vitest.config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/hoppscotch-cli/vitest.config.ts b/packages/hoppscotch-cli/vitest.config.ts index 697aa8b9569..bac92a0ec41 100644 --- a/packages/hoppscotch-cli/vitest.config.ts +++ b/packages/hoppscotch-cli/vitest.config.ts @@ -9,8 +9,6 @@ export default defineConfig({ "**/node_modules/**", "**/dist/**", "**/src/__tests__/functions/**/*.ts", - // echo.hoppscotch.io failing service-side, breaking these live e2e tests; re-enable once echo recovers. - "**/src/__tests__/e2e/**", ], }, }); From 5c46dd9e032d98fadfabb3eeb215c7be9016ed43 Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:40:36 +0530 Subject: [PATCH 12/14] chore: update lock file --- pnpm-lock.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8deae90cbe..6ceb4a25b1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11182,6 +11182,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + papaparse@5.5.4: + resolution: {integrity: sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==} + papaparse@5.6.0: resolution: {integrity: sha512-N2vuNQAYGK1/4vs6HJX86+VYU6OkiSTgdJz3JQfTk1y51cFCO/U8gnaeTF4iNE4r57Tt0sV47dUua1/19pxO6Q==} @@ -26470,6 +26473,8 @@ snapshots: pako@1.0.11: {} + papaparse@5.5.4: {} + papaparse@5.6.0: {} param-case@3.0.4: From 4789a0f2fae484aa7bc1233603d66b97b8ebfd8b Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:05:10 +0530 Subject: [PATCH 13/14] feat: REST-GQL Unified workspace (#6545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: “mirarifhasan” Co-authored-by: John An Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com> --- .../migration.sql | 55 + .../hoppscotch-backend/prisma/schema.prisma | 328 +-- packages/hoppscotch-backend/src/errors.ts | 13 +- .../src/infra-config/infra-config.model.ts | 2 +- .../src/mock-server/mock-server.controller.ts | 3 + .../mock-server/mock-server.service.spec.ts | 442 +++++ .../src/mock-server/mock-server.service.ts | 216 ++ .../src/shortcode/shortcode.model.ts | 2 +- .../src/shortcode/shortcode.service.spec.ts | 4 +- .../src/shortcode/shortcode.service.ts | 10 +- .../team-collection/team-collection.model.ts | 4 +- .../team-collection.resolver.ts | 4 +- .../team-collection.service.spec.ts | 73 +- .../team-collection.service.ts | 47 +- .../src/team-request/team-request.model.ts | 4 +- .../team-request/team-request.service.spec.ts | 58 +- .../src/team-request/team-request.service.ts | 25 +- .../src/types/RequestTypes.ts | 6 + .../user-collection/user-collections.model.ts | 4 +- .../src/user-history/user-history.model.ts | 6 +- .../src/user-request/user-request.model.ts | 4 +- packages/hoppscotch-cli/README.md | 29 + packages/hoppscotch-cli/package.json | 1 + .../src/__tests__/e2e/commands/test.spec.ts | 29 + .../e2e/fixtures/collections/gql-coll.json | 50 + .../collections/mixed-rest-gql-coll.json | 57 + .../e2e/fixtures/environments/gql-envs.json | 13 + .../unit/collection-fixtures.spec.ts | 112 ++ .../src/__tests__/unit/gql-auth-body.spec.ts | 86 + .../src/__tests__/unit/gql-request.spec.ts | 325 +++ .../hoppscotch-cli/src/utils/collections.ts | 14 +- .../hoppscotch-cli/src/utils/gql-request.ts | 150 ++ packages/hoppscotch-cli/src/utils/mutators.ts | 19 +- .../hoppscotch-cli/src/utils/pre-request.ts | 22 +- packages/hoppscotch-cli/src/utils/request.ts | 7 + .../src/utils/workspace-access.ts | 10 +- packages/hoppscotch-common/locales/en.json | 24 +- .../hoppscotch-common/src/components.d.ts | 40 + .../src/components/app/Inspection.vue | 2 +- .../src/components/app/ShortcutsPrompt.vue | 12 +- .../app/spotlight/entry/RESTRequest.vue | 8 + .../src/components/collections/AddRequest.vue | 4 +- .../src/components/collections/Collection.vue | 19 + .../components/collections/EditRequest.vue | 4 +- .../collections/ExampleResponse.vue | 15 +- .../components/collections/MyCollections.vue | 53 +- .../src/components/collections/Request.vue | 25 +- .../components/collections/SaveRequest.vue | 316 +-- .../collections/TeamCollections.vue | 29 +- .../documentation/CollectionPreview.vue | 2 +- .../documentation/CollectionStructure.vue | 8 +- .../collections/documentation/FolderItem.vue | 4 +- .../collections/documentation/Preview.vue | 26 +- .../PublishDocSnapshotPreview.vue | 22 +- .../collections/documentation/RequestItem.vue | 20 +- .../documentation/RequestPreview.vue | 225 ++- .../collections/documentation/index.vue | 21 +- .../documentation/sections/GqlVariables.vue | 42 + .../documentation/sections/Query.vue | 48 + .../documentation/sections/RequestBody.vue | 6 +- .../documentation/sections/Response.vue | 6 +- .../components/collections/graphql/index.vue | 7 + .../src/components/collections/index.vue | 627 ++++-- .../src/components/documentation/Content.vue | 17 +- .../src/components/embeds/GQLIndex.vue | 126 ++ .../src/components/embeds/GQLRequest.vue | 127 ++ .../src/components/embeds/Request.vue | 22 +- .../src/components/embeds/index.vue | 5 +- .../src/components/environments/Add.vue | 16 +- .../src/components/environments/index.vue | 25 +- .../src/components/gql/Argument.vue | 103 + .../src/components/gql/Arguments.vue | 32 + .../src/components/gql/Authorization.vue | 337 ++++ .../src/components/gql/DefaultValue.vue | 34 + .../src/components/gql/Directives.vue | 24 + .../src/components/gql/DocExplorer.vue | 110 ++ .../src/components/gql/EnumValues.vue | 95 + .../src/components/gql/ExplorerSection.vue | 24 + .../src/components/gql/Field.vue | 78 + .../src/components/gql/FieldDocumentation.vue | 61 + .../src/components/gql/FieldLink.vue | 62 + .../src/components/gql/Fields.vue | 49 + .../src/components/gql/Headers.vue | 718 +++++++ .../components/gql/ImplementsInterfaces.vue | 27 + .../src/components/gql/Query.vue | 300 +++ .../src/components/gql/Request.vue | 297 +++ .../src/components/gql/RequestOptions.vue | 428 ++++ .../src/components/gql/RequestTab.vue | 57 + .../src/components/gql/Response.vue | 498 +++++ .../src/components/gql/ResponseMeta.vue | 182 ++ .../src/components/gql/Schema.vue | 135 ++ .../components/gql/SchemaDocumentation.vue | 106 + .../src/components/gql/SchemaSearch.vue | 374 ++++ .../src/components/gql/SubscriptionLog.vue | 133 ++ .../src/components/gql/TabHead.vue | 164 ++ .../src/components/gql/TypeDocumentation.vue | 23 + .../src/components/gql/TypeLink.vue | 45 + .../src/components/gql/Variable.vue | 180 ++ .../src/components/gql/example/Response.vue | 164 ++ .../gql/example/ResponseRequest.vue | 174 ++ .../components/gql/example/ResponseTab.vue | 48 + .../src/components/graphql/Authorization.vue | 3 +- .../src/components/graphql/Headers.vue | 15 +- .../src/components/graphql/Request.vue | 4 +- .../src/components/graphql/RequestOptions.vue | 6 +- .../src/components/graphql/Response.vue | 4 +- .../src/components/graphql/ResponseMeta.vue | 12 +- .../src/components/history/Personal.vue | 250 ++- .../src/components/history/graphql/Card.vue | 20 +- .../components/history/graphql/MergedCard.vue | 108 + .../src/components/history/index.vue | 2 +- .../src/components/http/Authorization.vue | 11 +- .../src/components/http/Body.vue | 14 +- .../src/components/http/BodyParameters.vue | 14 +- .../src/components/http/Codegen.vue | 4 +- .../src/components/http/Headers.vue | 17 +- .../src/components/http/ImportCurl.vue | 4 +- .../src/components/http/KeyValue.vue | 4 +- .../src/components/http/Parameters.vue | 29 +- .../src/components/http/PreRequestScript.vue | 4 +- .../src/components/http/ProtocolSwitcher.vue | 278 +++ .../src/components/http/RawBody.vue | 4 + .../src/components/http/Request.vue | 12 +- .../src/components/http/RequestOptions.vue | 44 +- .../src/components/http/RequestTab.vue | 2 +- .../src/components/http/RequestVariables.vue | 10 +- .../src/components/http/Response.vue | 2 +- .../src/components/http/ResponseInterface.vue | 6 +- .../src/components/http/ResponseMeta.vue | 4 +- .../src/components/http/Sidebar.vue | 82 +- .../src/components/http/TabHead.vue | 16 +- .../src/components/http/TestResultEntry.vue | 8 + .../src/components/http/Tests.vue | 4 +- .../src/components/http/URLEncodedParams.vue | 13 +- .../components/http/authorization/ASAP.vue | 4 +- .../src/components/http/authorization/JWT.vue | 5 + .../components/http/authorization/OAuth2.vue | 23 +- .../http/example/LenseBodyRenderer.vue | 4 +- .../src/components/http/example/Response.vue | 5 +- .../http/example/ResponseRequest.vue | 6 +- .../components/http/example/ResponseTab.vue | 8 +- .../src/components/http/test/Response.vue | 2 +- .../components/http/test/ResultRequest.vue | 29 +- .../src/components/http/test/Runner.vue | 23 +- .../src/components/http/test/RunnerModal.vue | 6 +- .../http/test/RunnerRequestSelector.vue | 23 +- .../src/components/http/test/RunnerResult.vue | 2 +- .../ImportExportSteps/AllCollectionImport.vue | 2 +- .../ImportExportSteps/ImportSummary.vue | 6 +- .../src/components/importExport/types.ts | 6 +- .../lenses/ResponseBodyRenderer.vue | 5 +- .../lenses/renderers/HTMLLensRenderer.vue | 3 +- .../lenses/renderers/JSONLensRenderer.vue | 10 +- .../lenses/renderers/RawLensRenderer.vue | 3 +- .../lenses/renderers/XMLLensRenderer.vue | 3 +- .../src/components/mockServer/LogSection.vue | 3 +- .../src/components/share/CreateModal.vue | 53 +- .../components/share/CustomizeGQLModal.vue | 469 +++++ .../src/components/share/CustomizeModal.vue | 29 +- .../src/components/share/Modal.vue | 97 +- .../src/components/share/Request.vue | 62 +- .../src/components/share/index.vue | 366 ++-- .../src/components/share/templates/Embeds.vue | 6 +- .../components/share/templates/EmbedsGQL.vue | 104 + .../src/components/smart/EnvInput.vue | 19 +- .../src/components/workspace/Selector.vue | 5 +- .../src/composables/ai-experiments.ts | 12 +- .../src/composables/codemirror.ts | 159 +- .../src/composables/gqlWorkspaceVisibility.ts | 25 + .../src/composables/useDocumentationWorker.ts | 8 +- .../src/helpers/RequestRunner.ts | 107 +- .../hoppscotch-common/src/helpers/actions.ts | 13 +- .../src/helpers/auth/index.ts | 5 +- .../src/helpers/backend/GQLClient.ts | 2 +- .../src/helpers/backend/helpers.ts | 5 +- .../helpers/backend/mutations/MockServer.ts | 6 +- .../backend/mutations/PublishedDocs.ts | 6 +- .../helpers/backend/mutations/Shortcode.ts | 4 +- .../src/helpers/backend/mutations/Team.ts | 16 +- .../backend/mutations/TeamCollection.ts | 1 + .../backend/mutations/TeamInvitation.ts | 3 +- .../helpers/backend/queries/MockServerLogs.ts | 8 +- .../helpers/backend/queries/PublishedDocs.ts | 28 +- .../src/helpers/clientLocalVariables.ts | 9 +- .../src/helpers/collection/collection.ts | 20 +- .../src/helpers/collection/request.ts | 20 +- .../editor/extensions/HoppEnvironment.ts | 35 +- .../src/helpers/fixBrokenRequestVersion.ts | 40 +- .../graphql/__tests__/testRunner.spec.ts | 190 ++ .../src/helpers/graphql/connection.ts | 52 +- .../src/helpers/graphql/default.ts | 11 +- .../src/helpers/graphql/explorer.ts | 4 +- .../src/helpers/graphql/index.ts | 5 + .../src/helpers/graphql/testRunner.ts | 407 ++++ .../export/__tests__/runnerResults.spec.ts | 24 +- .../helpers/import-export/export/openapi.ts | 5 + .../import-export/export/runnerResults.ts | 10 +- .../src/helpers/import-export/import/hopp.ts | 39 +- .../src/helpers/import-export/import/index.ts | 4 +- .../import-export/import/insomnia/types.ts | 4 +- .../import/openapi/example-generators/v3.ts | 3 +- .../import/openapi/example-generators/v31.ts | 3 +- .../import-export/import/openapi/index.ts | 22 +- .../helpers/kernel/__tests__/kernel.spec.ts | 6 +- .../src/helpers/kernel/common/auth.ts | 56 +- .../src/helpers/kernel/gql/response.ts | 12 +- .../hoppscotch-common/src/helpers/network.ts | 14 +- .../src/helpers/realtime/SIOClients.ts | 7 +- .../src/helpers/request-type.ts | 124 ++ .../src/helpers/runner/collection-tree.ts | 1 + .../src/helpers/runner/selection.ts | 8 +- .../src/helpers/{rest => tab}/document.ts | 102 +- .../src/helpers/tab/type-converter.ts | 171 ++ .../src/helpers/teams/TeamCollection.ts | 2 +- .../helpers/teams/TeamCollectionAdapter.ts | 1215 ------------ .../src/helpers/teams/TeamRequest.ts | 44 +- .../src/helpers/teams/TeamsSearch.service.ts | 32 +- .../src/helpers/utils/EffectiveURL.ts | 215 +- .../__tests__/effectiveURLGQLAuth.spec.ts | 126 ++ .../helpers/workers/documentation.worker.ts | 8 +- .../src/helpers/workers/sandbox.worker.ts | 6 +- .../hoppscotch-common/src/kernel/store.ts | 3 +- .../src/lib/sync/collections/index.ts | 196 +- .../src/lib/sync/collections/sync.ts | 63 +- .../src/newstore/collections.ts | 26 +- .../src/newstore/settings.ts | 2 + .../hoppscotch-common/src/pages/e/_id.vue | 119 +- .../hoppscotch-common/src/pages/graphql.vue | 8 +- .../hoppscotch-common/src/pages/index.vue | 239 ++- .../hoppscotch-common/src/pages/oauth.vue | 6 +- .../hoppscotch-common/src/pages/r/_id.vue | 38 +- .../hoppscotch-common/src/pages/settings.vue | 9 + .../src/pages/view/_id/_version.vue | 22 +- .../hoppscotch-common/src/platform/backend.ts | 4 +- .../src/platform/std/backend.ts | 4 +- .../std/kernel-interceptors/proxy/index.ts | 3 +- .../hoppscotch-common/src/platform/tab.ts | 2 +- .../__tests__/workspace.service.spec.ts | 159 ++ .../menu/__tests__/url.menu.spec.ts | 4 +- .../context-menu/menu/parameter.menu.ts | 18 +- .../services/context-menu/menu/url.menu.ts | 8 +- .../src/services/documentation.service.ts | 13 +- .../src/services/gql-query-builder.service.ts | 457 +++++ .../services/gql-tab-connection.service.ts | 1759 +++++++++++++++++ .../src/services/initialization.service.ts | 6 +- .../inspection/__tests__/index.spec.ts | 78 +- .../src/services/inspection/index.ts | 81 +- .../__tests__/environment.inspector.spec.ts | 151 +- .../__tests__/request.inspector.spec.ts | 183 ++ .../inspectors/environment.inspector.ts | 201 +- .../inspectors/request.inspector.ts | 88 +- .../inspectors/response.inspector.ts | 13 +- .../scripting-interceptor.inspector.ts | 8 +- .../persistence/__tests__/__mocks__/index.ts | 24 +- .../persistence/__tests__/index.spec.ts | 33 +- .../__tests__/validation-schemas.spec.ts | 85 + .../src/services/persistence/index.ts | 27 +- .../persistence/validation-schemas/index.ts | 104 +- .../__tests__/environment.searcher.spec.ts | 96 + .../__tests__/history.searcher.spec.ts | 106 +- .../searchers/collections.searcher.ts | 76 +- .../searchers/environment.searcher.ts | 18 +- .../spotlight/searchers/history.searcher.ts | 45 +- .../spotlight/searchers/request.searcher.ts | 20 +- .../spotlight/searchers/tab.searcher.ts | 6 +- .../searchers/teamRequest.searcher.ts | 24 +- ...spec.ts => workspace-tabs.service.spec.ts} | 12 +- .../src/services/tab/index.ts | 47 +- .../src/services/tab/rest.ts | 171 -- .../hoppscotch-common/src/services/tab/tab.ts | 12 +- .../src/services/tab/workspace-tabs.ts | 359 ++++ .../src/services/team-collection.service.ts | 35 +- .../__tests__/plan-collection.spec.ts | 115 +- .../__tests__/run-gql-request.spec.ts | 236 +++ .../test-runner/test-runner.service.ts | 197 +- .../src/services/workspace.service.ts | 46 +- .../hoppscotch-data/src/collection/index.ts | 90 +- .../hoppscotch-data/src/environment/index.ts | 12 +- .../src/gql-request-response/index.ts | 49 + .../original-request/index.ts | 31 + .../original-request/v/1.ts | 20 + .../src/gql-request-response/v/0.ts | 39 + packages/hoppscotch-data/src/graphql/index.ts | 12 +- packages/hoppscotch-data/src/graphql/v/10.ts | 30 + packages/hoppscotch-data/src/index.ts | 1 + .../hoppscotch-js-sandbox/src/utils/shared.ts | 18 +- pnpm-lock.yaml | 3 + 287 files changed, 17891 insertions(+), 3393 deletions(-) create mode 100644 packages/hoppscotch-backend/prisma/migrations/20260713090000_mock_server_gql_examples/migration.sql create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/gql-coll.json create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/mixed-rest-gql-coll.json create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/gql-envs.json create mode 100644 packages/hoppscotch-cli/src/__tests__/unit/collection-fixtures.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/unit/gql-auth-body.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/unit/gql-request.spec.ts create mode 100644 packages/hoppscotch-cli/src/utils/gql-request.ts create mode 100644 packages/hoppscotch-common/src/components/collections/documentation/sections/GqlVariables.vue create mode 100644 packages/hoppscotch-common/src/components/collections/documentation/sections/Query.vue create mode 100644 packages/hoppscotch-common/src/components/embeds/GQLIndex.vue create mode 100644 packages/hoppscotch-common/src/components/embeds/GQLRequest.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Argument.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Arguments.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Authorization.vue create mode 100644 packages/hoppscotch-common/src/components/gql/DefaultValue.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Directives.vue create mode 100644 packages/hoppscotch-common/src/components/gql/DocExplorer.vue create mode 100644 packages/hoppscotch-common/src/components/gql/EnumValues.vue create mode 100644 packages/hoppscotch-common/src/components/gql/ExplorerSection.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Field.vue create mode 100644 packages/hoppscotch-common/src/components/gql/FieldDocumentation.vue create mode 100644 packages/hoppscotch-common/src/components/gql/FieldLink.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Fields.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Headers.vue create mode 100644 packages/hoppscotch-common/src/components/gql/ImplementsInterfaces.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Query.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Request.vue create mode 100644 packages/hoppscotch-common/src/components/gql/RequestOptions.vue create mode 100644 packages/hoppscotch-common/src/components/gql/RequestTab.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Response.vue create mode 100644 packages/hoppscotch-common/src/components/gql/ResponseMeta.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Schema.vue create mode 100644 packages/hoppscotch-common/src/components/gql/SchemaDocumentation.vue create mode 100644 packages/hoppscotch-common/src/components/gql/SchemaSearch.vue create mode 100644 packages/hoppscotch-common/src/components/gql/SubscriptionLog.vue create mode 100644 packages/hoppscotch-common/src/components/gql/TabHead.vue create mode 100644 packages/hoppscotch-common/src/components/gql/TypeDocumentation.vue create mode 100644 packages/hoppscotch-common/src/components/gql/TypeLink.vue create mode 100644 packages/hoppscotch-common/src/components/gql/Variable.vue create mode 100644 packages/hoppscotch-common/src/components/gql/example/Response.vue create mode 100644 packages/hoppscotch-common/src/components/gql/example/ResponseRequest.vue create mode 100644 packages/hoppscotch-common/src/components/gql/example/ResponseTab.vue create mode 100644 packages/hoppscotch-common/src/components/history/graphql/MergedCard.vue create mode 100644 packages/hoppscotch-common/src/components/http/ProtocolSwitcher.vue create mode 100644 packages/hoppscotch-common/src/components/share/CustomizeGQLModal.vue create mode 100644 packages/hoppscotch-common/src/components/share/templates/EmbedsGQL.vue create mode 100644 packages/hoppscotch-common/src/composables/gqlWorkspaceVisibility.ts create mode 100644 packages/hoppscotch-common/src/helpers/graphql/__tests__/testRunner.spec.ts create mode 100644 packages/hoppscotch-common/src/helpers/graphql/testRunner.ts create mode 100644 packages/hoppscotch-common/src/helpers/request-type.ts rename packages/hoppscotch-common/src/helpers/{rest => tab}/document.ts (73%) create mode 100644 packages/hoppscotch-common/src/helpers/tab/type-converter.ts delete mode 100644 packages/hoppscotch-common/src/helpers/teams/TeamCollectionAdapter.ts create mode 100644 packages/hoppscotch-common/src/helpers/utils/__tests__/effectiveURLGQLAuth.spec.ts create mode 100644 packages/hoppscotch-common/src/services/gql-query-builder.service.ts create mode 100644 packages/hoppscotch-common/src/services/gql-tab-connection.service.ts create mode 100644 packages/hoppscotch-common/src/services/persistence/__tests__/validation-schemas.spec.ts create mode 100644 packages/hoppscotch-common/src/services/spotlight/searchers/__tests__/environment.searcher.spec.ts rename packages/hoppscotch-common/src/services/tab/__tests__/{rest-tab.service.spec.ts => workspace-tabs.service.spec.ts} (97%) delete mode 100644 packages/hoppscotch-common/src/services/tab/rest.ts create mode 100644 packages/hoppscotch-common/src/services/tab/workspace-tabs.ts create mode 100644 packages/hoppscotch-common/src/services/test-runner/__tests__/run-gql-request.spec.ts create mode 100644 packages/hoppscotch-data/src/gql-request-response/index.ts create mode 100644 packages/hoppscotch-data/src/gql-request-response/original-request/index.ts create mode 100644 packages/hoppscotch-data/src/gql-request-response/original-request/v/1.ts create mode 100644 packages/hoppscotch-data/src/gql-request-response/v/0.ts create mode 100644 packages/hoppscotch-data/src/graphql/v/10.ts diff --git a/packages/hoppscotch-backend/prisma/migrations/20260713090000_mock_server_gql_examples/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20260713090000_mock_server_gql_examples/migration.sql new file mode 100644 index 00000000000..75f2893f054 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20260713090000_mock_server_gql_examples/migration.sql @@ -0,0 +1,55 @@ +-- Extend sync_mock_examples() to project the GraphQL operation identity of +-- saved example responses (operationName / operationType, stamped by the app +-- when a GraphQL run is saved as an example). The mock server's GraphQL +-- matcher selects examples by these, the way REST examples match by +-- method + path. REST examples project both fields as NULL. +CREATE OR REPLACE FUNCTION sync_mock_examples() +RETURNS TRIGGER AS $$ +BEGIN + NEW."mockExamples" := jsonb_build_object( + 'examples', + COALESCE( + ( + SELECT jsonb_agg( + jsonb_build_object( + 'key', key, + 'name', value->>'name', + 'endpoint', value->'originalRequest'->>'endpoint', + 'method', value->'originalRequest'->>'method', + 'headers', COALESCE(value->'originalRequest'->'headers', '[]'::jsonb), + 'statusCode', (value->>'code')::int, + 'statusText', value->>'status', + 'responseBody', value->>'body', + 'responseHeaders', COALESCE(value->'headers', '[]'::jsonb), + 'operationName', value->>'operationName', + 'operationType', value->>'operationType' + ) + ) + FROM jsonb_each(NEW.request->'responses') AS responses(key, value) + WHERE jsonb_typeof(NEW.request->'responses') = 'object' + ), + '[]'::jsonb + ) + ); + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Backfill: re-fire the trigger so existing rows pick up the new projection. +-- +-- Scoped to rows that actually carry saved example responses. `request` is +-- NOT NULL on both tables, so an `IS NOT NULL` guard would filter nothing and +-- rewrite every request row in the database — a full-table rewrite (plus bloat +-- and a long lock hold on large self-hosted instances) to recompute a value +-- that is unchanged for those rows: with no `responses` object the projection +-- COALESCEs to the same `{"examples": []}` the previous trigger produced. +UPDATE "UserRequest" +SET request = request +WHERE jsonb_typeof(request -> 'responses') = 'object' + AND request -> 'responses' <> '{}'::jsonb; + +UPDATE "TeamRequest" +SET request = request +WHERE jsonb_typeof(request -> 'responses') = 'object' + AND request -> 'responses' <> '{}'::jsonb; diff --git a/packages/hoppscotch-backend/prisma/schema.prisma b/packages/hoppscotch-backend/prisma/schema.prisma index 26a3b263059..54d8f25c435 100644 --- a/packages/hoppscotch-backend/prisma/schema.prisma +++ b/packages/hoppscotch-backend/prisma/schema.prisma @@ -7,14 +7,46 @@ datasource db { provider = "postgresql" } +// ============================================================ +// ENUMS +// ============================================================ + +enum WorkspaceType { + USER + TEAM +} + +enum ReqType { + REST + GQL +} + +enum TeamAccessRole { + OWNER + VIEWER + EDITOR +} + +enum MockServerAction { + CREATED + DELETED + ACTIVATED + DEACTIVATED +} + +// ============================================================ +// TEAM +// ============================================================ + model Team { - id String @id @default(cuid()) - name String - TeamCollection TeamCollection[] - TeamEnvironment TeamEnvironment[] - TeamInvitation TeamInvitation[] - members TeamMember[] - TeamRequest TeamRequest[] + id String @id @default(cuid()) + name String + + collections TeamCollection[] + environments TeamEnvironment[] + invitations TeamInvitation[] + members TeamMember[] + requests TeamRequest[] } model TeamMember { @@ -22,7 +54,8 @@ model TeamMember { role TeamAccessRole userUid String teamID String - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) @@unique([teamID, userUid]) } @@ -33,55 +66,46 @@ model TeamInvitation { creatorUid String inviteeEmail String inviteeRole TeamAccessRole - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) @@unique([teamID, inviteeEmail]) @@index([teamID]) } model TeamCollection { - id String @id @default(cuid()) + id String @id @default(cuid()) parentID String? teamID String title String orderIndex Int - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) data Json? - parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id], onDelete: Cascade) - children TeamCollection[] @relation("TeamCollectionChildParent") - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) - requests TeamRequest[] + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) + + parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id], onDelete: Cascade) + children TeamCollection[] @relation("TeamCollectionChildParent") + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + requests TeamRequest[] @@unique([teamID, parentID, orderIndex]) } model TeamRequest { - id String @id @default(cuid()) + id String @id @default(cuid()) collectionID String teamID String title String request Json mockExamples Json? orderIndex Int - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) - collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) - @@unique([teamID, collectionID, orderIndex]) -} - -model Shortcode { - id String @id @unique - request Json - creatorUid String? - createdOn DateTime @default(now()) @db.Timestamptz(3) - embedProperties Json? - updatedOn DateTime @default(now()) @updatedAt @db.Timestamptz(3) - User User? @relation(fields: [creatorUid], references: [uid]) + collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) - @@unique([id, creatorUid], name: "creator_uid_shortcode_unique") + @@unique([teamID, collectionID, orderIndex]) } model TeamEnvironment { @@ -89,32 +113,38 @@ model TeamEnvironment { teamID String name String variables Json - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) } +// ============================================================ +// USER +// ============================================================ + model User { - uid String @id @default(cuid()) - displayName String? - email String? @unique - photoURL String? - isAdmin Boolean @default(false) - refreshToken String? - currentRESTSession Json? - currentGQLSession Json? - createdOn DateTime @default(now()) @db.Timestamptz(3) - lastLoggedOn DateTime? @db.Timestamptz(3) - lastActiveOn DateTime? @db.Timestamptz(3) + uid String @id @default(cuid()) + displayName String? + email String? @unique + photoURL String? + isAdmin Boolean @default(false) + refreshToken String? + currentRESTSession Json? + currentGQLSession Json? + createdOn DateTime @default(now()) @db.Timestamptz(3) + lastLoggedOn DateTime? @db.Timestamptz(3) + lastActiveOn DateTime? @db.Timestamptz(3) + providerAccounts Account[] invitedUsers InvitedUsers[] mockServers MockServer[] personalAccessTokens PersonalAccessToken[] shortcodes Shortcode[] userCollections UserCollection[] - UserEnvironments UserEnvironment[] - UserHistory UserHistory[] + environments UserEnvironment[] + history UserHistory[] userRequests UserRequest[] settings UserSettings? - VerificationToken VerificationToken[] + verificationTokens VerificationToken[] } model Account { @@ -126,7 +156,8 @@ model Account { providerAccessToken String? providerScope String? loggedIn DateTime @default(now()) @db.Timestamptz(3) - user User @relation(fields: [userId], references: [uid], onDelete: Cascade) + + user User @relation(fields: [userId], references: [uid], onDelete: Cascade) @@unique([provider, providerAccountId], name: "verifyProviderAccount") } @@ -136,7 +167,8 @@ model VerificationToken { token String @unique @default(cuid()) userUid String expiresOn DateTime @db.Timestamptz(3) - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) @@unique([deviceIdentifier, token], name: "passwordless_deviceIdentifier_tokens") } @@ -146,7 +178,8 @@ model UserSettings { userUid String @unique properties Json updatedOn DateTime @updatedAt @db.Timestamptz(3) - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) } model UserHistory { @@ -157,7 +190,8 @@ model UserHistory { responseMetadata Json isStarred Boolean executedOn DateTime @default(now()) @db.Timestamptz(3) - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) } model UserEnvironment { @@ -166,7 +200,8 @@ model UserEnvironment { name String? variables Json isGlobal Boolean - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) } model InvitedUsers { @@ -174,20 +209,22 @@ model InvitedUsers { adminEmail String inviteeEmail String @unique invitedOn DateTime @default(now()) @db.Timestamptz(3) - user User @relation(fields: [adminUid], references: [uid], onDelete: Cascade) + + user User @relation(fields: [adminUid], references: [uid], onDelete: Cascade) } model UserRequest { - id String @id @default(cuid()) - collectionID String - userUid String - title String - request Json - mockExamples Json? - type ReqType - orderIndex Int - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) + id String @id @default(cuid()) + collectionID String + userUid String + title String + request Json + type ReqType + mockExamples Json? + orderIndex Int + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) + userCollection UserCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) @@ -195,77 +232,52 @@ model UserRequest { } model UserCollection { - id String @id @default(cuid()) + id String @id @default(cuid()) parentID String? userUid String title String - orderIndex Int type ReqType - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) + orderIndex Int data Json? - parent UserCollection? @relation("ParentUserCollection", fields: [parentID], references: [id], onDelete: Cascade) - children UserCollection[] @relation("ParentUserCollection") - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) - requests UserRequest[] - - @@unique([userUid, parentID, orderIndex]) -} + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) -model InfraConfig { - id String @id @default(cuid()) - name String @unique - value String? - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) - isEncrypted Boolean @default(false) - lastSyncedEnvFileValue String? -} + parent UserCollection? @relation("ParentUserCollection", fields: [parentID], references: [id], onDelete: Cascade) + children UserCollection[] @relation("ParentUserCollection") + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + requests UserRequest[] -model PersonalAccessToken { - id String @id @default(cuid()) - userUid String - label String - token String @unique @default(uuid()) - expiresOn DateTime? @db.Timestamptz(3) - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) - user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + @@unique([userUid, parentID, orderIndex]) } -model InfraToken { - id String @id @default(cuid()) - creatorUid String - label String - token String @unique @default(uuid()) - expiresOn DateTime? @db.Timestamptz(3) - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @default(now()) @db.Timestamptz(3) -} +// ============================================================ +// MOCK SERVER +// ============================================================ model MockServer { - id String @id @default(cuid()) - name String - subdomain String @unique - creatorUid String? - collectionID String - workspaceType WorkspaceType - workspaceID String - delayInMs Int @default(0) - isPublic Boolean @default(true) - isActive Boolean @default(true) - hitCount Int @default(0) - lastHitAt DateTime? @db.Timestamptz(3) - createdOn DateTime @default(now()) @db.Timestamptz(3) - updatedOn DateTime @updatedAt @db.Timestamptz(3) - deletedAt DateTime? @db.Timestamptz(3) + id String @id @default(cuid()) + name String + subdomain String @unique + creatorUid String? + collectionID String + workspaceType WorkspaceType + workspaceID String + delayInMs Int @default(0) + isPublic Boolean @default(true) + isActive Boolean @default(true) + hitCount Int @default(0) + lastHitAt DateTime? @db.Timestamptz(3) + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) + deletedAt DateTime? @db.Timestamptz(3) + user User? @relation(fields: [creatorUid], references: [uid], onDelete: SetNull) requestLogs MockServerLog[] activityHistory MockServerActivity[] } model MockServerLog { - id String @id @default(cuid()) + id String @id @default(cuid()) mockServerID String requestMethod String requestPath String @@ -278,8 +290,9 @@ model MockServerLog { responseTime Int ipAddress String? userAgent String? - executedAt DateTime @default(now()) @db.Timestamptz(3) - mockServer MockServer @relation(fields: [mockServerID], references: [id], onDelete: Cascade) + executedAt DateTime @default(now()) @db.Timestamptz(3) + + mockServer MockServer @relation(fields: [mockServerID], references: [id], onDelete: Cascade) @@index([mockServerID]) @@index([mockServerID, executedAt]) @@ -291,11 +304,61 @@ model MockServerActivity { action MockServerAction performedBy String? performedAt DateTime @default(now()) @db.Timestamptz(3) - mockServer MockServer @relation(fields: [mockServerID], references: [id], onDelete: Cascade) + + mockServer MockServer @relation(fields: [mockServerID], references: [id], onDelete: Cascade) @@index([mockServerID]) } +// ============================================================ +// OTHERS +// ============================================================ + +model Shortcode { + id String @id @unique + request Json + creatorUid String? + embedProperties Json? + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @default(now()) @updatedAt @db.Timestamptz(3) + + user User? @relation(fields: [creatorUid], references: [uid]) + + @@unique([id, creatorUid], name: "creator_uid_shortcode_unique") +} + +model InfraConfig { + id String @id @default(cuid()) + name String @unique + value String? + isEncrypted Boolean @default(false) + lastSyncedEnvFileValue String? + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) +} + +model PersonalAccessToken { + id String @id @default(cuid()) + userUid String + label String + token String @unique @default(uuid()) + expiresOn DateTime? @db.Timestamptz(3) + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @updatedAt @db.Timestamptz(3) + + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) +} + +model InfraToken { + id String @id @default(cuid()) + creatorUid String + label String + token String @unique @default(uuid()) + expiresOn DateTime? @db.Timestamptz(3) + createdOn DateTime @default(now()) @db.Timestamptz(3) + updatedOn DateTime @default(now()) @db.Timestamptz(3) +} + model PublishedDocs { id String @id @default(cuid()) slug String @@ -317,26 +380,3 @@ model PublishedDocs { @@unique([slug, version]) @@index([collectionID]) } - -enum WorkspaceType { - USER - TEAM -} - -enum ReqType { - REST - GQL -} - -enum TeamAccessRole { - OWNER - VIEWER - EDITOR -} - -enum MockServerAction { - CREATED - DELETED - ACTIVATED - DEACTIVATED -} diff --git a/packages/hoppscotch-backend/src/errors.ts b/packages/hoppscotch-backend/src/errors.ts index fac3473880b..98c749f911c 100644 --- a/packages/hoppscotch-backend/src/errors.ts +++ b/packages/hoppscotch-backend/src/errors.ts @@ -189,6 +189,12 @@ export const TEAM_FB_COLL_PATH_RESOLVE_FAIL = 'team/fb_coll_path_resolve_fail'; */ export const TEAM_COLL_NOT_FOUND = 'team_coll/collection_not_found'; +/** + * The collection does not have the same parent as the expected parent + * (TeamCollectionService) + */ +export const TEAM_COLL_NOT_SAME_PARENT = 'team_coll/not_same_parent'; + /** * Could not find the team in the database * (TeamCollectionService) @@ -221,13 +227,6 @@ export const TEAM_COLL_DEST_SAME = export const TEAM_COL_ALREADY_ROOT = 'team_coll/target_collection_is_already_root_collection'; -/** - * Collections have different parents - * (TeamCollectionService) - */ -export const TEAM_COL_NOT_SAME_PARENT = - 'team_coll/team_collections_have_different_parents'; - /** * Collection and next Collection are the same * (TeamCollectionService) diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.model.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.model.ts index 7962c696528..48051eb7ce5 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.model.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.model.ts @@ -5,7 +5,7 @@ import { ServiceStatus } from './helper'; @ObjectType() export class InfraConfig { - @Field({ + @Field(() => InfraConfigEnum, { description: 'Infra Config Name', }) name: InfraConfigEnum; diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts index 8872bba984b..dcf43143161 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.controller.ts @@ -93,6 +93,9 @@ export class MockServerController { method, queryParams, requestHeaders, + // Parsed by the global express.json() middleware — needed for + // GraphQL operation-based matching ({query, operationName, variables}) + req.body, ); if (E.isLeft(result)) { diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts index 4f6ba1a7755..0e84b6190b8 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.service.spec.ts @@ -2070,4 +2070,446 @@ describe('MockServerService', () => { expect(result.queryParams).toEqual({}); }); }); + + describe('handleMockRequest — GraphQL', () => { + const gqlExample = { + key: 'gql1', + name: 'Get User Example', + endpoint: null, + method: null, + operationName: 'GetUser', + operationType: 'query', + statusCode: 200, + statusText: 'OK', + responseBody: '{"data":{"user":{"id":"1"}}}', + responseHeaders: [{ key: 'content-type', value: 'application/json' }], + headers: [], + }; + + const gqlUserRequest = { + id: 'gqlreq1', + collectionID: userCollection.id, + teamID: null, + title: 'Get User', + request: {}, + mockExamples: { + examples: [gqlExample], + }, + orderIndex: 1, + createdOn: currentTime, + updatedOn: currentTime, + } as any; + + const setupMocks = (requests: any[]) => { + mockPrisma.userCollection.findUnique.mockResolvedValue(userCollection); + mockPrisma.userCollection.findMany.mockResolvedValue([]); + mockPrisma.userRequest.findMany.mockResolvedValue(requests as any); + }; + + test('matches a GraphQL example by operation name and type', async () => { + setupMocks([gqlUserRequest]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(200); + expect((result.right as any).body).toBe('{"data":{"user":{"id":"1"}}}'); + } + }); + + test('exact operation-name match beats a wildcard example', async () => { + const wildcardExample = { + ...gqlExample, + key: 'gql-wild', + name: 'Any Query', + operationName: null, + responseBody: '{"data":{"wildcard":true}}', + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [wildcardExample, gqlExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"user":{"id":"1"}}}'); + } + }); + + test('wildcard example matches an unnamed operation of the same type', async () => { + const wildcardExample = { + ...gqlExample, + key: 'gql-wild', + name: 'Any Query', + operationName: null, + responseBody: '{"data":{"wildcard":true}}', + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [wildcardExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: '{ user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"wildcard":true}}'); + } + }); + + test('operation type mismatch returns Left', async () => { + setupMocks([gqlUserRequest]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'mutation GetUser { updateUser { id } }' }, + ); + + expect(E.isLeft(result)).toBe(true); + }); + + test('matches a large query', async () => { + setupMocks([gqlUserRequest]); + + const large = `query GetUser { user { id ${'alias: id '.repeat( + 5_000, + )} } }`; + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: large }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"user":{"id":"1"}}}'); + } + }); + + test('anonymous multi-operation document without operationName returns 400 errors body', async () => { + setupMocks([gqlUserRequest]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { + query: 'query GetUser { user { id } } query Other { other { id } }', + }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(400); + expect((result.right as any).body).toContain('operationName'); + } + }); + + test('multi-operation document with operationName selects the named operation', async () => { + setupMocks([gqlUserRequest]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { + query: 'query GetUser { user { id } } query Other { other { id } }', + operationName: 'GetUser', + }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(200); + } + }); + + test('unparseable query falls through to REST matching', async () => { + const restExample = { + key: 'rest1', + name: 'REST Example', + method: 'POST', + endpoint: 'http://api.example.com/search', + statusCode: 200, + statusText: 'OK', + responseBody: '{"rest":true}', + responseHeaders: [], + headers: [], + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [restExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/search', + 'POST', + {}, + {}, + { query: 'SELECT * FROM users' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"rest":true}'); + } + }); + + // Regression: operation stamping only exists in clients shipping the + // unified playground, so every example in an already-deployed database is + // unstamped. A parseable GraphQL body must not divert into operation + // matching when the mock owns no GraphQL examples — doing so 404s every + // pre-existing REST mock of a GraphQL-over-HTTP endpoint. + const unstampedGraphqlRestExample = { + key: 'rest-gql', + name: 'Legacy GraphQL Mock', + method: 'POST', + endpoint: 'http://api.example.com/graphql', + statusCode: 200, + statusText: 'OK', + responseBody: '{"data":{"legacy":true}}', + responseHeaders: [], + headers: [], + }; + + test('parseable GraphQL body falls through to REST when no example is operation-stamped', async () => { + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [unstampedGraphqlRestExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"legacy":true}}'); + } + }); + + test('x-mock-response-name still resolves when no example is operation-stamped', async () => { + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [unstampedGraphqlRestExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + { 'x-mock-response-name': 'Legacy GraphQL Mock' }, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"legacy":true}}'); + } + }); + + test('GraphQL operation with no matching stamped example falls back to REST scoring', async () => { + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { + // A stamped mutation example plus a plain REST example on the same + // path — an unmatched *query* must reach the REST example, not 404 + examples: [ + { ...gqlExample, operationType: 'mutation', operationName: 'Nope' }, + unstampedGraphqlRestExample, + ], + }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe('{"data":{"legacy":true}}'); + } + }); + + test('x-mock-response-name override picks the named GraphQL example', async () => { + const otherExample = { + ...gqlExample, + key: 'gql2', + name: 'Error Case', + statusCode: 500, + responseBody: '{"errors":[{"message":"boom"}]}', + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [gqlExample, otherExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + { 'x-mock-response-name': 'Error Case' }, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(500); + } + }); + + test('x-mock-response-name override does not cross operation types', async () => { + // A query example and a mutation example share the name 'Shared Name'. + // A mutation request naming it must not resolve to the query example. + const queryNamed = { + ...gqlExample, + key: 'gql-q', + name: 'Shared Name', + operationType: 'query', + responseBody: '{"data":{"fromQuery":true}}', + }; + const mutationNamed = { + ...gqlExample, + key: 'gql-m', + name: 'Shared Name', + operationName: 'UpdateUser', + operationType: 'mutation', + responseBody: '{"data":{"fromMutation":true}}', + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [queryNamed, mutationNamed] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + { 'x-mock-response-name': 'Shared Name' }, + { query: 'mutation UpdateUser { updateUser { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).body).toBe( + '{"data":{"fromMutation":true}}', + ); + } + }); + + test('treats an empty-string operationName as unspecified', async () => { + setupMocks([gqlUserRequest]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }', operationName: '' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(200); + expect((result.right as any).body).toBe('{"data":{"user":{"id":"1"}}}'); + } + }); + + test('prefers a 200 example among equal-score matches', async () => { + const error500 = { + ...gqlExample, + key: 'gql-a-500', + name: 'Failure', + statusCode: 500, + responseBody: '{"errors":[{"message":"boom"}]}', + }; + setupMocks([ + { + ...gqlUserRequest, + mockExamples: { examples: [error500, gqlExample] }, + }, + ]); + + const result = await mockServerService.handleMockRequest( + dbMockServer, + '/graphql', + 'POST', + {}, + {}, + { query: 'query GetUser { user { id } }' }, + ); + + expect(E.isRight(result)).toBe(true); + if (E.isRight(result)) { + expect((result.right as any).statusCode).toBe(200); + } + }); + }); }); diff --git a/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts b/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts index 6efbb8a4e15..7578cfd5699 100644 --- a/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts +++ b/packages/hoppscotch-backend/src/mock-server/mock-server.service.ts @@ -21,6 +21,7 @@ import { MOCK_SERVER_COLLECTION_CREATION_FAILED, } from 'src/errors'; import { randomBytes } from 'crypto'; +import { parse, getOperationAST } from 'graphql'; import { WorkspaceType } from 'src/types/WorkspaceTypes'; import { MockServerAction, @@ -37,6 +38,23 @@ import { ReqType } from 'src/types/RequestTypes'; import { AuthUser } from 'src/types/AuthUser'; import { mockServerCollRequestExample } from './constants/mock-server-coll-request-example'; +/** + * A saved example carrying GraphQL operation identity, projected by the + * `sync_mock_examples` trigger. Examples saved before operation stamping + * existed (i.e. every example in an already-deployed database) have no + * `operationType` and are not GraphQL examples. + */ +interface GqlExample { + id: string; + name: string; + operationName: string | null; + operationType: string; + statusCode: number; + statusText: string; + responseBody: string; + responseHeaders: Array<{ key: string; value: string }>; +} + @Injectable() export class MockServerService { constructor( @@ -714,6 +732,7 @@ export class MockServerService { method: string, queryParams?: Record, requestHeaders?: Record, + requestBody?: unknown, ): Promise> { try { // OPTIMIZATION: Fetch collection IDs once (recursive DB query) @@ -733,6 +752,56 @@ export class MockServerService { collectionIds, ); + // GraphQL branch: a body that parses as a GraphQL document routes to + // operation-based matching instead of path scoring. A body that merely + // LOOKS GraphQL-ish but fails to parse falls through to REST matching, + // so REST payloads that happen to carry a `query` field keep working. + // + // The branch is gated on this mock actually owning GraphQL-stamped + // examples. Operation stamping only exists in clients that ship the + // unified playground, so every example in an already-deployed database + // is unstamped — without the gate, a mock that has always served + // `POST /graphql` from a REST example would start returning 404 on + // upgrade, with no way back (the divert precedes the + // `x-mock-response-id`/`-name` override below). + // + // The example gate is evaluated FIRST, and deliberately so: collecting + // examples is an in-memory pass over rows already fetched above, whereas + // parsing allocates an AST several times the size of its input. This + // endpoint is public (throttled only), so a mock that could not use the + // result should never reach the parser. + const gqlExamples = this.collectGraphQLExamples(requests); + if (gqlExamples.length > 0) { + const gqlOperation = this.resolveGraphQLOperation(requestBody); + + if (gqlOperation) { + if (E.isLeft(gqlOperation)) { + // Genuinely GraphQL (parsed) but unresolvable operation — + // respond per GraphQL-over-HTTP instead of a REST-style 404 + return E.right({ + statusCode: 400, + body: JSON.stringify({ + errors: [{ message: gqlOperation.left }], + }), + headers: JSON.stringify({ 'content-type': 'application/json' }), + delay: mockServer.delayInMs || 0, + }); + } + + const gqlResult = this.handleGraphQLMockRequest( + gqlOperation.right, + gqlExamples, + requestHeaders, + mockServer.delayInMs, + ); + + // A mixed collection can hold GraphQL examples that don't cover this + // operation while still holding a REST example for the same path, so + // a miss falls through to REST scoring rather than terminating in 404. + if (E.isRight(gqlResult)) return gqlResult; + } + } + // OPTIMIZATION: Check for custom headers first (fastest path) // If user specified exact example, return it immediately without scoring if (requestHeaders) { @@ -1175,6 +1244,153 @@ export class MockServerService { /** * Format example response for return */ + /** + * Resolve the executed GraphQL operation from a request body. + * + * Returns `null` when the body is not a GraphQL payload (missing/non-string + * `query`, or a `query` that fails GraphQL parsing — the latter deliberately + * falls through to REST matching so REST payloads carrying a `query` field + * are never misclassified). Returns `Left` only for genuinely-GraphQL + * documents whose operation can't be resolved (anonymous multi-operation + * documents without an `operationName`). + */ + private resolveGraphQLOperation( + body: unknown, + ): null | E.Either { + if (!body || typeof body !== 'object' || Array.isArray(body)) return null; + + const query = (body as Record).query; + if (typeof query !== 'string' || query.trim().length === 0) return null; + + let doc; + try { + doc = parse(query); + } catch (_e) { + return null; + } + + const rawOpName = (body as Record).operationName; + const node = getOperationAST( + doc, + typeof rawOpName === 'string' && rawOpName.length > 0 + ? rawOpName + : undefined, + ); + if (!node) { + return E.left( + 'Must provide operationName if query contains multiple operations.', + ); + } + + return E.right({ name: node.name?.value ?? null, type: node.operation }); + } + + /** + * Operation-based matching for GraphQL mock requests — the GraphQL + * counterpart of the path/query scoring pipeline. GraphQL examples are + * discriminated by SHAPE (presence of `operationType`, stamped by the app + * and projected by the `sync_mock_examples` trigger). Team requests carry + * no REST/GQL discriminator of their own, so shape is the only signal: + * GraphQL requests live alongside REST ones in the same rows by design. + */ + private collectGraphQLExamples( + requests: Array<{ id: string; mockExamples: any }>, + ): GqlExample[] { + const examples: GqlExample[] = []; + for (const request of requests) { + const mockExamples = request.mockExamples as any; + if (!Array.isArray(mockExamples?.examples)) continue; + for (const exampleData of mockExamples.examples) { + if (typeof exampleData?.operationType !== 'string') continue; + examples.push({ + id: exampleData.key || `${request.id}-${exampleData.name}`, + name: exampleData.name || '', + operationName: exampleData.operationName || null, + operationType: exampleData.operationType, + statusCode: exampleData.statusCode || 200, + statusText: exampleData.statusText || 'OK', + responseBody: exampleData.responseBody || '', + responseHeaders: Array.isArray(exampleData.responseHeaders) + ? exampleData.responseHeaders + : [], + }); + } + } + return examples; + } + + private handleGraphQLMockRequest( + operation: { name: string | null; type: string }, + examples: GqlExample[], + requestHeaders: Record | undefined, + delayInMs: number, + ): E.Either { + if (examples.length === 0) { + return E.left( + `No GraphQL mock examples found for ${operation.type} ${operation.name ?? '(anonymous)'}`, + ); + } + + // x-mock-response-id / x-mock-response-name: exact-example override, + // bypassing scoring. The REST fast path gates its override on the HTTP + // method; over GraphQL the method carries no meaning, so the operation + // type is the equivalent discriminator — a mutation request must never + // resolve to a query example just because the names collide. + const overrideId = requestHeaders?.['x-mock-response-id']; + const overrideName = requestHeaders?.['x-mock-response-name']; + if (overrideId || overrideName) { + const exact = examples.find( + (ex) => + ex.operationType === operation.type && + ((overrideId && ex.id === overrideId) || + (overrideName && ex.name === overrideName)), + ); + if (exact) return this.formatExampleResponse(exact, delayInMs); + } + + // x-mock-response-code: status pre-filter with silent fallback (REST parity) + let filtered = examples; + const overrideCode = requestHeaders?.['x-mock-response-code']; + if (overrideCode) { + const statusCode = parseInt(overrideCode, 10); + const codeFiltered = examples.filter( + (ex) => ex.statusCode === statusCode, + ); + if (codeFiltered.length > 0) filtered = codeFiltered; + } + + // Scoring: operation type must match; exact operation-name match beats a + // wildcard example (one saved without an operation name); anything else + // is a non-match. Anonymous request operations match only wildcards. + const scored = filtered + .map((example) => { + if (example.operationType !== operation.type) + return { example, score: 0 }; + if (example.operationName === operation.name) + return { example, score: 100 }; + if (!example.operationName) return { example, score: 95 }; + return { example, score: 0 }; + }) + .filter((s) => s.score > 0) + .sort((a, b) => b.score - a.score); + + if (scored.length === 0) { + return E.left( + `No matching GraphQL mock example for ${operation.type} ${operation.name ?? '(anonymous)'}`, + ); + } + + // Deterministic selection among ties: prefer 200s (REST parity), then + // lexicographic example id — unlike REST, never DB row order + const highest = scored[0].score; + const top = scored + .filter((s) => s.score === highest) + .sort((a, b) => a.example.id.localeCompare(b.example.id)); + const selected = top.find((s) => s.example.statusCode === 200) ?? top[0]; + + return this.formatExampleResponse(selected.example, delayInMs); + } + private formatExampleResponse( example: any, delayInMs: number, diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.model.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.model.ts index 635312a8ec3..f8295c5531a 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.model.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.model.ts @@ -60,7 +60,7 @@ export class ShortcodeWithUserEmail { }) createdOn: Date; - @Field({ + @Field(() => ShortcodeCreator, { description: 'Details of user who created the shortcode', nullable: true, }) diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts index 672dfd86c68..b76dc9b0375 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts @@ -99,7 +99,7 @@ const shortcodesWithUserEmail = [ creatorUid: user.uid, createdOn: new Date(), updatedOn: createdOn, - User: user, + user: user, }, { id: 'blablabla1', @@ -112,7 +112,7 @@ const shortcodesWithUserEmail = [ creatorUid: user.uid, createdOn: new Date(), updatedOn: createdOn, - User: user, + user: user, }, ]; diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts index 70a59c5e902..f230f7d5102 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts @@ -298,7 +298,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit { const shortCodes = await this.prisma.shortcode.findMany({ where: userEmail ? { - User: { + user: { email: { equals: userEmail, mode: 'insensitive', @@ -313,7 +313,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit { take: args.take, cursor: args.cursor ? { id: args.cursor } : undefined, include: { - User: true, + user: true, }, }); @@ -327,10 +327,10 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit { ? JSON.stringify(code.embedProperties) : null, createdOn: code.createdOn, - creator: code.User + creator: code.user ? { - uid: code.User.uid, - email: code.User.email, + uid: code.user.uid, + email: code.user.email, } : null, }; diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.model.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.model.ts index d34a290f888..1c497523554 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.model.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.model.ts @@ -27,12 +27,12 @@ export class TeamCollection { @ObjectType() export class CollectionReorderData { - @Field({ + @Field(() => TeamCollection, { description: 'Team Collection being moved', }) collection: TeamCollection; - @Field({ + @Field(() => TeamCollection, { description: 'Team Collection succeeding the collection being moved in its new position', nullable: true, diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts index 25e89dcaa56..7a559d8b0ba 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts @@ -164,12 +164,12 @@ export class TeamCollectionResolver { type: () => ID, }) collectionID: string, - ) { + ): Promise { const teamCollections = await this.teamCollectionService.getCollection(collectionID); if (E.isLeft(teamCollections)) throwErr(teamCollections.left); - return { + return { id: teamCollections.right.id, title: teamCollections.right.title, parentID: teamCollections.right.parentID, diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts index 8488da37b7d..e109a4f875f 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts @@ -9,6 +9,7 @@ import { TEAM_COLL_INVALID_JSON, TEAM_COLL_IS_PARENT_COLL, TEAM_COLL_NOT_FOUND, + TEAM_COLL_NOT_SAME_PARENT, TEAM_COLL_NOT_SAME_TEAM, TEAM_COLL_SHORT_TITLE, TEAM_COL_ALREADY_ROOT, @@ -675,9 +676,7 @@ describe('createCollection', () => { }); test('should throw TEAM_NOT_OWNER when parent TeamCollection does not belong to the team', async () => { - jest - .spyOn(teamCollectionService as any, 'isOwnerCheck') - .mockResolvedValueOnce(O.none); + mockPrisma.teamCollection.findFirst.mockResolvedValueOnce(null); const result = await teamCollectionService.createCollection( rootTeamCollection.teamID, @@ -688,10 +687,10 @@ describe('createCollection', () => { expect(result).toEqualLeft(TEAM_NOT_OWNER); }); - test('should throw TEAM_COLL_DATA_INVALID when parent TeamCollection does not belong to the team', async () => { - jest - .spyOn(teamCollectionService as any, 'isOwnerCheck') - .mockResolvedValueOnce(O.some(true)); + test('should throw TEAM_COLL_DATA_INVALID when the data is invalid JSON', async () => { + mockPrisma.teamCollection.findFirst.mockResolvedValueOnce({ + ...rootTeamCollection, + }); const result = await teamCollectionService.createCollection( rootTeamCollection.teamID, @@ -720,9 +719,10 @@ describe('createCollection', () => { }); test('should successfully create a new child TeamCollection with valid inputs', async () => { - jest - .spyOn(teamCollectionService as any, 'isOwnerCheck') - .mockResolvedValueOnce(O.some(true)); + // parent ownership check + mockPrisma.teamCollection.findFirst.mockResolvedValueOnce({ + ...rootTeamCollection, + }); mockPrisma.$transaction.mockImplementationOnce(async (fn) => fn(mockPrisma), ); @@ -740,9 +740,10 @@ describe('createCollection', () => { }); test('should send pubsub message to "team_coll//coll_added" if child TeamCollection is created successfully', async () => { - jest - .spyOn(teamCollectionService as any, 'isOwnerCheck') - .mockResolvedValueOnce(O.some(true)); + // parent ownership check + mockPrisma.teamCollection.findFirst.mockResolvedValueOnce({ + ...rootTeamCollection, + }); mockPrisma.$transaction.mockImplementationOnce(async (fn) => fn(mockPrisma), ); @@ -1338,6 +1339,33 @@ describe('updateCollectionOrder', () => { expect(result).toEqualLeft(TEAM_COLL_NOT_SAME_TEAM); }); + test('should throw TEAM_COLL_NOT_SAME_PARENT if collection and nextCollection have different parents', async () => { + // getCollection; both collections belong to the same team but sit under + // different parents, so reordering between them is not a valid operation + mockPrisma.teamCollection.findUniqueOrThrow + .mockResolvedValueOnce(childTeamCollectionList[4]) + .mockResolvedValueOnce(childTeamCollection_2); + + const result = await teamCollectionService.updateCollectionOrder( + childTeamCollectionList[4].id, + childTeamCollection_2.id, + ); + expect(result).toEqualLeft(TEAM_COLL_NOT_SAME_PARENT); + }); + + test('should not reorder when collection and nextCollection have different parents', async () => { + mockPrisma.teamCollection.findUniqueOrThrow + .mockResolvedValueOnce(childTeamCollectionList[4]) + .mockResolvedValueOnce(childTeamCollection_2); + + await teamCollectionService.updateCollectionOrder( + childTeamCollectionList[4].id, + childTeamCollection_2.id, + ); + expect(mockPrisma.$transaction).not.toHaveBeenCalled(); + expect(mockPubSub.publish).not.toHaveBeenCalled(); + }); + test('should successfully update the order of the child TeamCollection list', async () => { // getCollection; mockPrisma.teamCollection.findUniqueOrThrow @@ -1451,6 +1479,21 @@ describe('importCollectionsFromJSON', () => { expect(result).toEqualLeft(TEAM_COLL_INVALID_JSON); }); + test('should throw TEAM_NOT_OWNER when the parent collection does not belong to the team', async () => { + // getCollection (parent lookup) + mockPrisma.teamCollection.findUniqueOrThrow.mockResolvedValueOnce({ + ...rootTeamCollection, + teamID: 'another-team-id', + }); + + const result = await teamCollectionService.importCollectionsFromJSON( + jsonString, + rootTeamCollection.teamID, + rootTeamCollection.id, + ); + expect(result).toEqualLeft(TEAM_NOT_OWNER); + }); + test('should successfully create new TeamCollections in root and TeamRequests with valid inputs', async () => { mockPrisma.$transaction.mockImplementation(async (fn) => fn(mockPrisma)); mockPrisma.teamCollection.findFirst.mockResolvedValueOnce(null); @@ -1465,6 +1508,10 @@ describe('importCollectionsFromJSON', () => { }); test('should successfully create new TeamCollections in a child collection and TeamRequests with valid inputs', async () => { + // getCollection (parent lookup) + mockPrisma.teamCollection.findUniqueOrThrow.mockResolvedValueOnce({ + ...rootTeamCollection, + }); mockPrisma.$transaction.mockImplementation(async (fn) => fn(mockPrisma)); mockPrisma.teamCollection.findFirst.mockResolvedValueOnce(null); mockPrisma.teamCollection.create.mockResolvedValueOnce(rootTeamCollection); diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts index 40a98e76dfa..95d915ae5cf 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts @@ -20,6 +20,7 @@ import { TEAM_COLL_PARENT_TREE_GEN_FAILED, TEAM_MEMBER_NOT_FOUND, TEAM_COLL_CREATION_FAILED, + TEAM_COLL_NOT_SAME_PARENT, } from '../errors'; import { PubSubService } from '../pubsub/pubsub.service'; import { @@ -210,6 +211,15 @@ export class TeamCollectionService { if (!Array.isArray(collectionsList.right)) return E.left(TEAM_COLL_INVALID_JSON); + // When importing into an existing parent, ensure the parent belongs to + // the team + if (parentID) { + const parentCollection = await this.getCollection(parentID); + if (E.isLeft(parentCollection)) return E.left(TEAM_COLL_NOT_FOUND); + if (parentCollection.right.teamID !== teamID) + return E.left(TEAM_NOT_OWNER); + } + let teamCollections: DBTeamCollection[] = []; let queryList: Prisma.TeamCollectionCreateInput[] = []; try { @@ -279,7 +289,7 @@ export class TeamCollectionService { private cast(teamCollection: DBTeamCollection): TeamCollection { const data = transformCollectionData(teamCollection.data); - return { + return { id: teamCollection.id, title: teamCollection.title, parentID: teamCollection.parentID, @@ -419,28 +429,6 @@ export class TeamCollectionService { } } - /** - * Check to see if Collection belongs to Team - * - * @param collectionID getChildCollectionsCount - * @param teamID The Team ID - * @returns An Option of a Boolean - */ - private async isOwnerCheck(collectionID: string, teamID: string) { - try { - await this.prisma.teamCollection.findFirstOrThrow({ - where: { - id: collectionID, - teamID, - }, - }); - - return O.some(true); - } catch (error) { - return O.none; - } - } - /** * Create a new TeamCollection * @@ -458,10 +446,13 @@ export class TeamCollectionService { const isTitleValid = isValidLength(title, this.TITLE_LENGTH); if (!isTitleValid) return E.left(TEAM_COLL_SHORT_TITLE); - // Check to see if parentTeamCollectionID belongs to this Team + // Check that the parent collection belongs to this Team if (parentID !== null) { - const isOwner = await this.isOwnerCheck(parentID, teamID); - if (O.isNone(isOwner)) return E.left(TEAM_NOT_OWNER); + const parentCollection = await this.prisma.teamCollection.findFirst({ + where: { id: parentID, teamID }, + select: { id: true }, + }); + if (!parentCollection) return E.left(TEAM_NOT_OWNER); } if (data === '') return E.left(TEAM_COLL_DATA_INVALID); @@ -976,6 +967,10 @@ export class TeamCollectionService { if (collection.right.teamID !== subsequentCollection.right.teamID) return E.left(TEAM_COLL_NOT_SAME_TEAM); + // Check if collection and subsequentCollection have the same parentID + if (collection.right.parentID !== subsequentCollection.right.parentID) + return E.left(TEAM_COLL_NOT_SAME_PARENT); + try { await this.prisma.$transaction(async (tx) => { try { diff --git a/packages/hoppscotch-backend/src/team-request/team-request.model.ts b/packages/hoppscotch-backend/src/team-request/team-request.model.ts index 79363b4b5fc..ce93ee3f09d 100644 --- a/packages/hoppscotch-backend/src/team-request/team-request.model.ts +++ b/packages/hoppscotch-backend/src/team-request/team-request.model.ts @@ -30,12 +30,12 @@ export class TeamRequest { @ObjectType() export class RequestReorderData { - @Field({ + @Field(() => TeamRequest, { description: 'Team Request being moved', }) request: TeamRequest; - @Field({ + @Field(() => TeamRequest, { description: 'Team Request succeeding the request being moved in its new position', nullable: true, 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 3814844d099..d6baef9331c 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 @@ -264,6 +264,7 @@ describe('createTeamRequest', () => { jest .spyOn(mockTeamCollectionService, 'getTeamOfCollection') .mockResolvedValue(E.right(team)); + mockPrisma.teamCollection.findUnique.mockResolvedValue(teamCollection); mockPrisma.$transaction.mockImplementation(async (fn) => { return fn(mockPrisma); }); @@ -271,13 +272,13 @@ describe('createTeamRequest', () => { mockPrisma.teamRequest.create.mockResolvedValue(dbRequest); const response = teamRequestService.createTeamRequest( - teamRequest.title, + teamCollection.id, team.id, teamRequest.title, teamRequest.request, ); - expect(response).resolves.toEqualRight(teamRequest); + await expect(response).resolves.toEqualRight(teamRequest); }); test('publishes creation to pubsub topic "team_req//req_created"', async () => { @@ -287,6 +288,7 @@ describe('createTeamRequest', () => { jest .spyOn(mockTeamCollectionService, 'getTeamOfCollection') .mockResolvedValue(E.right(team)); + mockPrisma.teamCollection.findUnique.mockResolvedValue(teamCollection); mockPrisma.$transaction.mockImplementation(async (fn) => { return fn(mockPrisma); }); @@ -294,7 +296,7 @@ describe('createTeamRequest', () => { mockPrisma.teamRequest.create.mockResolvedValue(dbRequest); await teamRequestService.createTeamRequest( - teamRequest.title, + teamCollection.id, team.id, teamRequest.title, teamRequest.request, @@ -527,6 +529,7 @@ describe('findRequestAndNextRequest', () => { mockPrisma.teamRequest.findFirst .mockResolvedValueOnce(dbTeamRequests[0]) .mockResolvedValueOnce(dbTeamRequests[4]); + mockPrisma.teamCollection.findUnique.mockResolvedValueOnce(teamCollection); const result = await (teamRequestService as any).findRequestAndNextRequest( args.srcCollID, @@ -607,6 +610,54 @@ describe('findRequestAndNextRequest', () => { expect(result).toEqualLeft(TEAM_REQ_INVALID_TARGET_COLL_ID); }); + test('Should resolve left if the destination collection does not exist when nextRequestID is given', async () => { + const args: MoveTeamRequestArgs = { + srcCollID: teamRequests[0].collectionID, + destCollID: 'non-existent-coll', + requestID: teamRequests[0].id, + nextRequestID: teamRequests[4].id, + }; + + mockPrisma.teamRequest.findFirst.mockResolvedValueOnce(dbTeamRequests[0]); + mockPrisma.teamCollection.findUnique.mockResolvedValueOnce(null); + + const result = await (teamRequestService as any).findRequestAndNextRequest( + args.srcCollID, + args.requestID, + args.destCollID, + args.nextRequestID, + ); + + expect(result).toEqualLeft(TEAM_INVALID_COLL_ID); + // The destination is validated before the nextRequest lookup, so only the + // request itself should have been fetched + expect(mockPrisma.teamRequest.findFirst).toHaveBeenCalledTimes(1); + }); + test('Should resolve left if the destination collection belongs to a different team when nextRequestID is given', async () => { + const args: MoveTeamRequestArgs = { + srcCollID: teamRequests[0].collectionID, + destCollID: 'cross-team-coll', + requestID: teamRequests[0].id, + nextRequestID: teamRequests[4].id, + }; + + mockPrisma.teamRequest.findFirst.mockResolvedValueOnce(dbTeamRequests[0]); + mockPrisma.teamCollection.findUnique.mockResolvedValueOnce({ + ...teamCollection, + id: 'cross-team-coll', + teamID: 'different-team-id', + }); + + const result = await (teamRequestService as any).findRequestAndNextRequest( + args.srcCollID, + args.requestID, + args.destCollID, + args.nextRequestID, + ); + + expect(result).toEqualLeft(TEAM_REQ_INVALID_TARGET_COLL_ID); + expect(mockPrisma.teamRequest.findFirst).toHaveBeenCalledTimes(1); + }); test('Should resolve left if the request is not found', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, @@ -637,6 +688,7 @@ describe('findRequestAndNextRequest', () => { mockPrisma.teamRequest.findFirst .mockResolvedValueOnce(dbTeamRequests[0]) .mockResolvedValueOnce(null); + mockPrisma.teamCollection.findUnique.mockResolvedValueOnce(teamCollection); const result = (teamRequestService as any).findRequestAndNextRequest( args.srcCollID, 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 0faf18bc56e..a43f58c0ccd 100644 --- a/packages/hoppscotch-backend/src/team-request/team-request.service.ts +++ b/packages/hoppscotch-backend/src/team-request/team-request.service.ts @@ -35,7 +35,7 @@ export class TeamRequestService { * A helper function to cast the Prisma TeamRequest model to the TeamRequest model * @param tr TeamRequest model from Prisma */ - private cast(tr: DbTeamRequest) { + private cast(tr: DbTeamRequest): TeamRequest { return { id: tr.id, collectionID: tr.collectionID, @@ -388,6 +388,17 @@ export class TeamRequestService { }); if (!request) return E.left(TEAM_REQ_NOT_FOUND); + // The destination collection must exist and belong to the same team as + // the request + const destCollection = await this.prisma.teamCollection.findUnique({ + where: { id: destCollID }, + select: { teamID: true }, + }); + if (!destCollection) return E.left(TEAM_INVALID_COLL_ID); + if (destCollection.teamID !== request.teamID) { + return E.left(TEAM_REQ_INVALID_TARGET_COLL_ID); + } + let nextRequest = null; if (nextRequestID) { nextRequest = await this.prisma.teamRequest.findFirst({ @@ -401,18 +412,6 @@ export class TeamRequestService { ) { return E.left(TEAM_REQ_INVALID_TARGET_COLL_ID); } - } else { - // When nextRequestID is null, validate that the destination collection - // belongs to the same team as the request to prevent cross-team moves - const destCollection = await this.prisma.teamCollection.findUnique({ - where: { id: destCollID }, - select: { teamID: true }, - }); - if (!destCollection) return E.left(TEAM_INVALID_COLL_ID); - - if (destCollection.teamID !== request.teamID) { - return E.left(TEAM_REQ_INVALID_TARGET_COLL_ID); - } } return E.right({ request, nextRequest }); diff --git a/packages/hoppscotch-backend/src/types/RequestTypes.ts b/packages/hoppscotch-backend/src/types/RequestTypes.ts index 717db6ac7be..2485251b54a 100644 --- a/packages/hoppscotch-backend/src/types/RequestTypes.ts +++ b/packages/hoppscotch-backend/src/types/RequestTypes.ts @@ -1,4 +1,10 @@ +import { registerEnumType } from '@nestjs/graphql'; + export enum ReqType { REST = 'REST', GQL = 'GQL', } + +registerEnumType(ReqType, { + name: 'ReqType', +}); diff --git a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts index c7947c97aba..1caf8942122 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts @@ -32,12 +32,12 @@ export class UserCollection { @ObjectType() export class UserCollectionReorderData { - @Field({ + @Field(() => UserCollection, { description: 'User Collection being moved', }) userCollection: UserCollection; - @Field({ + @Field(() => UserCollection, { description: 'User Collection succeeding the collection being moved in its new position', nullable: true, diff --git a/packages/hoppscotch-backend/src/user-history/user-history.model.ts b/packages/hoppscotch-backend/src/user-history/user-history.model.ts index c384391bcd3..71c420fe793 100644 --- a/packages/hoppscotch-backend/src/user-history/user-history.model.ts +++ b/packages/hoppscotch-backend/src/user-history/user-history.model.ts @@ -1,4 +1,4 @@ -import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { Field, ID, ObjectType } from '@nestjs/graphql'; import { ReqType } from 'src/types/RequestTypes'; @ObjectType() @@ -51,7 +51,3 @@ export class UserHistoryDeletedManyData { }) reqType: ReqType; } - -registerEnumType(ReqType, { - name: 'ReqType', -}); diff --git a/packages/hoppscotch-backend/src/user-request/user-request.model.ts b/packages/hoppscotch-backend/src/user-request/user-request.model.ts index eb2987fdcc8..5cf17ac1989 100644 --- a/packages/hoppscotch-backend/src/user-request/user-request.model.ts +++ b/packages/hoppscotch-backend/src/user-request/user-request.model.ts @@ -36,12 +36,12 @@ export class UserRequest { @ObjectType() export class UserRequestReorderData { - @Field({ + @Field(() => UserRequest, { description: 'User request being moved', }) request: UserRequest; - @Field({ + @Field(() => UserRequest, { description: 'User request succeeding the request being moved in its new position', nullable: true, diff --git a/packages/hoppscotch-cli/README.md b/packages/hoppscotch-cli/README.md index 8a36a42027b..f3e9e611702 100644 --- a/packages/hoppscotch-cli/README.md +++ b/packages/hoppscotch-cli/README.md @@ -35,6 +35,35 @@ hopp [options or commands] arguments - Outputs the response of each request. - Executes and outputs test-script response. + #### GraphQL support: + + Collections can freely mix REST and GraphQL requests — both run in + collection order through the same pipeline (environment templating, + the full auth surface, pre-request/test scripts, collection-level + script inheritance, metrics, and reports). + + - GraphQL requests execute as GraphQL-over-HTTP: a `POST` with a JSON + `{"query", "variables", "operationName"}` body. + - The **first operation** in the document runs; for multi-operation + documents its name is injected as `operationName` automatically. + - Environment variables resolve anywhere in the request — URL, headers, + the query (including a whole document stored in a variable), and + **inside `variables`**, including bare non-string positions like + `{ "count": <> }`. Invalid `variables` JSON fails the request with + an error instead of sending a broken body. + - Test scripts assert on the GraphQL response body as parsed JSON, e.g. + `pw.expect(pw.response.body.data.hello).toBe("world")`. + - **Subscriptions cannot run** in a single HTTP round-trip: they are + reported as request errors, the run continues, and the exit code is + non-zero (matching the app's collection runner). + + Runnable examples live in + [`src/__tests__/e2e/fixtures/`](./src/__tests__/e2e/fixtures/): + `collections/mixed-rest-gql-coll.json` (REST + GraphQL, run with + `--env environments/gql-envs.json`) and `collections/gql-coll.json` + (GraphQL only). They run in CI against the public echo server at + `https://echo.hoppscotch.io/graphql`. + #### Options: ##### `-e, --env ` diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 8bdb1faa3ca..3709acb58ab 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -46,6 +46,7 @@ "axios-cookiejar-support": "6.0.5", "chalk": "5.6.2", "commander": "14.0.3", + "graphql": "16.13.2", "isolated-vm": "6.1.2", "js-md5": "0.8.3", "jsonc-parser": "3.3.1", diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts index 48b80b6652b..4b7a7903615 100644 --- a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts @@ -1514,4 +1514,33 @@ describe("hopp test [options] ", { timeout: 100000 }, () => { expect(result.error).toBeNull(); }); }); + + describe("Test `hopp test ` command with GraphQL requests:", () => { + test("Successfully runs an all-GraphQL collection, including pre-request scripts and header templating", async () => { + const args = `test ${getTestJsonFilePath("gql-coll.json", "collection")}`; + const result = await runCLIWithNetworkRetry(args); + if (result === null) return; + + // Assert content so a regression back to skip-GraphQL can't pass + expect(result.stdout).toContain("echoes the POST method"); + expect(result.stdout).toContain("scripted header echoed"); + expect(result.error).toBeNull(); + }); + + test("Successfully runs a collection mixing REST and GraphQL requests, resolving environment variables from the supplied env file", async () => { + const COLL_PATH = getTestJsonFilePath( + "mixed-rest-gql-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath("gql-envs.json", "environment"); + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; + const result = await runCLIWithNetworkRetry(args); + if (result === null) return; + + expect(result.stdout).toContain("REST responds 200"); + expect(result.stdout).toContain("env templated header echoed"); + expect(result.stdout).toContain("anonymous query in nested folder"); + expect(result.error).toBeNull(); + }); + }); }); diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/gql-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/gql-coll.json new file mode 100644 index 00000000000..d78ab63037a --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/gql-coll.json @@ -0,0 +1,50 @@ +[ + { + "v": 1, + "name": "GraphQL Only", + "folders": [], + "requests": [ + { + "v": 10, + "name": "Named query", + "url": "https://echo.hoppscotch.io/graphql", + "headers": [], + "query": "query RequestInfo { method url }", + "variables": "{}", + "auth": { "authType": "none", "authActive": true }, + "description": null, + "responses": {}, + "preRequestScript": "", + "testScript": "pw.test(\"echoes the POST method\", () => { pw.expect(pw.response.body.data.method).toBe(\"POST\") })" + }, + { + "v": 10, + "name": "Scripted auth header", + "url": "https://echo.hoppscotch.io/graphql", + "headers": [ + { "key": "Authorization", "value": "Bearer <>", "active": true, "description": "" } + ], + "query": "query AuthEcho { headers { key value } }", + "variables": "{}", + "auth": { "authType": "none", "authActive": true }, + "description": null, + "responses": {}, + "preRequestScript": "pw.env.set(\"myToken\", \"secret123\")", + "testScript": "pw.test(\"scripted header echoed\", () => { const auth = pw.response.body.data.headers.find((h) => h.key === \"authorization\"); pw.expect(auth.value).toBe(\"Bearer secret123\") })" + }, + { + "v": 10, + "name": "Anonymous query", + "url": "https://echo.hoppscotch.io/graphql", + "headers": [], + "query": "{ url }", + "variables": "{}", + "auth": { "authType": "none", "authActive": true }, + "description": null, + "responses": {}, + "preRequestScript": "", + "testScript": "" + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/mixed-rest-gql-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/mixed-rest-gql-coll.json new file mode 100644 index 00000000000..3e10644f6e3 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/mixed-rest-gql-coll.json @@ -0,0 +1,57 @@ +[ + { + "v": 1, + "name": "Mixed REST GQL Suite", + "folders": [ + { + "v": 1, + "name": "Inherited", + "folders": [], + "requests": [ + { + "v": 10, + "name": "GQL nested in folder", + "url": "https://echo.hoppscotch.io/graphql", + "headers": [], + "query": "{ method }", + "variables": "{}", + "auth": { "authType": "none", "authActive": true }, + "description": null, + "responses": {}, + "preRequestScript": "", + "testScript": "pw.test(\"anonymous query in nested folder\", () => { pw.expect(pw.response.body.data.method).toBe(\"POST\") })" + } + ] + } + ], + "requests": [ + { + "v": "1", + "name": "REST echo", + "method": "GET", + "endpoint": "https://echo.hoppscotch.io", + "params": [], + "headers": [], + "preRequestScript": "", + "testScript": "pw.test(\"REST responds 200\", () => { pw.expect(pw.response.status).toBe(200); pw.expect(pw.response.body.method).toBe(\"GET\") })", + "auth": { "authType": "none", "authActive": true }, + "body": { "contentType": null, "body": null } + }, + { + "v": 10, + "name": "GQL env templated header", + "url": "https://echo.hoppscotch.io/graphql", + "headers": [ + { "key": "x-hopp-greeting", "value": "<>", "active": true, "description": "" } + ], + "query": "query GreetingEcho { headers { key value } }", + "variables": "{}", + "auth": { "authType": "none", "authActive": true }, + "description": null, + "responses": {}, + "preRequestScript": "", + "testScript": "pw.test(\"env templated header echoed\", () => { const greeting = pw.response.body.data.headers.find((h) => h.key === \"x-hopp-greeting\"); pw.expect(greeting.value).toBe(\"hello-world\") })" + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/gql-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/gql-envs.json new file mode 100644 index 00000000000..d9fbaef32a9 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/gql-envs.json @@ -0,0 +1,13 @@ +{ + "id": "gql-envs", + "v": 2, + "name": "gql-envs", + "variables": [ + { + "key": "greeting", + "initialValue": "hello-world", + "currentValue": "hello-world", + "secret": false + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/unit/collection-fixtures.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/collection-fixtures.spec.ts new file mode 100644 index 00000000000..6b356f1439b --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/collection-fixtures.spec.ts @@ -0,0 +1,112 @@ +import { describe, expect, test } from "vitest"; +import { Environment, isGQLRequest } from "@hoppscotch/data"; +import { mkdtempSync, readFileSync, writeFileSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; + +import { parseCollectionData } from "../../utils/mutators"; +import { getTestJsonFilePath } from "../utils"; + +describe("collection ingestion — mixed and GraphQL fixtures", () => { + test("mixed-rest-gql-coll.json parses with REST and GraphQL requests in order", async () => { + const collections = await parseCollectionData( + getTestJsonFilePath("mixed-rest-gql-coll.json", "collection"), + {} + ); + + expect(collections).toHaveLength(1); + const [collection] = collections; + + expect(collection.requests.map((r) => isGQLRequest(r))).toEqual([ + false, + true, + ]); + + expect(collection.folders).toHaveLength(1); + expect(collection.folders[0].requests.map((r) => isGQLRequest(r))).toEqual([ + true, + ]); + + const gqlRequest = collection.requests[1]; + if (isGQLRequest(gqlRequest)) { + expect(gqlRequest.query).toContain("query GreetingEcho"); + expect(gqlRequest.headers[0].value).toContain("<>"); + } + }); + + test("gql-coll.json parses as an all-GraphQL collection", async () => { + const collections = await parseCollectionData( + getTestJsonFilePath("gql-coll.json", "collection"), + {} + ); + + expect(collections).toHaveLength(1); + const requests = collections[0].requests; + expect(requests).toHaveLength(3); + expect(requests.every((r) => isGQLRequest(r))).toBe(true); + + const scripted = requests[1]; + if (isGQLRequest(scripted)) { + expect(scripted.query).toContain("query AuthEcho"); + expect(scripted.preRequestScript).toContain("pw.env.set"); + } + }); + + + test("old-version GQL requests migrate to the latest schema on ingestion", async () => { + // v9 predates responses and the script fields + const legacyCollection = [ + { + v: 1, + name: "Legacy", + folders: [], + requests: [ + { + v: 9, + name: "Legacy GQL", + url: "https://echo.hoppscotch.io/graphql", + headers: [], + query: "query Hello { method }", + variables: "{}", + auth: { authType: "none", authActive: true }, + }, + ], + }, + ]; + const file = join( + mkdtempSync(join(tmpdir(), "hopp-gql-migration-")), + "legacy-coll.json" + ); + writeFileSync(file, JSON.stringify(legacyCollection)); + + const collections = await parseCollectionData(file, {}); + const request = collections[0].requests[0]; + expect(isGQLRequest(request)).toBe(true); + if (isGQLRequest(request)) { + expect(request.v).toBe(10); + expect(request.preRequestScript).toBe(""); + expect(request.testScript).toBe(""); + expect(request.responses).toEqual({}); + } + }); + + test("gql-envs.json is a latest-version environment export", () => { + const contents = JSON.parse( + readFileSync(getTestJsonFilePath("gql-envs.json", "environment"), "utf8") + ); + + const parsed = Environment.safeParse(contents); + expect(parsed.type).toBe("ok"); + if (parsed.type === "ok") { + expect(parsed.value.v).toBe(2); + expect(parsed.value.variables).toEqual([ + { + key: "greeting", + initialValue: "hello-world", + currentValue: "hello-world", + secret: false, + }, + ]); + } + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/unit/gql-auth-body.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/gql-auth-body.spec.ts new file mode 100644 index 00000000000..e5614ac8c01 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/gql-auth-body.spec.ts @@ -0,0 +1,86 @@ +import { describe, expect, test, vi } from "vitest"; +import { Environment, HoppGQLRequest } from "@hoppscotch/data"; +import * as E from "fp-ts/Either"; + +import { preProcessGQLRequest } from "../../utils/gql-request"; + +// Capture what the signer is constructed with — the signature has to be +// computed over the body actually sent, not the stub placeholder +const signerCalls = vi.hoisted(() => [] as Record[]); + +vi.mock("aws4fetch", () => ({ + AwsV4Signer: class { + constructor(opts: Record) { + signerCalls.push(opts); + } + async sign() { + return { headers: new Map(), url: new URL("https://example.com") }; + } + }, +})); + +const { getEffectiveRESTRequest } = await import("../../utils/pre-request"); + +const collection = { + v: 12, + name: "Base", + folders: [], + requests: [], + headers: [], + auth: { authType: "none", authActive: false }, + variables: [], + preRequestScript: "", + testScript: "", +} as any; + +const gqlRequest = { + v: 10, + name: "Signed GQL", + url: "https://example.com/graphql", + headers: [], + query: "query Hello { hello }", + variables: '{ "id": "1" }', + auth: { + authType: "aws-signature", + authActive: true, + accessKey: "AKIAEXAMPLE", + secretKey: "secret", + region: "us-east-1", + serviceName: "execute-api", + serviceToken: "", + addTo: "HEADERS", + }, + description: null, + responses: {}, + preRequestScript: "", + testScript: "", +} as unknown as HoppGQLRequest; + +const emptyEnv: Environment = { + v: 2, + id: "env", + name: "env", + variables: [], +}; + +describe("GraphQL requests with signing auth", () => { + test("aws-signature signs the assembled GraphQL payload, not the empty stub body", async () => { + signerCalls.length = 0; + + const stub = preProcessGQLRequest(gqlRequest, collection); + // The stub carries a placeholder body until effective-request time + expect(stub.body.body).toBe(""); + + const result = await getEffectiveRESTRequest(stub, emptyEnv); + expect(E.isRight(result)).toBe(true); + + expect(signerCalls).toHaveLength(1); + const signedBody = signerCalls[0].body as string; + + expect(JSON.parse(signedBody)).toEqual({ + query: "query Hello { hello }", + variables: { id: "1" }, + operationName: "Hello", + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/unit/gql-request.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/gql-request.spec.ts new file mode 100644 index 00000000000..f1ee14b0c6f --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/gql-request.spec.ts @@ -0,0 +1,325 @@ +import { describe, expect, test } from "vitest"; +import { + EnvironmentVariable, + HoppCollection, + HoppGQLRequest, +} from "@hoppscotch/data"; +import * as E from "fp-ts/Either"; + +import { + GQLStubRequest, + buildEffectiveGQLPayload, + isGQLStubRequest, + preProcessGQLRequest, +} from "../../utils/gql-request"; + +const baseCollection = { + v: 12, + name: "Base", + folders: [], + requests: [], + headers: [], + auth: { authType: "none", authActive: false }, + variables: [], + preRequestScript: "", + testScript: "", +} as unknown as HoppCollection; + +const baseRequest: HoppGQLRequest = { + v: 10, + name: "Get Hello", + url: "https://echo.hoppscotch.io/graphql", + headers: [], + query: "query HelloOp { hello }", + variables: '{ "id": "1" }', + auth: { authType: "none", authActive: true }, + description: null, + responses: {}, + preRequestScript: 'pw.env.set("a", "b")', + testScript: 'pw.test("t", () => {})', +}; + +const envs = (vars: Record): EnvironmentVariable[] => + Object.entries(vars).map(([key, value]) => ({ + key, + initialValue: value, + currentValue: value, + secret: false, + })); + +const stubFor = ( + request: Partial, + collection: HoppCollection = baseCollection +): GQLStubRequest => + preProcessGQLRequest({ ...baseRequest, ...request } as HoppGQLRequest, collection); + +const payloadFor = ( + request: Partial, + variables: EnvironmentVariable[] = [], + collection: HoppCollection = baseCollection +) => { + const result = buildEffectiveGQLPayload( + stubFor(request, collection), + variables + ); + expect(E.isRight(result)).toBe(true); + return JSON.parse((result as E.Right).right); +}; + +describe("preProcessGQLRequest", () => { + test("builds a REST-shaped POST stub carrying scripts and the raw query/variables", () => { + const stub = stubFor({}); + + expect(stub.method).toBe("POST"); + expect(stub.endpoint).toBe("https://echo.hoppscotch.io/graphql"); + expect(stub.body.contentType).toBe("application/json"); + expect(stub.preRequestScript).toBe('pw.env.set("a", "b")'); + expect(stub.testScript).toBe('pw.test("t", () => {})'); + expect(stub.gqlRaw).toEqual({ + query: "query HelloOp { hello }", + variables: '{ "id": "1" }', + }); + expect(isGQLStubRequest(stub)).toBe(true); + }); + + test("falls back to 'Untitled Request' for unnamed requests", () => { + expect(stubFor({ name: "" }).name).toBe("Untitled Request"); + }); + + test("an inactive request header does not suppress a same-key active parent header", () => { + const collection = { + ...baseCollection, + headers: [ + { key: "x-parent", value: "parent-value", active: true, description: "" }, + ], + } as HoppCollection; + + const stub = stubFor( + { + headers: [ + { key: "x-parent", value: "child-value", active: false, description: "" }, + ], + }, + collection + ); + + expect(stub.headers).toEqual([ + { key: "x-parent", value: "parent-value", active: true, description: "" }, + ]); + }); + + test("an active request header wins over a same-key parent header; inactive parents are dropped", () => { + const collection = { + ...baseCollection, + headers: [ + { key: "x-shared", value: "parent-value", active: true, description: "" }, + { key: "x-off", value: "off", active: false, description: "" }, + ], + } as HoppCollection; + + const stub = stubFor( + { + headers: [ + { key: "x-shared", value: "child-value", active: true, description: "" }, + ], + }, + collection + ); + + expect(stub.headers).toEqual([ + { key: "x-shared", value: "child-value", active: true, description: "" }, + ]); + }); + + test("auth inherit + active resolves to the parent auth", () => { + const collection = { + ...baseCollection, + auth: { authType: "bearer", token: "parent-token", authActive: true }, + } as HoppCollection; + + const stub = stubFor( + { auth: { authType: "inherit", authActive: true } }, + collection + ); + + expect(stub.auth).toEqual({ + authType: "bearer", + token: "parent-token", + authActive: true, + }); + }); + + test("auth inherit + inactive resolves to no auth even when the parent has auth", () => { + const collection = { + ...baseCollection, + auth: { authType: "bearer", token: "parent-token", authActive: true }, + } as HoppCollection; + + const stub = stubFor( + { auth: { authType: "inherit", authActive: false } }, + collection + ); + + expect(stub.auth).toEqual({ authType: "none", authActive: false }); + }); + + test("explicit request-level auth is carried onto the stub", () => { + const stub = stubFor({ + auth: { authType: "bearer", token: "req-token", authActive: true }, + }); + + expect(stub.auth).toEqual({ + authType: "bearer", + token: "req-token", + authActive: true, + }); + }); +}); + +describe("buildEffectiveGQLPayload", () => { + test("assembles query, parsed variables, and operationName for a named operation", () => { + const payload = payloadFor({}); + + expect(payload).toEqual({ + query: "query HelloOp { hello }", + variables: { id: "1" }, + operationName: "HelloOp", + }); + }); + + test("omits operationName for anonymous operations", () => { + const payload = payloadFor({ query: "{ hello }" }); + + expect(payload.operationName).toBeUndefined(); + }); + + test("selects the first operation of a multi-operation document", () => { + const payload = payloadFor({ + query: "query First { a }\nquery Second { b }", + }); + + expect(payload.operationName).toBe("First"); + }); + + test("an anonymous FIRST operation in a multi-op document sends no operationName (server reports the spec error)", () => { + const payload = payloadFor({ + query: "{ a }\nquery Second { b }", + }); + + expect(payload.operationName).toBeUndefined(); + }); + + test("resolves env templates in bare non-string variable positions", () => { + const payload = payloadFor( + { variables: '{ "count": <> }' }, + envs({ n: "5" }) + ); + + expect(payload.variables).toEqual({ count: 5 }); + }); + + test("a multi-line env value as the whole query document survives intact", () => { + const doc = "query FromEnv {\n method\n url\n}"; + const payload = payloadFor( + { query: "<>", variables: "" }, + envs({ doc }) + ); + + expect(payload.query).toBe(doc); + expect(payload.operationName).toBe("FromEnv"); + }); + + test("safe env values substitute into variables string positions", () => { + const payload = payloadFor( + { variables: '{ "m": "<> from CLI" }' }, + envs({ v: "hello-world" }) + ); + + expect(payload.variables.m).toBe("hello-world from CLI"); + }); + + test("env values that break the variables JSON fail loudly instead of sending a mangled body", () => { + // Substitution into `variables` is textual, so a raw quote in the value + // breaks the JSON — the app fails this identically + const result = buildEffectiveGQLPayload( + stubFor({ variables: '{ "m": "<>" }' }), + envs({ v: 'has a "quote"' }) + ); + + expect(E.isLeft(result)).toBe(true); + if (E.isLeft(result)) { + expect(String(result.left.data)).toContain("Invalid JSON"); + } + }); + + test("a literal subscription fails with REQUEST_ERROR before any network call", () => { + const result = buildEffectiveGQLPayload( + stubFor({ query: "subscription S { countdown }" }), + [] + ); + + expect(E.isLeft(result)).toBe(true); + if (E.isLeft(result)) { + expect(result.left.code).toBe("REQUEST_ERROR"); + expect(String(result.left.data)).toContain( + "GraphQL subscriptions are not supported" + ); + } + }); + + test("a templated document resolving to a subscription is detected after substitution", () => { + const result = buildEffectiveGQLPayload( + stubFor({ query: "<>" }), + envs({ subdoc: "subscription S { countdown }" }) + ); + + expect(E.isLeft(result)).toBe(true); + if (E.isLeft(result)) { + expect(result.left.code).toBe("REQUEST_ERROR"); + } + }); + + test("invalid variables JSON fails with REQUEST_ERROR instead of sending a mangled body", () => { + const result = buildEffectiveGQLPayload( + stubFor({ variables: '{ "m": broken' }), + [] + ); + + expect(E.isLeft(result)).toBe(true); + if (E.isLeft(result)) { + expect(result.left.code).toBe("REQUEST_ERROR"); + expect(String(result.left.data)).toContain("Invalid JSON"); + } + }); + + test("empty variables text omits the variables key entirely", () => { + const payload = payloadFor({ variables: "" }); + + expect("variables" in payload).toBe(false); + }); + + test("whitespace-only variables fail as invalid JSON (app kernel parity)", () => { + const result = buildEffectiveGQLPayload(stubFor({ variables: " " }), []); + + expect(E.isLeft(result)).toBe(true); + if (E.isLeft(result)) { + expect(String(result.left.data)).toContain("Invalid JSON"); + } + }); + + test("empty and fragment-only documents are sent as-is without operationName", () => { + expect(payloadFor({ query: "", variables: "" })).toEqual({ query: "" }); + + const fragmentOnly = "fragment F on Query { method }"; + const payload = payloadFor({ query: fragmentOnly }); + expect(payload.query).toBe(fragmentOnly); + expect(payload.operationName).toBeUndefined(); + }); + + test("an unparseable document is sent as-is for the server to report", () => { + const payload = payloadFor({ query: "query { unbalanced" }); + + expect(payload.query).toBe("query { unbalanced"); + expect(payload.operationName).toBeUndefined(); + }); +}); diff --git a/packages/hoppscotch-cli/src/utils/collections.ts b/packages/hoppscotch-cli/src/utils/collections.ts index 276d1542c65..66fc4fb5c05 100644 --- a/packages/hoppscotch-cli/src/utils/collections.ts +++ b/packages/hoppscotch-cli/src/utils/collections.ts @@ -1,4 +1,8 @@ -import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; +import { + HoppCollection, + HoppRESTRequest, + isGQLRequest, +} from "@hoppscotch/data"; import chalk from "chalk"; import { log } from "console"; import * as A from "fp-ts/Array"; @@ -27,6 +31,7 @@ import { } from "./display"; import { exceptionColors } from "./getters"; import { getPreRequestMetrics } from "./pre-request"; +import { preProcessGQLRequest } from "./gql-request"; import { buildJUnitReport, generateJUnitReportExport } from "./reporters/junit"; import { getRequestMetrics, @@ -125,9 +130,12 @@ const processCollection = async ( collection.testScript, ]); - // Process each request in the collection + // GraphQL requests become REST-shaped stubs so the shared pipeline runs + // them unchanged; unrunnable stubs fail at effective-request time for (const request of collection.requests) { - const _request = preProcessRequest(request as HoppRESTRequest, collection); + const _request = isGQLRequest(request) + ? preProcessGQLRequest(request, collection) + : preProcessRequest(request as HoppRESTRequest, collection); const requestPath = `${path}/${_request.name}`; const collectionVariables = collection.variables.filter( diff --git a/packages/hoppscotch-cli/src/utils/gql-request.ts b/packages/hoppscotch-cli/src/utils/gql-request.ts new file mode 100644 index 00000000000..448e97cb621 --- /dev/null +++ b/packages/hoppscotch-cli/src/utils/gql-request.ts @@ -0,0 +1,150 @@ +import { + EnvironmentVariable, + HoppCollection, + HoppGQLRequest, + HoppRESTAuth, + HoppRESTRequest, + getDefaultRESTRequest, + parseTemplateString, +} from "@hoppscotch/data"; +import * as E from "fp-ts/Either"; +import { parse } from "graphql"; +import type { OperationDefinitionNode } from "graphql"; + +import { HoppCLIError, error } from "../types/errors"; + +/** + * A GraphQL request converted to a REST-shaped POST so the CLI's existing + * pipeline (templating, auth, scripts, metrics, reporting) runs it unchanged. + * + * `gqlRaw` carries the untemplated query/variables; the wire payload is + * assembled by {@link buildEffectiveGQLPayload} AFTER env templating — + * assembling earlier would JSON-escape the text before substitution and + * corrupt the payload on env values containing quotes or newlines. + */ +export interface GQLStubRequest extends HoppRESTRequest { + gqlRaw: { query: string; variables: string }; +} + +export const isGQLStubRequest = ( + request: HoppRESTRequest +): request is GQLStubRequest => "gqlRaw" in request; + +/** + * Converts a GraphQL request into a {@link GQLStubRequest}. Header/auth + * merging matches the app runner: inactive headers are dropped BEFORE the + * merge (so they can't suppress an active parent header), and `inherit` + * auth falls back to the parent only when active. + */ +export const preProcessGQLRequest = ( + request: HoppGQLRequest, + collection: HoppCollection +): GQLStubRequest => { + const requestHeaders = (request.headers ?? []).filter( + (header) => header.active && header.key !== "" + ); + const parentHeaders = (collection.headers ?? []).filter( + (header) => header.active && header.key !== "" + ); + const headers = [ + ...parentHeaders.filter( + (parentHeader) => + !requestHeaders.some((header) => header.key === parentHeader.key) + ), + ...requestHeaders, + ]; + + // GQL auth is a structural subset of REST auth; inherited collection auth + // can be a REST-only type (digest, HAWK, JWT…) + const requestAuth = (request.auth ?? { + authType: "none", + authActive: false, + }) as HoppRESTAuth; + const auth: HoppRESTAuth = + requestAuth.authType === "inherit" + ? requestAuth.authActive && collection.auth + ? (collection.auth as HoppRESTAuth) + : { authType: "none", authActive: false } + : requestAuth; + + return { + ...getDefaultRESTRequest(), + name: request.name || "Untitled Request", + method: "POST", + endpoint: request.url ?? "", + params: [], + headers, + auth, + preRequestScript: request.preRequestScript ?? "", + testScript: request.testScript ?? "", + // Placeholder — replaced from `gqlRaw` at effective-request time + body: { contentType: "application/json", body: "" }, + requestVariables: [], + gqlRaw: { + query: request.query ?? "", + variables: request.variables ?? "", + }, + }; +}; + +/** + * Assembles the GraphQL-over-HTTP JSON payload with env templating applied, + * mirroring the app's collection runner: the FIRST operation of the + * ENV-RESOLVED document runs (templated documents only reveal their + * operations after substitution), subscriptions and invalid variables JSON + * fail the row before any network call, and unparseable documents are sent + * as-is for the server to report. + */ +export const buildEffectiveGQLPayload = ( + request: GQLStubRequest, + envVariables: EnvironmentVariable[] +): E.Either => { + const query = parseTemplateString(request.gqlRaw.query, envVariables); + // No trim — the app kernel JSON.parses non-empty text as-is, so + // whitespace-only variables fail identically in both runners + const variablesText = parseTemplateString( + request.gqlRaw.variables, + envVariables + ); + + let operation: OperationDefinitionNode | null = null; + try { + const operations = parse(query).definitions.filter( + (definition): definition is OperationDefinitionNode => + definition.kind === "OperationDefinition" + ); + operation = operations[0] ?? null; + } catch (_e) { + // Unparseable document — the server's GraphQL error beats a client-side + // parse message + } + + if (operation?.operation === "subscription") { + return E.left( + error({ + code: "REQUEST_ERROR", + data: `GraphQL subscriptions are not supported in the CLI runner: ${request.name}`, + }) + ); + } + + let variables: unknown = undefined; + if (variablesText) { + try { + variables = JSON.parse(variablesText); + } catch (_e) { + return E.left( + error({ + code: "REQUEST_ERROR", + data: `Invalid JSON in GraphQL variables: ${request.name}`, + }) + ); + } + } + + const payload: Record = { query }; + if (variables !== undefined) payload.variables = variables; + if (operation?.name?.value) payload.operationName = operation.name.value; + + return E.right(JSON.stringify(payload)); +}; diff --git a/packages/hoppscotch-cli/src/utils/mutators.ts b/packages/hoppscotch-cli/src/utils/mutators.ts index 153e66b24a9..f997ffe9b7c 100644 --- a/packages/hoppscotch-cli/src/utils/mutators.ts +++ b/packages/hoppscotch-cli/src/utils/mutators.ts @@ -1,4 +1,9 @@ -import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; +import { + Environment, + HoppCollection, + HoppGQLRequest, + HoppRESTRequest, +} from "@hoppscotch/data"; import fs from "fs/promises"; import { entityReference } from "verzod"; import { z } from "zod"; @@ -14,9 +19,17 @@ const getValidRequests = ( collectionFilePath: string ) => { return collections.map((collection) => { - // Validate requests using zod schema + // Unified collections can mix REST and GraphQL requests — validate each + // entry against its own schema (REST first; a GraphQL request lacks + // `endpoint`, so it can never mis-validate as REST). Order is preserved: + // runs execute requests in collection order regardless of protocol. const requestSchemaParsedResult = z - .array(entityReference(HoppRESTRequest)) + .array( + z.union([ + entityReference(HoppRESTRequest), + entityReference(HoppGQLRequest), + ]) + ) .safeParse(collection.requests); // Handle validation errors diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index 83979862ebb..5ed419ee611 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -34,6 +34,7 @@ import { import { isHoppCLIError } from "./checks"; import { arrayFlatMap, arraySort, tupleToRecord } from "./functions/array"; import { getEffectiveFinalMetaData, getResolvedVariables } from "./getters"; +import { buildEffectiveGQLPayload, isGQLStubRequest } from "./gql-request"; import { stripComments } from "./jsonc"; import { toFormData } from "./mutators"; import { combineScriptsWithIIFE, filterValidScripts } from "@hoppscotch/js-sandbox/scripting"; @@ -179,14 +180,15 @@ export async function getEffectiveRESTRequest( } const effectiveFinalParams = _effectiveFinalParams.right; - // Parsing final-body with applied ENVs. - const _effectiveFinalBody = getFinalBodyFromRequest( - request, - resolvedVariables - ); + // Parsing final-body with applied ENVs. GraphQL stubs assemble their + // payload from the raw query/variables AFTER templating — see gql-request.ts + const _effectiveFinalBody = isGQLStubRequest(request) + ? buildEffectiveGQLPayload(request, resolvedVariables) + : getFinalBodyFromRequest(request, resolvedVariables); if (E.isLeft(_effectiveFinalBody)) { return _effectiveFinalBody; } + const effectiveFinalBody = _effectiveFinalBody.right; // Authentication if (request.auth.authActive) { @@ -259,11 +261,10 @@ export async function getEffectiveRESTRequest( const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, ""); const { method, endpoint } = request; - const body = getFinalBodyFromRequest(request, resolvedVariables); - const signer = new AwsV4Signer({ method, - body: E.isRight(body) ? body.right?.toString() : undefined, + // Must be the body actually sent, else the signature mismatches + body: effectiveFinalBody?.toString(), datetime: amzDate, signQuery: addTo === "QUERY_PARAMS", accessKeyId: parseTemplateString( @@ -337,7 +338,8 @@ export async function getEffectiveRESTRequest( opaque: request.auth.opaque ? parseTemplateString(request.auth.opaque, resolvedVariables) : authInfo.opaque, - reqBody: typeof request.body.body === "string" ? request.body.body : "", + reqBody: + typeof effectiveFinalBody === "string" ? effectiveFinalBody : "", }; // Step 3: Generate the Authorization header @@ -434,8 +436,6 @@ export async function getEffectiveRESTRequest( } } - const effectiveFinalBody = _effectiveFinalBody.right; - if ( request.body.contentType && !effectiveFinalHeaders.some( diff --git a/packages/hoppscotch-cli/src/utils/request.ts b/packages/hoppscotch-cli/src/utils/request.ts index 3c42dd3850f..0548ef21f1e 100644 --- a/packages/hoppscotch-cli/src/utils/request.ts +++ b/packages/hoppscotch-cli/src/utils/request.ts @@ -290,6 +290,13 @@ export const processRequest = // Ensure, the CLI fails with a non-zero exit code if there are any errors report.result = false; + + // REQUEST_ERROR here means the request could not be constructed + // (GraphQL subscription / invalid variables) — nothing to send + if (preRequestRes.left.code === "REQUEST_ERROR") { + result.report = report; + return result; + } } else { // Updating effective-request and consuming updated envs after pre-request script execution ({ effectiveRequest, updatedEnvs } = preRequestRes.right); diff --git a/packages/hoppscotch-cli/src/utils/workspace-access.ts b/packages/hoppscotch-cli/src/utils/workspace-access.ts index 2c9a947fd09..2ad5c305dd9 100644 --- a/packages/hoppscotch-cli/src/utils/workspace-access.ts +++ b/packages/hoppscotch-cli/src/utils/workspace-access.ts @@ -6,6 +6,7 @@ import { HoppCollectionVariable, HoppRESTAuth, HoppRESTHeaders, + HoppGQLRequest, HoppRESTRequest, } from "@hoppscotch/data"; @@ -39,12 +40,17 @@ interface WorkspaceRequest { * Transforms the incoming list of workspace requests by applying `JSON.parse` to the `request` field. * It includes the `v` field indicating the schema version, but migration is handled already at the `parseCollectionData()` helper function. * + * Unified team collections can mix REST and GraphQL requests — both are + * returned (in order) and validated per-schema downstream in + * `getValidRequests`; the runner routes each by shape at execution time. + * * @param {WorkspaceRequest[]} requests - An array of workspace request objects to be transformed. - * @returns {HoppRESTRequest[]} The transformed array of requests conforming to the `HoppRESTRequest` type. + * @returns The transformed array of REST/GraphQL requests. */ const transformWorkspaceRequests = ( requests: WorkspaceRequest[] -): HoppRESTRequest[] => requests.map(({ request }) => JSON.parse(request)); +): (HoppRESTRequest | HoppGQLRequest)[] => + requests.map(({ request }) => JSON.parse(request)); /** * Apply relevant migrations for data conforming to older formats diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 4dd9e298878..c6673e9fe4e 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -475,6 +475,7 @@ "title": "Body" }, "copied_to_clipboard": "Copied to clipboard!", + "copy_to_clipboard": "Copy to clipboard", "curl": { "click_to_load": "Click to load cURL command", "copied": "cURL command copied to clipboard!", @@ -503,6 +504,7 @@ "no_documentation_found": "No documentation found for folders or requests", "no_request_data": "No request data available", "no_requests_or_folders": "No requests or folders", + "no_write_access": "You don't have write access to this documentation", "not_set": "Not set", "open_request_in_new_tab": "Open request in new tab", "parameters": { @@ -518,6 +520,7 @@ "button": "Publish", "copy_url": "Copy URL", "delete": "Delete Documentation", + "delete_error": "Failed to delete the published documentation", "unpublish_doc": "Are you sure you want to unpublish the documentation?", "delete_success": "Published documentation deleted successfully", "doc_title": "Title", @@ -587,6 +590,9 @@ "untitled_collection": "Untitled Collection", "untitled_request": "Untitled Request", "value": "Value", + "query": { + "title": "Query" + }, "variables": { "no_vars": "No variables defined", "title": "Variables" @@ -825,19 +831,25 @@ "arguments": "Arguments", "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", "connection_error_http": "Failed to fetch GraphQL Schema due to network error.", - "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", + "connection_error_introspection_disabled": "Introspection is disabled on this server.", + "connection_error_invalid_json": "Introspection response was not valid JSON — the endpoint may not be a GraphQL server.", + "connection_switch_new_url": "Switching to this tab will disconnect you from the active GraphQL connection. The new connection URL is", "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "deprecated": "Deprecated", "fields": "Fields", "mutation": "Mutation", "mutations": "Mutations", + "operation_error": "Something went wrong while running the operation", "schema": "Schema", "show_depricated_values": "Show deprecated values", "subscription": "Subscription", + "subscription_log": "Subscription Log", "subscriptions": "Subscriptions", + "subscribing": "Connecting to subscription endpoint…", "switch_connection": "Switch connection", "url_placeholder": "Enter a GraphQL endpoint URL", - "query": "Query" + "query": "Query", + "waiting_for_events": "Subscribed. Waiting for events…" }, "graphql_collections": { "title": "GraphQL Collections" @@ -1200,6 +1212,7 @@ "show_content_type": "Show Content Type", "different_collection": "Cannot reorder requests from different collections", "duplicated": "Request duplicated", + "rest_in_gql_collection": "This is a REST request — open it from the REST workspace", "duration": "Duration", "enter_curl": "Enter cURL command", "generate_code": "Generate code", @@ -1212,6 +1225,7 @@ "moved": "Request moved", "name": "Request name", "new": "New Request", + "new_gql": "New GraphQL Request", "order_changed": "Request Order Updated", "override": "Override", "override_help": "Set Content-Type in Headers", @@ -1234,7 +1248,9 @@ "share_description": "Share Hoppscotch with your friends", "share_request": "Share Request", "stop": "Stop", + "switch_protocol": "Switch protocol", "title": "Request", + "try": "Try", "type": "Request type", "url": "URL", "url_placeholder": "Enter a URL or paste a cURL command", @@ -1269,6 +1285,7 @@ "generate_data_schema": "Generate Data Schema", "data_schema": "Data Schema", "saved": "Response saved", + "save_as_example": "Save as example", "invalid_name": "Please provide a name for the response" }, "script": { @@ -1376,6 +1393,7 @@ "experimental_scripting_sandbox": "Experimental scripting sandbox", "enable_experimental_mock_servers": "Enable Mock Servers", "enable_experimental_documentation": "Enable Documentation", + "enable_gql_in_rest_workspace": "Enable Unified GraphQL workspace", "sync": "Synchronise", "sync_collections": "Collections", "sync_description": "These settings are synced to cloud.", @@ -1429,6 +1447,7 @@ "proxy_auth": "You can also include username and password in the URL." }, "shared_requests": { + "auth_warning": "This request contains authentication credentials. Anyone with the share link will be able to read them — including tokens, passwords, and API keys.", "button": "Button", "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "copy_html": "Copy HTML", @@ -1941,6 +1960,7 @@ "snippets": "Snippets", "run": "Run", "run_again": "Run again", + "running": "Running tests...", "stop": "Stop", "new_run": "New Run", "iterations": "Iterations", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 05bb83ac4d3..1211528f261 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -68,8 +68,10 @@ declare module 'vue' { CollectionsDocumentationRequestPreview: typeof import('./components/collections/documentation/RequestPreview.vue')['default'] CollectionsDocumentationSectionsAuth: typeof import('./components/collections/documentation/sections/Auth.vue')['default'] CollectionsDocumentationSectionsCurlView: typeof import('./components/collections/documentation/sections/CurlView.vue')['default'] + CollectionsDocumentationSectionsGqlVariables: typeof import('./components/collections/documentation/sections/GqlVariables.vue')['default'] CollectionsDocumentationSectionsHeaders: typeof import('./components/collections/documentation/sections/Headers.vue')['default'] CollectionsDocumentationSectionsParameters: typeof import('./components/collections/documentation/sections/Parameters.vue')['default'] + CollectionsDocumentationSectionsQuery: typeof import('./components/collections/documentation/sections/Query.vue')['default'] CollectionsDocumentationSectionsRequestBody: typeof import('./components/collections/documentation/sections/RequestBody.vue')['default'] CollectionsDocumentationSectionsResponse: typeof import('./components/collections/documentation/sections/Response.vue')['default'] CollectionsDocumentationSectionsVariables: typeof import('./components/collections/documentation/sections/Variables.vue')['default'] @@ -108,6 +110,8 @@ declare module 'vue' { DocumentationHeader: typeof import('./components/documentation/Header.vue')['default'] DocumentationSkeleton: typeof import('./components/documentation/Skeleton.vue')['default'] Embeds: typeof import('./components/embeds/index.vue')['default'] + EmbedsGQLIndex: typeof import('./components/embeds/GQLIndex.vue')['default'] + EmbedsGQLRequest: typeof import('./components/embeds/GQLRequest.vue')['default'] EmbedsHeader: typeof import('./components/embeds/Header.vue')['default'] EmbedsRequest: typeof import('./components/embeds/Request.vue')['default'] Environments: typeof import('./components/environments/index.vue')['default'] @@ -123,6 +127,37 @@ declare module 'vue' { EnvironmentsTeamsEnvironment: typeof import('./components/environments/teams/Environment.vue')['default'] FirebaseLogin: typeof import('./components/firebase/Login.vue')['default'] FirebaseLogout: typeof import('./components/firebase/Logout.vue')['default'] + GqlArgument: typeof import('./components/gql/Argument.vue')['default'] + GqlArguments: typeof import('./components/gql/Arguments.vue')['default'] + GqlAuthorization: typeof import('./components/gql/Authorization.vue')['default'] + GqlDefaultValue: typeof import('./components/gql/DefaultValue.vue')['default'] + GqlDirectives: typeof import('./components/gql/Directives.vue')['default'] + GqlDocExplorer: typeof import('./components/gql/DocExplorer.vue')['default'] + GqlEnumValues: typeof import('./components/gql/EnumValues.vue')['default'] + GqlExampleResponse: typeof import('./components/gql/example/Response.vue')['default'] + GqlExampleResponseRequest: typeof import('./components/gql/example/ResponseRequest.vue')['default'] + GqlExampleResponseTab: typeof import('./components/gql/example/ResponseTab.vue')['default'] + GqlExplorerSection: typeof import('./components/gql/ExplorerSection.vue')['default'] + GqlField: typeof import('./components/gql/Field.vue')['default'] + GqlFieldDocumentation: typeof import('./components/gql/FieldDocumentation.vue')['default'] + GqlFieldLink: typeof import('./components/gql/FieldLink.vue')['default'] + GqlFields: typeof import('./components/gql/Fields.vue')['default'] + GqlHeaders: typeof import('./components/gql/Headers.vue')['default'] + GqlImplementsInterfaces: typeof import('./components/gql/ImplementsInterfaces.vue')['default'] + GqlQuery: typeof import('./components/gql/Query.vue')['default'] + GqlRequest: typeof import('./components/gql/Request.vue')['default'] + GqlRequestOptions: typeof import('./components/gql/RequestOptions.vue')['default'] + GqlRequestTab: typeof import('./components/gql/RequestTab.vue')['default'] + GqlResponse: typeof import('./components/gql/Response.vue')['default'] + GqlResponseMeta: typeof import('./components/gql/ResponseMeta.vue')['default'] + GqlSchema: typeof import('./components/gql/Schema.vue')['default'] + GqlSchemaDocumentation: typeof import('./components/gql/SchemaDocumentation.vue')['default'] + GqlSchemaSearch: typeof import('./components/gql/SchemaSearch.vue')['default'] + GqlSubscriptionLog: typeof import('./components/gql/SubscriptionLog.vue')['default'] + GqlTabHead: typeof import('./components/gql/TabHead.vue')['default'] + GqlTypeDocumentation: typeof import('./components/gql/TypeDocumentation.vue')['default'] + GqlTypeLink: typeof import('./components/gql/TypeLink.vue')['default'] + GqlVariable: typeof import('./components/gql/Variable.vue')['default'] GraphqlArgument: typeof import('./components/graphql/Argument.vue')['default'] GraphqlArguments: typeof import('./components/graphql/Arguments.vue')['default'] GraphqlAuthorization: typeof import('./components/graphql/Authorization.vue')['default'] @@ -154,6 +189,7 @@ declare module 'vue' { GraphqlVariable: typeof import('./components/graphql/Variable.vue')['default'] History: typeof import('./components/history/index.vue')['default'] HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default'] + HistoryGraphqlMergedCard: typeof import('./components/history/graphql/MergedCard.vue')['default'] HistoryPersonal: typeof import('./components/history/Personal.vue')['default'] HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default'] HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] @@ -208,6 +244,7 @@ declare module 'vue' { HttpKeyValue: typeof import('./components/http/KeyValue.vue')['default'] HttpParameters: typeof import('./components/http/Parameters.vue')['default'] HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default'] + HttpProtocolSwitcher: typeof import('./components/http/ProtocolSwitcher.vue')['default'] HttpRawBody: typeof import('./components/http/RawBody.vue')['default'] HttpReqChangeConfirmModal: typeof import('./components/http/ReqChangeConfirmModal.vue')['default'] HttpRequest: typeof import('./components/http/Request.vue')['default'] @@ -243,6 +280,7 @@ declare module 'vue' { IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] + IconLucideBox: typeof import('~icons/lucide/box')['default'] IconLucideBrush: typeof import('~icons/lucide/brush')['default'] IconLucideCheck: typeof import('~icons/lucide/check')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] @@ -317,11 +355,13 @@ declare module 'vue' { SettingsProxy: typeof import('./components/settings/Proxy.vue')['default'] Share: typeof import('./components/share/index.vue')['default'] ShareCreateModal: typeof import('./components/share/CreateModal.vue')['default'] + ShareCustomizeGQLModal: typeof import('./components/share/CustomizeGQLModal.vue')['default'] ShareCustomizeModal: typeof import('./components/share/CustomizeModal.vue')['default'] ShareModal: typeof import('./components/share/Modal.vue')['default'] ShareRequest: typeof import('./components/share/Request.vue')['default'] ShareTemplatesButton: typeof import('./components/share/templates/Button.vue')['default'] ShareTemplatesEmbeds: typeof import('./components/share/templates/Embeds.vue')['default'] + ShareTemplatesEmbedsGQL: typeof import('./components/share/templates/EmbedsGQL.vue')['default'] ShareTemplatesLink: typeof import('./components/share/templates/Link.vue')['default'] SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default'] SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/app/Inspection.vue b/packages/hoppscotch-common/src/components/app/Inspection.vue index fa628bf5d73..c0a12ada648 100644 --- a/packages/hoppscotch-common/src/components/app/Inspection.vue +++ b/packages/hoppscotch-common/src/components/app/Inspection.vue @@ -52,7 +52,7 @@
(), + { + documentationUrl: + "https://docs.hoppscotch.io/documentation/features/rest-api-testing#response", + } +) + const t = useI18n() diff --git a/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue index 95f2510e892..3d8c69cccbd 100644 --- a/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue +++ b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue @@ -13,6 +13,12 @@ > {{ request.method.toUpperCase() }} + + + {{ request.name }} @@ -22,8 +28,10 @@ diff --git a/packages/hoppscotch-common/src/components/collections/documentation/sections/Query.vue b/packages/hoppscotch-common/src/components/collections/documentation/sections/Query.vue new file mode 100644 index 00000000000..2583584a301 --- /dev/null +++ b/packages/hoppscotch-common/src/components/collections/documentation/sections/Query.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/hoppscotch-common/src/components/collections/documentation/sections/RequestBody.vue b/packages/hoppscotch-common/src/components/collections/documentation/sections/RequestBody.vue index 6d7edcf0a5c..1678014a8f7 100644 --- a/packages/hoppscotch-common/src/components/collections/documentation/sections/RequestBody.vue +++ b/packages/hoppscotch-common/src/components/collections/documentation/sections/RequestBody.vue @@ -19,8 +19,7 @@
{{ formatJSON(body.body) }}
+ >{{ formatJSON(body.body) }}
@@ -55,8 +54,7 @@
{{ body.body }}
+ >{{ body.body }}
diff --git a/packages/hoppscotch-common/src/components/collections/documentation/sections/Response.vue b/packages/hoppscotch-common/src/components/collections/documentation/sections/Response.vue index b2367bbabeb..2812033fc11 100644 --- a/packages/hoppscotch-common/src/components/collections/documentation/sections/Response.vue +++ b/packages/hoppscotch-common/src/components/collections/documentation/sections/Response.vue @@ -51,14 +51,12 @@
{{ formatJSON(example.body) }}
+ >{{ formatJSON(example.body) }}
{{ example.body }}
+ >{{ example.body }}
diff --git a/packages/hoppscotch-common/src/components/collections/graphql/index.vue b/packages/hoppscotch-common/src/components/collections/graphql/index.vue index cd454fc369b..cd4027500df 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/index.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/index.vue @@ -186,6 +186,7 @@ import { getDefaultGQLRequest, HoppCollection, HoppGQLRequest, + isGQLRequest, } from "@hoppscotch/data" import { Picked } from "~/helpers/types/HoppPicked" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" @@ -528,6 +529,12 @@ const selectRequest = ({ folderPath: string requestIndex: number }) => { + // GQL collections can hold REST-shaped requests (mixed import) — they + // can't open as GQL tabs on this page + if (!isGQLRequest(request)) { + toast.error(t("request.rest_in_gql_collection")) + return + } const possibleTab = tabs.getTabRefWithSaveContext({ originLocation: "user-collection", folderPath: folderPath, diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index d0b24107099..6ac9a15ffcd 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -42,6 +42,7 @@ " @add-folder="addFolder" @add-request="addRequest" + @add-gql-request="addGqlRequest" @edit-request="editRequest" @edit-collection="editCollection" @edit-folder="editFolder" @@ -94,6 +95,7 @@ :collection-move-loading="collectionMoveLoading" :request-move-loading="requestMoveLoading" @add-request="addRequest" + @add-gql-request="addGqlRequest" @add-folder="addFolder" @collection-click="handleCollectionClick" @duplicate-collection="duplicateCollection" @@ -150,6 +152,7 @@ @@ -304,13 +307,20 @@ import { generateUniqueRefId, getDefaultRESTRequest, HoppCollection, + HoppGQLRequest, + HoppGQLRequestResponse, HoppRESTAuth, HoppRESTHeaders, HoppRESTRequest, HoppRESTRequestResponse, + isGQLRequest, makeCollection, + makeHoppGQLResponseOriginalRequest, makeHoppRESTResponseOriginalRequest, } from "@hoppscotch/data" +import { getDefaultGQLRequest } from "~/helpers/graphql/default" +import { parse as parseGQLDocument } from "graphql" +import type { OperationDefinitionNode } from "graphql" import { useService } from "dioc/vue" import { stripJsonSerializedModulePrefix } from "@hoppscotch/js-sandbox/scripting" @@ -366,6 +376,7 @@ import { stripRefIdReplacer } from "~/helpers/import-export/export" import { hoppCollectionToOpenAPI } from "~/helpers/import-export/export/openapi" import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter" import { TeamSearchService } from "~/helpers/teams/TeamsSearch.service" +import { HoppTabDocument } from "~/helpers/tab/document" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { Picked } from "~/helpers/types/HoppPicked" import { @@ -396,7 +407,7 @@ import { currentReorderingStatus$ } from "~/newstore/reordering" import { platform } from "~/platform" import { PersistedOAuthConfig } from "~/services/oauth/oauth.service" import { PersistenceService } from "~/services/persistence" -import { RESTTabService } from "~/services/tab/rest" +import { WorkspaceTabsService } from "~/services/tab/workspace-tabs" import { TeamWorkspace, WorkspaceService } from "~/services/workspace.service" import { RESTOptionTabs } from "../http/RequestOptions.vue" import { Collection as NodeCollection } from "./MyCollections.vue" @@ -417,7 +428,7 @@ import { const t = useI18n() const toast = useToast() -const tabs = useService(RESTTabService) +const tabs = useService(WorkspaceTabsService) const props = defineProps({ saveRequest: { @@ -461,8 +472,9 @@ const editingCollectionPath = ref(null) const editingFolder = ref(null) const editingFolderName = ref(null) const editingFolderPath = ref(null) +const requestTypeToAdd = ref<"rest" | "gql">("rest") -const editingRequest = ref(null) +const editingRequest = ref(null) const editingRequestName = ref("") const editingResponseName = ref("") const editingResponseOldName = ref("") @@ -494,6 +506,18 @@ const currentUser = useReadonlyStream( const myCollections = useReadonlyStream(restCollections$, [], "deep") +const setRequestTabResponses = ( + tabRef: { value: { document: HoppTabDocument } }, + responses: HoppRESTRequest["responses"] | HoppGQLRequest["responses"] +) => { + const doc = tabRef.value.document + if (doc.type === "request") { + doc.request.responses = responses as HoppRESTRequest["responses"] + } else if (doc.type === "gql-request") { + doc.request.responses = responses as HoppGQLRequest["responses"] + } +} + // Dragging const draggingToRoot = ref(false) const collectionMoveLoading = ref([]) @@ -707,15 +731,16 @@ const filteredCollections = computed(() => { const isMatch = (text: string) => text.toLowerCase().includes(filterText) - const isRequestMatch = (request: HoppRESTRequest) => - isMatch(request.name) || isMatch(request.endpoint) + const isRequestMatch = (request: HoppRESTRequest | HoppGQLRequest) => + isMatch(request.name) || + (!isGQLRequest(request) && isMatch(request.endpoint)) || + (isGQLRequest(request) && isMatch(request.url)) for (const collection of collections) { const filteredRequests = [] const filteredFolders = [] for (const request of collection.requests) { - if (isRequestMatch(request as HoppRESTRequest)) - filteredRequests.push(request) + if (isRequestMatch(request)) filteredRequests.push(request) } for (const folder of collection.folders) { if (isMatch(folder.name)) filteredFolders.push(folder) @@ -1006,14 +1031,27 @@ const addRequest = (payload: { const { path, folder } = payload editingFolder.value = folder editingFolderPath.value = path + requestTypeToAdd.value = "rest" + displayModalAddRequest(true) +} + +const addGqlRequest = (payload: { + path: string + folder: HoppCollection | TeamCollection +}) => { + const { path, folder } = payload + editingFolder.value = folder + editingFolderPath.value = path + requestTypeToAdd.value = "gql" displayModalAddRequest(true) } const onAddRequest = async (requestName: string) => { - const newRequest = { - ...getDefaultRESTRequest(), - name: requestName, - } + const isGqlRequest = requestTypeToAdd.value === "gql" + + const newRequest = isGqlRequest + ? { ...getDefaultGQLRequest(), name: requestName } + : { ...getDefaultRESTRequest(), name: requestName } const path = editingFolderPath.value if (!path) return @@ -1023,24 +1061,40 @@ const onAddRequest = async (requestName: string) => { const insertionIndex = saveRESTRequestAs(path, newRequest) - tabs.createNewTab({ - type: "request", - request: newRequest, - isDirty: false, - saveContext: { - originLocation: "user-collection", - folderPath: path, - requestIndex: insertionIndex, - requestRefID: newRequest._ref_id, - }, - inheritedProperties: cascadeParentCollectionForProperties(path, "rest"), - }) + if (isGqlRequest) { + tabs.createNewTab({ + type: "gql-request", + request: newRequest as HoppGQLRequest, + isDirty: false, + cursorPosition: 0, + saveContext: { + originLocation: "user-collection", + folderPath: path, + requestIndex: insertionIndex, + requestRefID: (newRequest as HoppGQLRequest)._ref_id, + }, + inheritedProperties: cascadeParentCollectionForProperties(path, "rest"), + }) + } else { + tabs.createNewTab({ + type: "request", + request: newRequest as HoppRESTRequest, + isDirty: false, + saveContext: { + originLocation: "user-collection", + folderPath: path, + requestIndex: insertionIndex, + requestRefID: (newRequest as HoppRESTRequest)._ref_id, + }, + inheritedProperties: cascadeParentCollectionForProperties(path, "rest"), + }) + } platform.analytics?.logEvent({ type: "HOPP_SAVE_REQUEST", workspaceType: "personal", createdNow: true, - platform: "rest", + platform: isGqlRequest ? "gql" : "rest", }) displayModalAddRequest(false) @@ -1061,7 +1115,7 @@ const onAddRequest = async (requestName: string) => { platform.analytics?.logEvent({ type: "HOPP_SAVE_REQUEST", workspaceType: "team", - platform: "rest", + platform: isGqlRequest ? "gql" : "rest", createdNow: true, }) @@ -1075,20 +1129,42 @@ const onAddRequest = async (requestName: string) => { (result) => { const { createRequestInCollection } = result - tabs.createNewTab({ - type: "request", - request: newRequest, - isDirty: false, - saveContext: { - originLocation: "team-collection", - requestID: createRequestInCollection.id, - collectionID: path, - teamID: createRequestInCollection.collection.team.id, - requestRefID: newRequest._ref_id, - }, - inheritedProperties: - teamCollectionService.cascadeParentCollectionForProperties(path), - }) + if (isGqlRequest) { + tabs.createNewTab({ + type: "gql-request", + request: newRequest as HoppGQLRequest, + isDirty: false, + cursorPosition: 0, + saveContext: { + originLocation: "team-collection", + requestID: createRequestInCollection.id, + collectionID: path, + teamID: createRequestInCollection.collection.team.id, + requestRefID: (newRequest as HoppGQLRequest)._ref_id, + }, + inheritedProperties: + teamCollectionService.cascadeParentCollectionForProperties( + path + ), + }) + } else { + tabs.createNewTab({ + type: "request", + request: newRequest as HoppRESTRequest, + isDirty: false, + saveContext: { + originLocation: "team-collection", + requestID: createRequestInCollection.id, + collectionID: path, + teamID: createRequestInCollection.collection.team.id, + requestRefID: (newRequest as HoppRESTRequest)._ref_id, + }, + inheritedProperties: + teamCollectionService.cascadeParentCollectionForProperties( + path + ), + }) + } modalLoadingState.value = false displayModalAddRequest(false) @@ -1355,7 +1431,7 @@ const duplicateCollection = async ({ const editRequest = (payload: { folderPath: string | undefined requestIndex: string - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest }) => { const { folderPath, requestIndex, request } = payload editingRequest.value = request @@ -1396,7 +1472,8 @@ const updateEditingRequest = async (newName: string) => { if ( possibleActiveTab && - possibleActiveTab.value.document.type === "request" + (possibleActiveTab.value.document.type === "request" || + possibleActiveTab.value.document.type === "gql-request") ) { possibleActiveTab.value.document.request.name = requestUpdated.name nextTick(() => { @@ -1438,7 +1515,11 @@ const updateEditingRequest = async (newName: string) => { requestID, }) - if (possibleTab && possibleTab.value.document.type === "request") { + if ( + possibleTab && + (possibleTab.value.document.type === "request" || + possibleTab.value.document.type === "gql-request") + ) { possibleTab.value.document.request.name = requestName nextTick(() => { possibleTab.value.document.isDirty = false @@ -1450,7 +1531,7 @@ const updateEditingRequest = async (newName: string) => { type ResponseConfigPayload = { folderPath: string | undefined requestIndex: string - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest responseName: string responseID: string } @@ -1521,12 +1602,17 @@ const updateEditingResponse = (newName: string) => { if ( possibleExampleActiveTab && - possibleExampleActiveTab.value.document.type === "example-response" + (possibleExampleActiveTab.value.document.type === "example-response" || + possibleExampleActiveTab.value.document.type === "gql-example-response") ) { possibleExampleActiveTab.value.document.response.name = newName nextTick(() => { - if (possibleExampleActiveTab.value.document.type === "test-runner") + const docType = possibleExampleActiveTab.value.document.type + if ( + docType !== "example-response" && + docType !== "gql-example-response" + ) return possibleExampleActiveTab.value.document.isDirty = false @@ -1539,13 +1625,8 @@ const updateEditingResponse = (newName: string) => { }) } - // update the request tab responses if it's open - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - request.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, request.responses) } displayModalEditResponse(false) @@ -1591,11 +1672,17 @@ const updateEditingResponse = (newName: string) => { if ( possibleActiveResponseTab && - possibleActiveResponseTab.value.document.type === "example-response" + (possibleActiveResponseTab.value.document.type === "example-response" || + possibleActiveResponseTab.value.document.type === + "gql-example-response") ) { possibleActiveResponseTab.value.document.response.name = newName nextTick(() => { - if (possibleActiveResponseTab.value.document.type === "test-runner") + const docType = possibleActiveResponseTab.value.document.type + if ( + docType !== "example-response" && + docType !== "gql-example-response" + ) return possibleActiveResponseTab.value.document.isDirty = false possibleActiveResponseTab.value.document.saveContext = { @@ -1606,30 +1693,26 @@ const updateEditingResponse = (newName: string) => { }) } - // update the request tab responses if it's open - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - request.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, request.responses) } } } const duplicateRequest = async (payload: { folderPath: string - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest }) => { const { folderPath, request } = payload if (!folderPath) return const { id: _, ...requestWithoutID } = request + const cloned = cloneDeep(requestWithoutID) const newRequest = { - ...cloneDeep(requestWithoutID), + ...cloned, _ref_id: generateUniqueRefId("req"), name: `${request.name} - ${t("action.duplicate")}`, - } + } as HoppRESTRequest | HoppGQLRequest if (collectionsType.value.type === "my-collections") { const isValidToken = await handleTokenValidation() @@ -1704,13 +1787,8 @@ const duplicateResponse = async (payload: ResponseConfigPayload) => { folderPath, }) - // update the request tab responses if it's open - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - updatedRequest.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, updatedRequest.responses) } } else if (hasTeamWriteAccess.value) { duplicateRequestLoading.value = true @@ -1737,25 +1815,21 @@ const duplicateResponse = async (payload: ResponseConfigPayload) => { ) )() - // update the request tab responses if it's open + // update the request tab responses if it's open (REST or GQL) const possibleRequestActiveTab = tabs.getTabRefWithSaveContext({ originLocation: "team-collection", requestID: requestIndex, }) - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - updatedRequest.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, updatedRequest.responses) } } } const addExample = (payload: { folderPath: string - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest requestIndex: string | number }) => { const { folderPath, request, requestIndex } = payload @@ -1767,8 +1841,12 @@ const addExample = (payload: { return } - // Additional validation for required request properties - if (!request.name && !request.endpoint) { + // Additional validation for required request properties — accept either the + // REST endpoint or the GQL url as proof we have a real request. + const hasUrl = isGQLRequest(request) + ? !!request.url + : !!(request as HoppRESTRequest).endpoint + if (!request.name && !hasUrl) { console.error("Request missing required properties:", request) toast.error(t("error.invalid_request")) return @@ -1802,6 +1880,13 @@ const onAddExample = async () => { return } + // GQL examples have a parallel-but-distinct shape (GQL originalRequest, + // no method/params/body). Handle them via the gql-example-response tab type. + if (isGQLRequest(request)) { + await addGQLExample(request, exampleName) + return + } + // Check if example name already exists if (request.responses && request.responses[exampleName]) { toast.error(t("response.duplicate_name_error")) @@ -1972,6 +2057,170 @@ const onAddExample = async () => { } } +/** + * GQL counterpart of the REST add-example flow inside `onAddExample`. Builds a + * `HoppGQLRequestResponse` with an empty body, persists it under the parent + * request's `responses` map, and opens it as a new `gql-example-response` tab. + * Persistence goes through the REST mutation in both personal and team paths + * because unified-workspace GQL bodies live in REST collection rows. + */ +const addGQLExample = async (request: HoppGQLRequest, exampleName: string) => { + if (request.responses && request.responses[exampleName]) { + toast.error(t("response.duplicate_name_error")) + return + } + + const originalRequest = makeHoppGQLResponseOriginalRequest({ + name: request.name, + url: request.url, + query: request.query, + variables: request.variables, + headers: request.headers, + auth: request.auth, + }) + + // Stamp the operation identity from the request's document (first + // operation — the one a run would execute) so the mock server can match + // this example; without stamps the matcher skips it entirely + let operationName: string | undefined + let operationType: string | undefined + try { + const operation = parseGQLDocument(request.query).definitions.find( + (definition): definition is OperationDefinitionNode => + definition.kind === "OperationDefinition" + ) + if (operation) { + operationType = operation.operation + operationName = operation.name?.value + } + } catch (_e) { + // Unparseable document — leave the example unstamped + } + + const newExample: HoppGQLRequestResponse = { + name: exampleName, + code: 200, + status: "OK", + headers: [], + body: "", + originalRequest, + ...(operationType ? { operationType } : {}), + ...(operationName ? { operationName } : {}), + } + + const newExampleID = Object.keys(request.responses ?? {}).length.toString() + + const updatedRequest: HoppGQLRequest = { + ...request, + responses: { + ...(request.responses ?? {}), + [exampleName]: newExample, + }, + } + + if (collectionsType.value.type === "my-collections") { + const folderPath = editingFolderPath.value + const requestIndex = editingRequestIndex.value + if (folderPath === null || requestIndex === null) return + + const isValidToken = await handleTokenValidation() + if (!isValidToken) return + + editRESTRequest(folderPath, requestIndex, updatedRequest) + toast.success(t("response.saved")) + + const possibleRequestActiveTab = tabs.getTabRefWithSaveContext({ + originLocation: "user-collection", + requestIndex, + folderPath, + }) + if ( + possibleRequestActiveTab && + possibleRequestActiveTab.value.document.type === "gql-request" + ) { + possibleRequestActiveTab.value.document.request.responses = + updatedRequest.responses + } + + displayModalAddExample(false) + + tabs.createNewTab({ + response: { ...cloneDeep(newExample), name: exampleName }, + isDirty: false, + type: "gql-example-response", + saveContext: { + originLocation: "user-collection", + folderPath, + requestIndex, + exampleID: newExampleID, + }, + inheritedProperties: cascadeParentCollectionForProperties( + folderPath, + "rest" + ), + }) + return + } + + if (hasTeamWriteAccess.value) { + if (!editingRequestID.value) return + modalLoadingState.value = true + + const data = { + requestID: editingRequestID.value, + data: { title: request.name, request: JSON.stringify(updatedRequest) }, + } + + pipe( + runMutation(UpdateRequestDocument, data), + TE.match( + (err: GQLError) => { + toast.error(`${getErrorMessage(err)}`) + modalLoadingState.value = false + }, + () => { + modalLoadingState.value = false + toast.success(t("response.saved")) + displayModalAddExample(false) + + const requestID = editingRequestID.value + const collectionID = editingFolderPath.value + if (!requestID) return + + const possibleRequestActiveTab = tabs.getTabRefWithSaveContext({ + originLocation: "team-collection", + requestID, + }) + if ( + possibleRequestActiveTab && + possibleRequestActiveTab.value.document.type === "gql-request" + ) { + possibleRequestActiveTab.value.document.request.responses = + updatedRequest.responses + } + + tabs.createNewTab({ + response: { ...cloneDeep(newExample), name: exampleName }, + isDirty: false, + type: "gql-example-response", + saveContext: { + originLocation: "team-collection", + requestID, + collectionID: collectionID ?? undefined, + exampleID: newExampleID, + }, + inheritedProperties: collectionID + ? teamCollectionService.cascadeParentCollectionForProperties( + collectionID + ) + : undefined, + }) + } + ) + )() + } +} + const removeCollection = (id: string) => { if (collectionsType.value.type === "my-collections") editingCollectionIndex.value = parseInt(id) @@ -2196,16 +2445,19 @@ const onRemoveRequest = async () => { requestIndex, }) - // If there is a tab attached to this request, dissociate its state and mark it dirty - if (possibleTab && possibleTab.value.document.type === "request") { - possibleTab.value.document.saveContext = null - possibleTab.value.document.isDirty = true - - // since the request is deleted, we need to remove the saved responses as well - possibleTab.value.document.request.responses = {} - - // remove inherited properties - possibleTab.value.document.inheritedProperties = undefined + if (possibleTab) { + const doc = possibleTab.value.document + if (doc.type === "request") { + doc.saveContext = null + doc.isDirty = true + doc.request.responses = {} + doc.inheritedProperties = undefined + } else if (doc.type === "gql-request") { + doc.saveContext = null + doc.isDirty = true + doc.request.responses = {} + doc.inheritedProperties = undefined + } } const requestToRemove = navigateToFolderWithIndexPath( @@ -2261,15 +2513,19 @@ const onRemoveRequest = async () => { requestID, }) - if (possibleTab && possibleTab.value.document.type === "request") { - possibleTab.value.document.saveContext = null - possibleTab.value.document.isDirty = true - - // since the request is deleted, we need to remove the saved responses as well - possibleTab.value.document.request.responses = {} - - // remove inherited properties - possibleTab.value.document.inheritedProperties = undefined + if (possibleTab) { + const doc = possibleTab.value.document + if (doc.type === "request") { + doc.saveContext = null + doc.isDirty = true + doc.request.responses = {} + doc.inheritedProperties = undefined + } else if (doc.type === "gql-request") { + doc.saveContext = null + doc.isDirty = true + doc.request.responses = {} + doc.inheritedProperties = undefined + } } } } @@ -2303,7 +2559,7 @@ const onRemoveResponse = async () => { delete request.responses[responseName] - const requestUpdated: HoppRESTRequest = { + const requestUpdated: HoppRESTRequest | HoppGQLRequest = { ...request, } @@ -2330,10 +2586,13 @@ const onRemoveResponse = async () => { folderPath, }) - // If there is a tab attached to this request, close it and set the active tab to the first one + // If there is a tab attached to this response (REST or GQL example), close + // it and set the active tab to the first one. if ( possibleActiveResponseTab && - possibleActiveResponseTab.value.document.type === "example-response" + (possibleActiveResponseTab.value.document.type === "example-response" || + possibleActiveResponseTab.value.document.type === + "gql-example-response") ) { const activeTabs = tabs.getActiveTabs() @@ -2355,13 +2614,8 @@ const onRemoveResponse = async () => { } } - // update the request tab responses if it's open - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - requestUpdated.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, requestUpdated.responses) } toast.success(t("state.deleted")) @@ -2404,10 +2658,13 @@ const onRemoveResponse = async () => { requestID, }) - // If there is a tab attached to this request, close it and set the active tab to the first one + // If there is a tab attached to this response (REST or GQL example), close + // it and set the active tab to the first one. if ( possibleActiveResponseTab && - possibleActiveResponseTab.value.document.type === "example-response" + (possibleActiveResponseTab.value.document.type === "example-response" || + possibleActiveResponseTab.value.document.type === + "gql-example-response") ) { const activeTabs = tabs.getActiveTabs() @@ -2429,13 +2686,8 @@ const onRemoveResponse = async () => { } } - // update the request tab responses if it's open - if ( - possibleRequestActiveTab && - possibleRequestActiveTab.value.document.type === "request" - ) { - possibleRequestActiveTab.value.document.request.responses = - requestUpdated.responses + if (possibleRequestActiveTab) { + setRequestTabResponses(possibleRequestActiveTab, requestUpdated.responses) } } } @@ -2450,7 +2702,7 @@ const selectPicked = (payload: Picked | null) => { * @param selectedRequest The request that the user clicked on emitted from the collection tree */ const selectRequest = (selectedRequest: { - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest folderPath: string requestIndex: string isActive: boolean @@ -2474,17 +2726,34 @@ const selectRequest = (selectedRequest: { teamCollectionService.cascadeParentCollectionForProperties(folderPath) } + const isGql = isGQLRequest(request) + const possibleTab = tabs.getTabRefWithSaveContext({ originLocation: "team-collection", requestID: requestIndex, }) - if (possibleTab && possibleTab.value.document.type === "request") { + if (possibleTab) { tabs.setActiveTab(possibleTab.value.id) + } else if (isGql) { + tabs.createNewTab({ + type: "gql-request", + request: cloneDeep(request) as HoppGQLRequest, + isDirty: false, + cursorPosition: 0, + saveContext: { + originLocation: "team-collection", + requestID: requestIndex, + collectionID: folderPath, + exampleID: undefined, + requestRefID: request.id, + }, + inheritedProperties: inheritedProperties, + }) } else { tabs.createNewTab({ type: "request", - request: cloneDeep(request), + request: cloneDeep(request) as HoppRESTRequest, isDirty: false, saveContext: { originLocation: "team-collection", @@ -2497,6 +2766,8 @@ const selectRequest = (selectedRequest: { }) } } else { + const isGql = isGQLRequest(request) + possibleTab = tabs.getTabRefWithSaveContext({ originLocation: "user-collection", requestIndex: parseInt(requestIndex), @@ -2506,11 +2777,27 @@ const selectRequest = (selectedRequest: { if (possibleTab) { tabs.setActiveTab(possibleTab.value.id) + } else if (isGql) { + tabs.createNewTab({ + type: "gql-request", + request: cloneDeep(request) as HoppGQLRequest, + isDirty: false, + cursorPosition: 0, + saveContext: { + originLocation: "user-collection", + folderPath: folderPath!, + requestIndex: parseInt(requestIndex), + requestRefID: request._ref_id ?? request.id, + }, + inheritedProperties: cascadeParentCollectionForProperties( + folderPath, + "rest" + ), + }) } else { - // If not, open the request in a new tab tabs.createNewTab({ type: "request", - request: cloneDeep(request), + request: cloneDeep(request) as HoppRESTRequest, isDirty: false, saveContext: { originLocation: "user-collection", @@ -2531,12 +2818,82 @@ const selectResponse = (payload: { folderPath: string requestIndex: string responseName: string - request: HoppRESTRequest + request: HoppRESTRequest | HoppGQLRequest responseID: string }) => { const { folderPath, requestIndex, responseName, request, responseID } = payload + // GQL examples have their own tab document type (`gql-example-response`) + // backed by a GQL-shaped response payload; route there instead of the REST + // `example-response` path which renders REST-only components. + if (isGQLRequest(request)) { + const gqlResponse = request.responses[responseName] + if (!gqlResponse) return + + if (collectionsType.value.type === "my-collections") { + const possibleTab = tabs.getTabRefWithSaveContext({ + originLocation: "user-collection", + requestIndex: parseInt(requestIndex), + folderPath: folderPath!, + exampleID: responseID, + }) + + if (possibleTab) { + tabs.setActiveTab(possibleTab.value.id) + } else { + tabs.createNewTab({ + response: { + ...cloneDeep(gqlResponse), + name: responseName, + }, + isDirty: false, + type: "gql-example-response", + saveContext: { + originLocation: "user-collection", + folderPath: folderPath!, + requestIndex: parseInt(requestIndex), + exampleID: responseID, + }, + inheritedProperties: cascadeParentCollectionForProperties( + folderPath, + "rest" + ), + }) + } + } else { + const possibleTab = tabs.getTabRefWithSaveContext({ + originLocation: "team-collection", + requestID: requestIndex, + exampleID: responseID, + }) + + if (possibleTab) { + tabs.setActiveTab(possibleTab.value.id) + } else { + tabs.createNewTab({ + response: { + ...cloneDeep(gqlResponse), + name: responseName, + }, + isDirty: false, + type: "gql-example-response", + saveContext: { + originLocation: "team-collection", + requestID: requestIndex, + collectionID: folderPath, + exampleID: responseID, + }, + inheritedProperties: + teamCollectionService.cascadeParentCollectionForProperties( + folderPath + ), + }) + } + } + return + } + const response = request.responses[responseName] if (collectionsType.value.type === "my-collections") { @@ -2644,8 +3001,11 @@ const dropRequest = async (payload: { requestRefID, }) - // If there is a tab attached to this request, change save its save context - if (possibleTab && possibleTab.value.document.type === "request") { + if ( + possibleTab && + (possibleTab.value.document.type === "request" || + possibleTab.value.document.type === "gql-request") + ) { possibleTab.value.document.saveContext = { originLocation: "user-collection", folderPath: destinationCollectionIndex, @@ -2701,7 +3061,11 @@ const dropRequest = async (payload: { requestID: requestIndex, }) - if (possibleTab && possibleTab.value.document.type === "request") { + if ( + possibleTab && + (possibleTab.value.document.type === "request" || + possibleTab.value.document.type === "gql-request") + ) { possibleTab.value.document.saveContext = { originLocation: "team-collection", requestID: requestIndex, @@ -3352,7 +3716,11 @@ const doExportOpenAPI = async (format: "json" | "yaml") => { } } -const shareRequest = ({ request }: { request: HoppRESTRequest }) => { +const shareRequest = ({ + request, +}: { + request: HoppRESTRequest | HoppGQLRequest +}) => { if (currentUser.value) { // opens the share request modal invokeAction("share.request", { @@ -3836,6 +4204,7 @@ const getErrorMessage = (err: GQLError) => { case "team_req/requests_not_from_same_collection": return t("request.different_collection") case "team/team_collections_have_different_parents": + case "team_coll/not_same_parent": return t("collection.different_parent") default: return t("error.something_went_wrong") diff --git a/packages/hoppscotch-common/src/components/documentation/Content.vue b/packages/hoppscotch-common/src/components/documentation/Content.vue index 7361eeb0588..677d8e1ff33 100644 --- a/packages/hoppscotch-common/src/components/documentation/Content.vue +++ b/packages/hoppscotch-common/src/components/documentation/Content.vue @@ -51,9 +51,9 @@ /> import { PropType, ref, onMounted } from "vue" -import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data" +import { + Environment, + HoppCollection, + HoppGQLRequest, + HoppRESTRequest, +} from "@hoppscotch/data" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { useRouter, useRoute } from "vue-router" +type DocRequest = HoppRESTRequest | HoppGQLRequest + type DocumentationItem = { id: string type: "folder" | "request" - item: HoppCollection | HoppRESTRequest + item: HoppCollection | DocRequest inheritedProperties: HoppInheritedProperty } +const getRequestDescription = (req: DocRequest): string => req.description || "" + const props = defineProps({ collectionData: { type: Object as PropType, diff --git a/packages/hoppscotch-common/src/components/embeds/GQLIndex.vue b/packages/hoppscotch-common/src/components/embeds/GQLIndex.vue new file mode 100644 index 00000000000..8624f539a4d --- /dev/null +++ b/packages/hoppscotch-common/src/components/embeds/GQLIndex.vue @@ -0,0 +1,126 @@ + + + diff --git a/packages/hoppscotch-common/src/components/embeds/GQLRequest.vue b/packages/hoppscotch-common/src/components/embeds/GQLRequest.vue new file mode 100644 index 00000000000..5452ad02e92 --- /dev/null +++ b/packages/hoppscotch-common/src/components/embeds/GQLRequest.vue @@ -0,0 +1,127 @@ + + + diff --git a/packages/hoppscotch-common/src/components/embeds/Request.vue b/packages/hoppscotch-common/src/components/embeds/Request.vue index dea18bb7b43..24cf2a1cc58 100644 --- a/packages/hoppscotch-common/src/components/embeds/Request.vue +++ b/packages/hoppscotch-common/src/components/embeds/Request.vue @@ -55,7 +55,7 @@ import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils" import IconSave from "~icons/lucide/save" import { Ref } from "vue" -import { computed, useModel } from "vue" +import { computed, onBeforeUnmount, useModel } from "vue" import { ref } from "vue" import { useI18n } from "~/composables/i18n" import { useToast } from "~/composables/toast" @@ -64,7 +64,7 @@ import { useStreamSubscriber } from "~/composables/stream" import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse" import { runRESTRequest$ } from "~/helpers/RequestRunner" import { HoppTab } from "~/services/tab" -import { HoppRequestDocument } from "~/helpers/rest/document" +import { HoppRequestDocument } from "~/helpers/tab/document" import { transformRequestVariablesToAggregateEnv } from "~/helpers/utils/environments" const toast = useToast() @@ -99,10 +99,16 @@ const newSendRequest = async () => { loading.value = true - const [cancel, streamPromise] = runRESTRequest$(tab) + // Viewer envs must not resolve into a shared request's execution + const [cancel, streamPromise] = runRESTRequest$(tab, { isolatedEnvs: true }) + // Store the cancel handle synchronously — `runRESTRequest$` returns it + // immediately, before the stream resolves. If we waited until after the + // `await` below, an unmount during that window would leave `onBeforeUnmount` + // with a null handle and leak the in-flight request. + requestCancelFunc.value = cancel + const streamResult = await streamPromise - requestCancelFunc.value = cancel if (E.isRight(streamResult)) { subscribeToStream( streamResult.right, @@ -186,4 +192,12 @@ const cancelRequest = () => { updateRESTResponse(null) } + +// Cancel any in-flight REST request when the embed iframe is destroyed +// (host navigation, SPA route change). Otherwise the runner stays +// subscribed to the response stream after the component is gone — small +// memory leak that grows if a user clicks between several embed links. +onBeforeUnmount(() => { + requestCancelFunc.value?.() +}) diff --git a/packages/hoppscotch-common/src/components/embeds/index.vue b/packages/hoppscotch-common/src/components/embeds/index.vue index f1460ab6a6d..230fe01e37f 100644 --- a/packages/hoppscotch-common/src/components/embeds/index.vue +++ b/packages/hoppscotch-common/src/components/embeds/index.vue @@ -14,11 +14,14 @@ :shared-request-u-r-l="sharedRequestURL" />
+
@@ -33,7 +36,7 @@ import { computed, useModel } from "vue" import { ref } from "vue" import { HoppTab } from "~/services/tab" -import { HoppRequestDocument } from "~/helpers/rest/document" +import { HoppRequestDocument } from "~/helpers/tab/document" import { platform } from "~/platform" import { RESTOptionTabs } from "../http/RequestOptions.vue" import { transformRequestVariablesToAggregateEnv } from "~/helpers/utils/environments" diff --git a/packages/hoppscotch-common/src/components/environments/Add.vue b/packages/hoppscotch-common/src/components/environments/Add.vue index 7f47f6d8b8b..bcdb49ca656 100644 --- a/packages/hoppscotch-common/src/components/environments/Add.vue +++ b/packages/hoppscotch-common/src/components/environments/Add.vue @@ -85,14 +85,14 @@ import { updateEnvironment, } from "~/newstore/environments" import { CurrentValueService } from "~/services/current-environment-value.service" -import { RESTTabService } from "~/services/tab/rest" +import { WorkspaceTabsService } from "~/services/tab/workspace-tabs" import { Scope } from "./Selector.vue" import { GlobalEnvironment } from "@hoppscotch/data" const t = useI18n() const toast = useToast() -const tabs = useService(RESTTabService) +const tabs = useService(WorkspaceTabsService) const currentEnvironmentValueService = useService(CurrentValueService) const props = defineProps<{ @@ -239,12 +239,18 @@ const addEnvironment = async () => { if (replaceWithVariable.value) { //replace the current tab endpoint with the variable name with << and >> const variableName = `<<${editingName.value}>>` - //replace the currenttab endpoint containing the value in the text with variablename - tabs.currentActiveTab.value.document.request.endpoint = - tabs.currentActiveTab.value.document.request.endpoint.replace( + const doc = tabs.currentActiveTab.value.document + if (doc.type === "request") { + doc.request.endpoint = doc.request.endpoint.replace( editingValue.value, variableName ) + } else if (doc.type === "gql-request") { + doc.request.url = doc.request.url.replace( + editingValue.value, + variableName + ) + } } hideModal() diff --git a/packages/hoppscotch-common/src/components/environments/index.vue b/packages/hoppscotch-common/src/components/environments/index.vue index 5f36045284d..29de810d6bf 100644 --- a/packages/hoppscotch-common/src/components/environments/index.vue +++ b/packages/hoppscotch-common/src/components/environments/index.vue @@ -165,33 +165,18 @@ const updateEnvironmentType = (newEnvironmentType: EnvironmentType) => { const workspace = workspaceService.currentWorkspace -// Switch to my environments if workspace is personal and to team environments if workspace is team -// also resets selected environment if workspace is personal and the previous selected environment was a team environment +// Switch to my environments if workspace is personal and to team +// environments if workspace is team. Resetting a stale team-env selection +// is handled by WorkspaceService.changeWorkspace — the single funnel every +// workspace switch goes through, mounted or not. watch( workspace, (newWorkspace) => { - const { type: newWorkspaceType } = newWorkspace - - if (newWorkspaceType === "personal") { + if (newWorkspace.type === "personal") { switchToMyEnvironments() } else { updateSelectedTeam(newWorkspace) } - - const newTeamID = - newWorkspaceType === "team" ? newWorkspace.teamID : undefined - - // Set active environment to the `No environment` state - // if navigating away from a team workspace - if ( - selectedEnvironmentIndex.value.type === "TEAM_ENV" && - newTeamID && - selectedEnvironmentIndex.value.teamID !== newTeamID - ) { - setSelectedEnvironmentIndex({ - type: "NO_ENV_SELECTED", - }) - } }, { immediate: true } ) diff --git a/packages/hoppscotch-common/src/components/gql/Argument.vue b/packages/hoppscotch-common/src/components/gql/Argument.vue new file mode 100644 index 00000000000..e1f85abf98d --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Argument.vue @@ -0,0 +1,103 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Arguments.vue b/packages/hoppscotch-common/src/components/gql/Arguments.vue new file mode 100644 index 00000000000..a3754033d20 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Arguments.vue @@ -0,0 +1,32 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Authorization.vue b/packages/hoppscotch-common/src/components/gql/Authorization.vue new file mode 100644 index 00000000000..e6f997180e0 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Authorization.vue @@ -0,0 +1,337 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/DefaultValue.vue b/packages/hoppscotch-common/src/components/gql/DefaultValue.vue new file mode 100644 index 00000000000..eb06d8594ab --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/DefaultValue.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Directives.vue b/packages/hoppscotch-common/src/components/gql/Directives.vue new file mode 100644 index 00000000000..52363fa5f4a --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Directives.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/DocExplorer.vue b/packages/hoppscotch-common/src/components/gql/DocExplorer.vue new file mode 100644 index 00000000000..26f2e11481b --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/DocExplorer.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/packages/hoppscotch-common/src/components/gql/EnumValues.vue b/packages/hoppscotch-common/src/components/gql/EnumValues.vue new file mode 100644 index 00000000000..b2fdc61869e --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/EnumValues.vue @@ -0,0 +1,95 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/ExplorerSection.vue b/packages/hoppscotch-common/src/components/gql/ExplorerSection.vue new file mode 100644 index 00000000000..c7b5a511f8a --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/ExplorerSection.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Field.vue b/packages/hoppscotch-common/src/components/gql/Field.vue new file mode 100644 index 00000000000..89a2561b82c --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Field.vue @@ -0,0 +1,78 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/FieldDocumentation.vue b/packages/hoppscotch-common/src/components/gql/FieldDocumentation.vue new file mode 100644 index 00000000000..be938775862 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/FieldDocumentation.vue @@ -0,0 +1,61 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/FieldLink.vue b/packages/hoppscotch-common/src/components/gql/FieldLink.vue new file mode 100644 index 00000000000..2d96250a6ed --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/FieldLink.vue @@ -0,0 +1,62 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Fields.vue b/packages/hoppscotch-common/src/components/gql/Fields.vue new file mode 100644 index 00000000000..6d2bc425fab --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Fields.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Headers.vue b/packages/hoppscotch-common/src/components/gql/Headers.vue new file mode 100644 index 00000000000..025efe7cd69 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Headers.vue @@ -0,0 +1,718 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/ImplementsInterfaces.vue b/packages/hoppscotch-common/src/components/gql/ImplementsInterfaces.vue new file mode 100644 index 00000000000..310a0c62583 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/ImplementsInterfaces.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Query.vue b/packages/hoppscotch-common/src/components/gql/Query.vue new file mode 100644 index 00000000000..16268d4d6d3 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Query.vue @@ -0,0 +1,300 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Request.vue b/packages/hoppscotch-common/src/components/gql/Request.vue new file mode 100644 index 00000000000..6f793db9f62 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Request.vue @@ -0,0 +1,297 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/RequestOptions.vue b/packages/hoppscotch-common/src/components/gql/RequestOptions.vue new file mode 100644 index 00000000000..da05931e5d6 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/RequestOptions.vue @@ -0,0 +1,428 @@ + + + + + diff --git a/packages/hoppscotch-common/src/components/gql/RequestTab.vue b/packages/hoppscotch-common/src/components/gql/RequestTab.vue new file mode 100644 index 00000000000..5f6d317ddc6 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/RequestTab.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Response.vue b/packages/hoppscotch-common/src/components/gql/Response.vue new file mode 100644 index 00000000000..3a92e707e9e --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Response.vue @@ -0,0 +1,498 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/ResponseMeta.vue b/packages/hoppscotch-common/src/components/gql/ResponseMeta.vue new file mode 100644 index 00000000000..dcd140d8a50 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/ResponseMeta.vue @@ -0,0 +1,182 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Schema.vue b/packages/hoppscotch-common/src/components/gql/Schema.vue new file mode 100644 index 00000000000..f61b6f15aa6 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Schema.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/packages/hoppscotch-common/src/components/gql/SchemaDocumentation.vue b/packages/hoppscotch-common/src/components/gql/SchemaDocumentation.vue new file mode 100644 index 00000000000..ba3b42cf8d9 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/SchemaDocumentation.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/SchemaSearch.vue b/packages/hoppscotch-common/src/components/gql/SchemaSearch.vue new file mode 100644 index 00000000000..7169a0d2cfb --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/SchemaSearch.vue @@ -0,0 +1,374 @@ + + + + + diff --git a/packages/hoppscotch-common/src/components/gql/SubscriptionLog.vue b/packages/hoppscotch-common/src/components/gql/SubscriptionLog.vue new file mode 100644 index 00000000000..a9f14852f40 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/SubscriptionLog.vue @@ -0,0 +1,133 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/TabHead.vue b/packages/hoppscotch-common/src/components/gql/TabHead.vue new file mode 100644 index 00000000000..585a8e0b623 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/TabHead.vue @@ -0,0 +1,164 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/TypeDocumentation.vue b/packages/hoppscotch-common/src/components/gql/TypeDocumentation.vue new file mode 100644 index 00000000000..6b1ed1e2e26 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/TypeDocumentation.vue @@ -0,0 +1,23 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/TypeLink.vue b/packages/hoppscotch-common/src/components/gql/TypeLink.vue new file mode 100644 index 00000000000..7a14bec0a77 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/TypeLink.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/Variable.vue b/packages/hoppscotch-common/src/components/gql/Variable.vue new file mode 100644 index 00000000000..e85aa80ced5 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/Variable.vue @@ -0,0 +1,180 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/example/Response.vue b/packages/hoppscotch-common/src/components/gql/example/Response.vue new file mode 100644 index 00000000000..9f00bc71acd --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/example/Response.vue @@ -0,0 +1,164 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/example/ResponseRequest.vue b/packages/hoppscotch-common/src/components/gql/example/ResponseRequest.vue new file mode 100644 index 00000000000..12b74019b4c --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/example/ResponseRequest.vue @@ -0,0 +1,174 @@ + + + diff --git a/packages/hoppscotch-common/src/components/gql/example/ResponseTab.vue b/packages/hoppscotch-common/src/components/gql/example/ResponseTab.vue new file mode 100644 index 00000000000..0dbb394e092 --- /dev/null +++ b/packages/hoppscotch-common/src/components/gql/example/ResponseTab.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/hoppscotch-common/src/components/graphql/Authorization.vue b/packages/hoppscotch-common/src/components/graphql/Authorization.vue index a19ff787ee6..bab059beb76 100644 --- a/packages/hoppscotch-common/src/components/graphql/Authorization.vue +++ b/packages/hoppscotch-common/src/components/graphql/Authorization.vue @@ -238,8 +238,7 @@ const selectOAuth2AuthType = () => { // @ts-expect-error - the existing grantTypeInfo might be in the auth object, typescript doesnt know that const existingGrantTypeInfo = auth.value.grantTypeInfo as - | HoppGQLAuthOAuth2["grantTypeInfo"] - | undefined + HoppGQLAuthOAuth2["grantTypeInfo"] | undefined const grantTypeInfo = existingGrantTypeInfo ? existingGrantTypeInfo diff --git a/packages/hoppscotch-common/src/components/graphql/Headers.vue b/packages/hoppscotch-common/src/components/graphql/Headers.vue index fc16f3d0e02..f673e53ab93 100644 --- a/packages/hoppscotch-common/src/components/graphql/Headers.vue +++ b/packages/hoppscotch-common/src/components/graphql/Headers.vue @@ -478,12 +478,10 @@ const updateHeader = (index: number, header: GQLHeader & { id: number }) => { const deleteHeader = (index: number) => { const headersBeforeDeletion = clone(workingHeaders.value) - if ( - !( - headersBeforeDeletion.length > 0 && - index === headersBeforeDeletion.length - 1 - ) - ) { + if (!( + headersBeforeDeletion.length > 0 && + index === headersBeforeDeletion.length - 1 + )) { if (deletionToast.value) { deletionToast.value.goAway(0) deletionToast.value = null @@ -699,4 +697,9 @@ const mask = (header: any) => { } const changeTab = () => emit("change-tab", "authorization") + +// No inspection wiring here: this component serves the legacy /graphql page, +// whose tabs live in GQLTabService — the InspectionService only inspects the +// unified workspace's tabs, so reading its results here would show warnings +// belonging to a different page's active tab. diff --git a/packages/hoppscotch-common/src/components/graphql/Request.vue b/packages/hoppscotch-common/src/components/graphql/Request.vue index ce06d5cf513..5993cdad408 100644 --- a/packages/hoppscotch-common/src/components/graphql/Request.vue +++ b/packages/hoppscotch-common/src/components/graphql/Request.vue @@ -65,9 +65,7 @@ import { platform } from "~/platform" import { useI18n } from "@composables/i18n" import { computed, ref, watch } from "vue" -import { connection } from "~/helpers/graphql/connection" -import { connect } from "~/helpers/graphql/connection" -import { disconnect } from "~/helpers/graphql/connection" +import { connection, connect, disconnect } from "~/helpers/graphql/connection" import { KernelInterceptorService } from "~/services/kernel-interceptor.service" import { useService } from "dioc/vue" import { defineActionHandler } from "~/helpers/actions" diff --git a/packages/hoppscotch-common/src/components/graphql/RequestOptions.vue b/packages/hoppscotch-common/src/components/graphql/RequestOptions.vue index 991cc8f57ff..2266a0f74cb 100644 --- a/packages/hoppscotch-common/src/components/graphql/RequestOptions.vue +++ b/packages/hoppscotch-common/src/components/graphql/RequestOptions.vue @@ -48,7 +48,7 @@ @@ -82,6 +82,10 @@ const _VALID_GQL_OPERATIONS = [ "headers", "variables", "authorization", + // Script tabs exist only on the unified workspace's gql/RequestOptions — + // this legacy page doesn't render them, but it owns the GQLOptionTabs type + "preRequestScript", + "tests", ] as const export type GQLOptionTabs = (typeof _VALID_GQL_OPERATIONS)[number] diff --git a/packages/hoppscotch-common/src/components/graphql/Response.vue b/packages/hoppscotch-common/src/components/graphql/Response.vue index a3be45c9a6f..bd5b2daac40 100644 --- a/packages/hoppscotch-common/src/components/graphql/Response.vue +++ b/packages/hoppscotch-common/src/components/graphql/Response.vue @@ -1,6 +1,6 @@