Background: in the environment variables table view and the generic key-value editor, a space typed within 1s of the previous printable character is swallowed (typing "My key" produces "Mykey"); users had to insert spaces via arrow keys afterwards. Raw JSON editing is unaffected.
The react-aria ListBox typeahead (useTypeSelect) intercepts space keydowns in the capture phase while its search buffer is non-empty. Disable typeahead via disallowTypeAhead on the ListBoxes that embed inline editors (environment key-value editor and the generic key-value editor), remove the now-obsolete space<->NBSP keydown workaround, and add a module augmentation for the prop that is missing from ListBoxProps typings in all published react-aria-components versions.
Add smoke tests typing at human speed (120ms per keystroke) in the environment table, headers, and query params views; zero-delay bursts do not reproduce the bug.
## Background
Toast add/remove animations run through the View Transition API. While such a transition is active, Chromium retargets hit-testing for the whole viewport to `<html>` - including the moment a newer transition skips the running one - so a click can arrive with `document.documentElement` as its target. react-aria's `useInteractOutside` reads such an event as a click outside an open modal and closes it.
Reproduction: open Settings → General → Software Updates and click **Check** repeatedly. Every click fires toasts, every toast mutation starts a view transition, and a click landing in one of those windows closed the Settings modal itself.
## Changes
- `packages/insomnia/src/ui/css/styles.css`: disable pointer events on the `::view-transition` overlay and exclude the root element from the transition, so clicks keep reaching real elements while a toast animates. Both rules are required: the overlay rule alone still leaves the captured root's contents unhittable ([spec rationale](https://drafts.csswg.org/css-view-transitions-1/#view-transition-stacking-layer)), and excluding the root alone still leaves the overlay swallowing the clicks. The same pair is documented for this scenario in [Keeping the page interactive while a View Transition is running](https://www.bram.us/2025/01/29/view-transitions-page-interactivity/) and [csswg-drafts#11596](https://github.com/w3c/csswg-drafts/issues/11596).
- `packages/insomnia/src/ui/components/toast-notification.tsx`: drop `pointerdown`/`click` events that were retargeted to `<html>` while a view transition is active. Those clicks cannot be attributed to their real target (hit-testing was suppressed by the browser) and were being misread as clicks outside an open modal. Events that land on real elements are untouched. Registration is guarded with `typeof window !== 'undefined'` because this module is also evaluated in Node (dev server, prerender).
- `packages/insomnia/src/ui/components/toast-notification.tsx`: remove the stale `[view-transition-name:toast]` utility, which the inline per-toast `view-transition-name` has always overridden.
- `packages/insomnia/src/ui/css/styles.css`: remove the `::view-transition-new(toast)` / `::view-transition-old(toast)` slide rules and their keyframes. They selected a group named `toast`, which never existed because toasts need unique names (a duplicate `view-transition-name` aborts the whole transition), so they never applied.
- `packages/insomnia/src/ui/components/check-for-updates-button.tsx`: the button was fully disabled in development mode, which made this bug impossible to reproduce manually. It now shows a toast instead ("Updates are disabled in development mode"). Production behaviour is unchanged.
## Notes
- Measured with real (CDP/`sendInputEvent`) input against the real components in Electron 43.2.0 / Chromium 150.0.7871.129: without the change a click during a toast animation hits `<html>` and closes the modal; with it the button keeps receiving clicks (25/17/12 clicks handled for 60/90/130ms click intervals) and the modal stays open.
- No regressions measured: clicks while no transition runs (20/20 handled), mask click dismissal, Escape dismissal, popover outside-click dismissal (also while a transition runs), and clicking a toast.
- Residual: hit-testing still returns `<html>` for the fraction of a click that lands inside a transition window (~10% of samples under rage clicking) - that is compositor behaviour and cannot be opted out. Such clicks are now dropped instead of dismissing the modal, which means that one click does nothing.
- `:root { view-transition-name: none }` applies to every view transition in the app; the rule is commented in place so it is revisited if transitions other than toasts are introduced.
Related: https://github.com/Kong/insomnia/pull/10553
## Background
The `value-bearing curl param` smoke test failed intermittently on CI
(run 35832135624, shard 3/6, both retries) while passing on isolated
reruns and in dev mode.
Repro: `npm run test:smoke:build -- --project=Smoke --grep "value-bearing curl param" --repeat-each=20 --workers=4`
→ ~25% first-attempt failure rate.
## Root cause
A value-bearing deep link auto-scans, and the scan result replaces the
pre-scan form within milliseconds of the modal opening. The assertion
`getByText('http://insomnia.rest/')` could only match the transient
pre-scan state — the raw cURL in the textarea's initial text content —
so the test raced against scan completion and lost whenever the scan
landed before the first assertion poll (fast in build mode on CI).
The stable post-scan state never contained the URL text at all: the
scan-results table renders
`{importerSign} resources to be imported from {oriFileName}:`, and the
cURL scan source sets no `oriFileName`, producing the dangling-colon
label "cURL resources to be imported from :".
## Changes
- `import-modal/shared.tsx` — omit the `from {oriFileName}` clause when
a scan result has no source name (cURL deep-link scan). Sources with a
real name (uri, file, clipboard, mcp) keep the existing wording.
- `import-deep-link.test.ts` — assert the stable post-scan state
("cURL resources to be imported:" and "1 Request") instead of the
transient pre-scan form; renamed the test accordingly.
## Verification
- Fixed test: 20/20 passed under the same build-mode + parallel-load
conditions that produced 5/20 flaky before the fix.
- `import deep links` suite: 3/3 passed in build mode.
## Background
Collection Runner → Upload Data parses CSV by splitting rows on commas, so a quoted field containing a comma (valid per RFC 4180, e.g. `"ORD,YVR"`) is split into two columns and every following column in that row shifts, producing wrong variable values for the rest of the iteration.
Repro: upload a CSV whose rows contain quoted comma fields (e.g. `origin,destination` rows with `"ORD,YVR"`) — the Data Preview table splits `"ORD,YVR"` into two columns and the runner sends wrong values.
## Changes
- Replace the naive `split(',')` CSV parsing with papaparse in both places that share the same copied implementation: app `upload-runner-data-modal.tsx` and inso `getListFromFileOrUrl`
- Handles quoted commas, escaped quotes (`""`) and newlines inside quoted fields; delimiter pinned to `,` to preserve previous behavior; `skipEmptyLines` stops a trailing newline from producing a bogus empty iteration row
- Existing semantics preserved: first row is variable names, missing trailing cells default to `''`, error messages unchanged, fewer than two rows still rejected
- Unit tests added for both code paths
## Issue
INS-3906
A public repository cannot call a reusable workflow in the internal
KSAI repository, so the setup workflow fails before any job starts
with "workflow was not found". Call the published snapshot instead,
pinned to the current release and following its consumer template,
with every command enabled and a review on each opened pull request.
The snapshot names no model gateway of its own, so the call passes the
organization's gateway variable. It runs on GitHub-hosted runners,
because the Kong runner pool serves no public repository, and with no
GitHub App at all: push_as_app is false and no key is passed, so every
read and write goes through GITHUB_TOKEN.
Signed-off-by: Bart Smykla <bartek@smykla.com>
* refactor(sidebar): own the project shell in one layout route
`/organization/:organizationId/project` and
`/organization/:organizationId/project/:projectId` each rendered their own copy
of the project shell — panel group, navigation sidebar, project modal — through
two sidebar components, `ProjectNavigationSidebar` and
`EmptyProjectNavigationSidebar`. Because the two are sibling route modules,
gaining or losing the last project in an organization swapped one for the other
and remounted the entire sidebar.
Anything the sidebar owned was dropped at that moment. Most visibly, a Konnect
sync started from an empty organization lost its AbortController as soon as it
created the first project: the Cancel button vanished, the sync kept running
invisibly, and disconnecting afterwards deleted projects the still-running sync
then recreated.
Introduce `organization.$organizationId.project.tsx` as a parent layout owning
the shell. flatRoutes nests both existing children under it automatically, so no
route files are renamed. The children keep only what needs the active project:
the git file issues provider and the outlet context. The two sidebars collapse
into one that already handled an empty project list.
Also move `KonnectSyncResultPanel` out of the `showKonnectSyncIntro` ternary's
else branch. It hosts the Konnect settings modal, and the intro branch's
"Configure" button is what opens it, so nesting it there made that button set
state nothing rendered — leaving an organization with Konnect projects but no
PAT unable to reconnect, across restarts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: refresh circular reference baseline
The layout refactor removed two cycles rooted at
`organization.$organizationId.project.$projectId.tsx`, which no longer imports
the navigation sidebar or the sync bar:
...project.$projectId.tsx -> project-navigation-sidebar.tsx -> project-node.tsx -> insomnia-event-stream-context.tsx
...project.$projectId.tsx -> sync-bar.tsx
`check-cycle-references` fails on baseline drift as well as on new cycles, so
the recorded baseline has to drop them. No cycles were added — the regenerated
file differs from the previous one by exactly these two deletions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(routes): drop unused project index loader data
`ProjectIndexLoaderData` was exported but referenced nowhere, and nothing read
this route's loader data. The returned `{ projects, projectsCount }` was also
unreachable as anything but `{ [], 0 }` — the loader redirects whenever the
organization has projects, so the only way to reach the return is with none.
Return null instead and drop the interface.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two independent papercuts in the circular reference check, both hit while
investigating a failure on another PR.
`npm run check-cycle-references` could not run on Windows at all. It spawned
the extensionless `node_modules/.bin/depcruise` shim through `execFileSync`,
which Windows cannot launch; the `.cmd` sibling does not help either, since
Node >=18 refuses to spawn `.cmd` without a shell (CVE-2024-27980). Resolve
dependency-cruiser's own entry point and run it with `process.execPath`
instead — no shell, works everywhere.
The check also fails on baseline drift (cycles recorded in the baseline that
no longer exist), which is correct: the baseline is a ratchet, and leaving a
fixed cycle in it would silently keep permitting its reintroduction. But CI
labelled every non-success outcome "New circular references detected", so a PR
that *removed* two cycles was reported as having added some. That cost real
debugging time.
Give the script distinct exit codes — 1 for a new cycle, 2 for drift alone —
and have the workflow capture and map them to separate messages. The failure
behaviour is unchanged; only the wording is now accurate. Renamed the final
step to match what it actually gates on.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
Adds an **Export OpenAPI Spec** item right under the existing **Export** option in the sidebar workspace dropdown — the context menu for an API collection (opened via the ellipsis trigger button or by right-clicking the workspace row).
Selecting it prompts for **YAML or JSON**, then a save location, and writes the spec file — the same flow as the existing Insomnia/HAR exporters (format modal → native save dialog → `writeFile`), reusing `showSaveExportedFileDialog` / `writeExportedFileToFileSystem` so the last export path is remembered.
Closes INS-3641
## Behavior details
- The item shows for collection/design scope workspaces. If the spec is empty or missing, clicking it shows a `Cannot export` alert explaining there is nothing to export, rather than writing an empty file.
- Same serialization as inso: matching format passes the contents through untouched; cross-format converts via `YAML.parse` + `JSON.stringify(…, 2)` / `YAML.stringify`, with an error modal if the spec is invalid.
- Records `dataExport` telemetry like every sibling exporter in the file.
- Spec format detection/conversion are shared with the Spec editor toolbar via new `detectApiSpecSyntax` / `convertApiSpecSyntax` helpers in `common/api-specs` (the toolbar previously inlined the same JSON-parse probe).
## Background
The environment editor's JSON view had two reproducible problems.
**1. Deleting all JSON content left the Table view unchanged**
1. Open an environment and configure several variables in **Table View**.
2. Switch to **JSON View**.
3. Delete all content in the JSON editor.
4. Switch back to **Table View**.
Expected: the variable entries are gone, since the JSON content is empty. Actual: every previously configured entry is still listed — the deletion is ignored.
**2. Switching routes and coming back dropped the error state**
1. Clear the JSON view: the editor shows `Unexpected end of JSON input` and switching to Table view is blocked by the error dialog.
2. Navigate to another route, then return to this page.
Expected: the state stays consistent. Actual: the error is gone and the empty document can be switched to Table view, which again shows the last saved variables.
**Root cause**
- `EnvironmentEditor.getValue()` short-circuited on an empty document (`if (!editorRef.current || !value) return null`), so `onChange` never fired: no error was recorded, nothing was submitted, and `isValid()` stayed `true`. On the JSON → Table switch, `useToggleEnvironmentType` then derived `kvPairData` from the unchanged `environment.data` — the last saved variables.
- Validity was push-only: it was refreshed exclusively from `CodeEditor`'s debounced `onChange`. On remount the `CodeEditor` restores unsaved content from its `historyKey` cache programmatically (cached-state `setValue` inside `initEditor`, before the `changes` listener is attached), so no `onChange` ran and the error silently vanished.
## Changes
**`packages/insomnia/src/ui/components/editors/environment-editor.tsx`**
- `getValue()` no longer special-cases empty content: an empty (or whitespace-only) document reaches `orderedJSON.parse`, which throws, so it takes exactly the same error path as any other malformed JSON (inline notice + `isValid() === false`).
- Validation is one `validate()` helper (parse + `checkNestedKeys`) shared by `onChange` and by a new `useEffect` that re-applies it on mount and whenever the incoming value or `historyKey` changes, so the notice and the validity follow the document instead of the last edit event.
- `isValid()` is derived from the document on demand, so it cannot lag the `CodeEditor`'s 100 ms `onChange` debounce.
- `getValue()` is documented as throwing on invalid content; every caller gates on `isValid()`, and `request-group-pane` additionally wraps it in `try/catch`.
**`packages/insomnia-smoke-test/tests/smoke/environment-editor-interactions.test.ts`**
- New regression test: clear the JSON content, assert the parse error surfaces and that switching to Table view is blocked by the existing error dialog; then close and reopen the editor and assert the error state survives the remount and the switch stays blocked.
## Other
- **Product decision**: an empty document is treated as invalid JSON and follows the existing malformed-JSON handling (inline notice + the existing dialog that blocks the switch). Interpreting it as `{}` was rejected because it would wipe every variable on a transient select-all + delete.
- **Behavior changes**: empty content reports an error and blocks the JSON ↔ Table switch; an invalid document keeps its error across remounts and route changes until it is fixed; `getValue()` may throw for invalid content.
- **Unchanged**: a top-level JSON value whose object is falsy (`false`, `0`, `""`) keeps its pre-existing silent no-op behavior — neither widened nor narrowed.
- **Verification**:
- The new test fails on the pre-fix source (missing error notice, switch not blocked, KV list still visible) and passes with the fix.
- `npm run test:smoke:dev -- environment-editor-interactions`: 6 passed.
- `npm run type-check` clean, `npm run lint` 0 errors, `npx prettier --check` clean.
- `npm test -w packages/insomnia`: only the pre-existing, unrelated `git-service-clone-folder-naming` failure, which also fails on clean `develop`.
[INS-3884](https://konghq.atlassian.net/browse/INS-3884)
Bump @rjsf/core, @rjsf/utils and @rjsf/validator-ajv8 from
6.0.0-beta.15 to 6.0.0-beta.19.
When an MCP tool's input schema contained a nested object property
(an object nested under another object) with `additionalProperties`,
clicking "Add Item" in the Parameter Builder corrupted the form data:
each add wrapped the whole object inside a new key named after the
parent property (e.g. `{parameters: {parameters: {parameters: ...}}}`),
which made adding chaotic and delete appear to stop working. This is an
upstream @rjsf bug present in 6.0.0-beta.15/16 and fixed from
6.0.0-beta.17 onward. Root-level objects were unaffected.
beta.19 is the last release before the RJSF template API change
(`idSchema` -> `fieldPathId`, `onAddClick(schema)` -> `onAddProperty`),
so this is a drop-in upgrade that fixes the bug without any changes to
the custom RJSF theme templates.
Claude-Session: https://claude.ai/code/session_01LmeRUopGfF8pSZtBxWXDkz
Co-authored-by: Claude <noreply@anthropic.com>
[INS-3885](https://konghq.atlassian.net/browse/INS-3885)
Fixes#10492
## Problem
Setting `Accept: text/event-stream` on a request whose active response is a plain HTTP response made the response pane read that HTTP body out of `response.bodyPath` as if it were the NDJSON event log of a stream. The parsed objects have no `_id`, so the events table's row key was `undefined` and react-aria threw `Could not determine key for item`; the pane's error boundary replaced the whole response pane with `Render Failure: ...` (plus the "Application Error" modal).
Repro (verified in the smoke harness): send a request that returns **compact single-line JSON** (`GET http://localhost:4010/pets/1` in the smoke echo server), then add `Accept: text/event-stream` to it. A pretty-printed body does *not* reproduce it — per-line `JSON.parse` fails in the main process first, the events list stays empty and the pane looks fine.
A streamed Event Stream response writes its NDJSON event log into `bodyPath`, but the ordinary HTTP send path stores a raw body in that same field. So what an event is has to be decided by the **content** — not by the request's Accept header (that only expresses the intent to stream) and not by a stored flag on the response (which would need a startup backfill and could never be removed).
## Fix
`curl.event.findMany` keeps only lines that parse to an event — an object carrying the fields every curl event has (`_id`, `requestId`, `type`) — and skips everything else:
- a JSON or HTML response body yields no events instead of keyless rows, so the table can no longer blow up;
- a line still being appended while a stream is open is skipped instead of rejecting the whole read;
- event logs written by older versions keep working, since the rule is about the data, not about when it was written.
No model, loader or pane changes: the request header keeps deciding which pane to show.
Switching to a workspace while the sidebar filter is non-empty crashed the renderer with `TypeError: d.trim is not a function` and left the workspace unreachable, because the filter is persisted per organization and therefore kept crashing on every render.
Trace: the sidebar `flatItems` memo calls `filterCollection`, which feeds raw doc fields into `fuzzyMatchAll`, whose `allText.filter(t => t && t.trim())` runs over `[doc.name, doc.description ?? '', doc.url ?? '']`. The `?? ''` only guards null and undefined, so a single doc whose `name`, `description` or `url` holds a truthy non-string took down the whole sidebar.
Our Postman importer produced exactly that: Postman allows `description` to be raw text or an object holding the text and its format, and the folder, request and header paths stored the object verbatim. The collection-level path already had a `typeof === 'string'` guard, and the `@ts-expect-error` above `importItems` was masking the type error this caused.
- `common/collection.ts`: only strings are searchable text, so corrupted docs can no longer crash the sidebar, they simply cannot match.
- `importers/importers/postman.ts`: unwrap the object form for collection, folder, request and header descriptions, so docs only ever hold text.
showKonnectMovedNotice only checked settings.hasKonnectPat, which is
already true by the time a multi-organization migration conflict is
detected (the user could only have synced Konnect under several
organizations with a PAT already configured). Since the conflict check
in detectKonnectOrgMigration runs asynchronously in a separate effect,
the notice's condition was satisfied before that effect resolved,
so the "Konnect control planes have moved" popover could appear
before, or at the same time as, the migration modal that asks the user
which organization's data to keep — and since the popover renders at a
higher z-index than the modal's backdrop, it could visually sit on top
of an unresolved choice.
Add an explicit hasCheckedMigrationConflict flag (konnectMigrationGroups
being empty already doubles as "resolved, no conflict", but is also the
initial state before the check has run, so it can't disambiguate the
two on its own) and require it before showing the notice, so the popover
only ever appears once the conflict is either absent or has just been
resolved through the modal.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
`getInitialEntry()` restores `lastVisitedOrganizationId` on startup and
resolves an initial pathname for it, entirely independent of the
Konnect visibility state that `refreshKonnectAccess()` (called right
before it in entry.client.tsx) just recomputed. When the Konnect
organization has no projects, `getInitialRouteForOrganization` falls
back straight to `/organization/<id>/project` — a leaf route whose own
loader was never wired up with the `getKonnectOrganizationEscapeRoute`
check (only the org-index loader and the project-delete action were).
So an account that loses Konnect access (or synced projects) while the
app is closed reopens with the URL still pointing at Control Planes,
even though the organization has already disappeared from the
dropdown.
Reuse `getKonnectOrganizationEscapeRoute` here too: when the resolved
organizationId is the Konnect organization, re-validate it before
computing a route inside it, and land on the account's first real
organization instead if it just went dark.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
updateAppDataOnDbChanges only added a project's *new* parentId to the
revalidation set on an `update` event, never its previous one. Any
organization whose project list was already cached before the move
(staleTime: Infinity, so nothing refetches without an explicit
invalidation) kept showing the project until the whole renderer
restarted and rebuilt the query client from scratch.
This is pre-existing code (introduced in #10333, "Add app data service
and enhance the sidebar cache logic", before the Control Planes work)
and nothing before the Konnect migration ever moved a project between
already-populated organizations, so the gap was never exercised until
now. Fixed by also scanning cached organization-data queries for the
project's previous organization and invalidating that too, mirroring
the existing origin/destination handling for a collection child moving
between workspaces a few lines below.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Move Konnect projects into a global Control Planes organization
* fix: break new circular dependency in Konnect organization utils
useKonnectOrganization() imported useRootLoaderData from ~/root to read
accountId, but organization-utils.ts is itself reached from root.tsx via
the settings modal chain, closing a new import cycle flagged by CI's
dependency-cruiser check. Pass accountId in from the caller
(useOrganizations), which already has it, instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Konnect moved onboarding nudge.
* Can sync when the konnect org is empty.
* fix: guard two develop-introduced hooks against the Konnect organization
use-organization-storage-rule.ts and use-remote-files.ts's
useRemoteBackendProjects were added by develop's account-data refactor
after this plan's original API-guard audit, and both issued real
network/IPC calls for the local-only Konnect organization:
- useOrganizationStorageRule called the raw API directly and only
checked isScratchpadOrganizationId, missing Konnect. Fixed by
delegating to fetchAndCacheOrganizationStorageRule (which already
returns the correct local-only rules with no network call) instead
of gating with `enabled`, since a naive gate would fall back to the
permissive DEFAULT_STORAGE_RULES and surface Cloud Sync/Git Sync
inside Control Planes.
- useRemoteBackendProjects had no organization guard at all, firing
window.main.sync.remoteBackendProjectsOfTeam for an org whose
projects never have a remoteId. Fixed with an isLocalOrganizationId
check on its `enabled` gate.
Re-audited every other organizationId-scoped API guard in the plan;
no further gaps found. Plan doc updated with both findings.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: disable Connect & Sync in Konnect settings modal without entitlement
The modal let a user validate and store a PAT even when the account
lacks the Konnect control-planes entitlement, even though syncing
could never run in that state — the sidebar's Sync button was already
disabled here but the modal's own Connect & Sync button was not.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: re-fetch Konnect entitlements after logging back into the same account
refreshKonnectAccess deduped by accountId, but signing in never
reloads the renderer, so its module-level guard survives a logout —
and a fresh login into the same account keeps the same accountId,
silently skipping the re-fetch of /v1/user/entitlements.
Key the guard on sessionId instead: a new login always mints a new
session token even for the same account, so this still dedupes the
normal cold-start case (startup call and post-login loader share one
session) while correctly re-resolving after logout/login.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: navigate away when the Konnect organization becomes invisible
Deleting a user's last Konnect project (one at a time via the project
delete action, or in bulk via Disconnect) could leave them stranded on
a URL for an organization that no longer appears in the dropdown, with
no automatic navigation elsewhere.
Add getKonnectOrganizationEscapeRoute(organizationId), which re-checks
visibility with a fresh local project count (no network call — only
that half can change from a plain NeDB delete) combined with the
last-resolved entitlement, updates the shared access store so
useOrganizations() reflects it immediately, and returns the account's
first real organization to redirect to when the org just went dark.
Call it from both places that can remove the last Konnect project:
- organization.$organizationId._index.tsx's loader, which every
"no reachable project" fallback already redirects through
- organization.$organizationId.project.$projectId.delete.tsx's
action, which previously short-circuited straight back into the
same (now invisible) organization instead of going through that
loader
Extracted the shared "recompute + store update" logic into
reconcileKonnectAccess() to avoid duplicating it between
refreshKonnectAccess() and the new escape-route check.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Migrate after login
* Force fetch storage rule
* Replace Konnect sync module-var trigger with event bus
Swaps the register/run module variable in konnect-sync-trigger.ts for
uiEventBus so the sync callback is subscribed/unsubscribed via useEffect
instead of being reassigned on every render with no cleanup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove plan
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
expectWorkspaceActive polled focus mode and the tree's aria-selected state through
separate locator lookups:
if (await this.isWorkspaceFocused(workspaceName)) return true;
const gridItem = this.workspaceGridListItem(workspaceName);
if ((await gridItem.count()) === 0) return false;
return (await gridItem.getAttribute('aria-selected')) === 'true';
count() returns immediately, but getAttribute() retries until its selector matches. Focus
mode swaps the collection's tree row for the back-arrow header, so when that swap landed
between the two calls the second lookup waited for a row that never comes back. The
predicate never returned, so expect.poll never evaluated again and died on its own 25s
timeout, while the page snapshot taken at failure showed a healthy focused sidebar.
The fallback branch could not save it either: getSelectedItemId returns the open request
for a collection, so the collection's own row is never aria-selected while a request inside
it is open. That left the check unable to pass on any route that opens a request, i.e. every
state this spec reaches.
Wait for the focus header and then match the collection title, using web-first assertions
that re-query on every retry instead of one blocking element read. That also removes the
reload fallback added in #10489, which papered over the same hang, and the now-unused
isWorkspaceFocused helper.
* fix: limit cookie template rendering to manually-set cookies and narrow template file access
* fix: only expand nested templates for variable substitution, not direct tag output
* test: cover OAuth2 access token exposure via the request tag for nested-template regression
* sec: exclude response-sourced cookies from WebSocket/Socket.IO connect rendering
* test: fix mTLS/cert smoke tests relying on secure-read-file path-prefix bug
* test: scope mtls smoke test locator to avoid strict-mode collision
* sec: close hard-link bypass of reserved NeDB database file check
* fix: avoid doubled path separator when allowlisted folder is a filesystem root
* test: add reload fallback for sidebar-focus-onboarding flake
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: update sidebar-focus-onboarding test strategy to use runIndex and simplify artifact naming
* test: retry the reload fallback once more for sidebar-focus-onboarding
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: update sidebar-focus-onboarding test strategy to use sharding and restore previous run state
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Summary
- Replaces the madge-based `check-cycle-references` with a `dependency-cruiser`-based check, cruised once per npm workspace package.
- Keeps the checker, dependency-cruiser config, and known-cycle baseline together under `scripts/circular-references/`.
- Uses a committed baseline to grandfather existing cycles while failing on new cycles and stale baseline entries.
- Cleans the existing `insomnia-inso` type cycle by using `insomnia-data`'s `AllTypes` instead of importing the database container type.
- CI explicitly uses Bash for correct `tee`/pipeline failure propagation, safely handles skipped checks, and posts a PR report.
- Dropped `madge`/`.madgerc`, added `dependency-cruiser`.
add initial settings for legacy test
add initial check for legacy unit test suites
show dropdown
remove spec route and merged with default debug page
showing document as collection
remove document term in UI
fix lint issue
fix ut failures
## Problem
In the environment editor, double-clicking an environment name (sidebar or header) to rename it and pressing Enter could freeze the entire page: nothing responded to clicks or keyboard until pressing Escape. Root cause found empirically (instrumented focus trace + CPU profile + pause stacks) and confirmed against the installed library source.
## Root cause
Committing the rename unmounts the focused input **while the Enter keypress is still in flight**. `FocusScope`'s `restoreFocus` (async rAF) then raced the RAC collection correction and landed DOM focus on a draggable row — the KV editor's trailing blank row — before the same keypress's keyup fired. `@react-aria/dnd`'s keyboard-drag handler (`useDrag.mjs` `onKeyUpCapture`: `if (target === currentTarget && key === 'Enter') startDragging(...)`) read that keyup as a drag release and started a `DragSession`, which:
- `ariaHideOutside`-hides everything on the page except the drag source/drop targets, and
- capture-phase `preventDefault`s every `focusin`/`focusout`/`mousedown`/`keydown`.
Result: the whole app is inert until Escape (the session's only user-facing exit). CPU profile showed the renderer ~92% idle — the page wasn't busy, it was locked.
## Fix
- **`EditableInput`**: drop `restoreFocus` from the `FocusScope`. On commit, focus settles on `document.body`, the collection stays unfocused, and the keyboard drag can never be armed (mechanism removed, not mitigated).
- **`EditableInput`**: `settledRef` guard so Enter/Escape/blur commit exactly once — the unmount blur previously fired `onSubmit` a second time (double mutation + double revalidation per rename). Side effect: **Escape now truly cancels** — previously the unmount blur committed the typed value after Escape.
- **`OneLineEditor`**: the `ensureFocus` rAF loop bails when the CodeMirror wrapper is detached from the DOM, so a stale loop from a remount cannot steal focus into a mid-teardown editor.
## Known behavior change
After committing/canceling a rename, focus settles on the document body instead of returning to the trigger element. Keyboard users need to Tab back into the tree/list. `restoreFocus` could not be kept without re-arming the freeze (the drag is armed precisely by focus landing on a draggable row mid-keypress); a safe follow-up would be restoring collection position only after the keyup completes.
* feat(sync): allow deleting unsynced remote files from the project dashboard
Unsynced workspace cards now reveal a trash icon on hover that opens a
confirmation dialog before permanently archiving the remote backend project.
Adds archiveBackendProject(backendProjectId) to insomnia-vcs so a project can be
archived without being pulled locally first, exposes it through the global sync
IPC bridge, and reuses it from VCS.archiveProject. The local meta.json cleanup is
now guarded by hasItem, since a never-pulled project has no local meta file.
* test(smoke): disambiguate Delete dropdown locator in cloud-sync test
* test(smoke): cover deleting an unsynced remote file from the project dashboard
Builds each hint row with createElement/textContent/title assignment
rather than a templated HTML string, treating hint values as text
content rather than markup.
[INS-3702](https://konghq.atlassian.net/browse/INS-3702)
## Summary
- Extracts a JSONPath from each streaming SSE message and concatenates it into one readable transcript, shown in a new "Summary" tab next to Events — useful for debugging LLM streaming APIs (OpenAI, Anthropic, Gemini) without scrolling through raw chunks.
- Auto-infers the JSONPath from the request URL for known provider endpoints (OpenAI Chat Completions/legacy Completions/Responses, Anthropic Messages, Google Gemini streamGenerateContent), matched by pathname only (not full URL/host), so self-hosted or reverse-proxied endpoints that keep the same path shape still match. Falls back to a manually-editable path otherwise.
- Handles curl's SSE event log being chunk-granular rather than frame-granular (a JSON payload can span multiple raw network reads, or multiple SSE frames can land in one chunk) by reconstructing the raw wire text and re-parsing `data:` frames properly.
- Handles Gemini's default `streamGenerateContent` response (no `?alt=sse`), which is a single top-level JSON array with no `data:` frames and no blank lines between elements, by scanning the array's bracket/brace depth by hand and pulling out whichever elements have fully closed so far — including mid-stream, before the array's closing `]` (or even the last element's own closing `}`) has arrived — so the summary renders progressively instead of only after the full response finishes.
- Summary tab defaults to selected when the URL matches a known provider, otherwise Events stays default. Plain text/Markdown render toggle for the summary output.
- Scoped to `curl` (SSE) responses only — WebSocket, `socketIO`, and `mcp` responses are untouched.
## Problem
When editing a project environment in Table View with no key/value entries, the trailing blank row could freeze the UI after renaming the environment in the header and immediately clicking the blank `Input Name` field.
The blank row was simultaneously managed by React Aria's `ListBox autoFocus="last"`, an imperative `ListBoxItem.onFocus` handoff, and `OneLineEditor` autofocus/retry logic. In the single-item boundary case these focus owners could race with `FocusScope` restoration and async environment revalidation.
## Fix
- Keep the trailing blank row inside the existing React Aria `ListBox` so row semantics, styling, keyboard navigation, drag-and-drop, and state behavior remain unchanged.
- Remove `ListBox autoFocus="last"` and the blank-row `ListBoxItem` focus handoff.
- Give the blank row's `OneLineEditor` one-shot autofocus on initial editor mount. This works for both empty and non-empty lists and is cleared after the initial autofocus.
- Track editor refs by distinct name/value editor ids.
- Restore focus once to the editor that was active when a blank row was converted into a persisted pair, after the persisted data update has mounted the new editor.
This prevents repeated focus stealing while preserving focus through the blank-row-to-persisted-row transition.