API Client: offline Monaco, data-loss and hang fixes, render perf, folder collections, Insomnia/Bruno import - #310
Open
itsmeakhil wants to merge 9 commits into
Open
API Client: offline Monaco, data-loss and hang fixes, render perf, folder collections, Insomnia/Bruno import#310itsmeakhil wants to merge 9 commits into
itsmeakhil wants to merge 9 commits into
Conversation
Open a folder as a collection: requests stored as YAML files
(NNN- prefix = order, dirs = folders, collection.yaml manifest),
so collections can be committed, diffed and PR-reviewed. Filesystem
is the store — mutations diff the serialized tree and write only
changed files; window focus re-reads external edits. Secrets stay
out of files ({{vault.*}} tokens pass through; environments never
serialized). Desktop only: tauri fs/dialog/persisted-scope plugins
plus a recursive fs-scope grant command for picked dirs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… loss - Serve Monaco from the app bundle (scripts/copy-monaco.mjs → public/monaco/vs, loader.config in lib/monaco-loader.ts) instead of jsdelivr; every editor was stuck on "Loading editor..." offline. Bump monaco-editor to 0.55.1 — the 0.53 AMD build throws in language/css/monaco.contribution.js. - Drop the stale Firebase-key gate from build-tauri.mjs (auth was removed). - Script runner: 5 s hard timeout with worker terminate + respawn; a top-level infinite loop no longer hangs the tab forever. - Tabs persistence: strip refetchable graphqlSchema always; on quota overflow retry with example bodies + file bytes stripped instead of wiping all tabs. - History: strip uploaded file bytes before POST; SQL ORDER BY/LIMIT instead of loading every row; clear/delete hard-delete rows (sync is gone, tombstones only accumulated). - Rust proxy: pooled reqwest clients (TLS/keep-alive reuse) and a 50 MB response body cap with an error envelope instead of unbounded buffering. - generate-code.ts: replace raw NUL byte in Go escaper regex with \0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…ors mounted
- RequestTabs + ResponsePanel are React.memo; the nine inline
`(v) => updateActiveTab({...})` setters become one memoized setter map,
onSaveExample/onMount/onChange handlers are stable.
- handleSend/handleCopyCurl go through latest-value refs so RequestPanel's memo
holds and the keydown listener stops re-registering on every edit;
handleLoadRequest/handleCancel/handleCurlPaste/example handlers memoized.
- Response JSON parsed once (useMemo) instead of three times per render for
the tree/table/geo views; those views cap at 200 children / 500 rows.
- Desktop layout keeps the request panel in one ResizablePanelGroup and adds
the response panel conditionally (id/order) — Send no longer remounts Monaco.
Body / pre-request / tests editor tabs are forceMount + hidden when inactive.
- History list row renderer memoized so VirtualHistoryList's memo holds;
TabBar's layout effect keys on tab id/kind/method/name, not the tabs array.
- sendProxyRequest() returns the Rust envelope directly instead of
stringify → fake Response → json(); typed ProxyEnvelope.
- Secret-variable scan covers params/headers/auth/body only, not the whole tab
(previous response + example bodies).
- Scripts worker caches pm.response.json() per run.
- next/dynamic for GrpcPanel (protobufjs), WebSocketPanel, ImportDialog
(js-yaml), CookieJarDialog, MetricsDialog, CollectionRunnerDialog; exporters
are imported on click.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…llections Brings the folder-of-YAML collections (file-store.ts, collection-files.ts, move-to-vault.ts, Rust fs/dialog/persisted-scope plugins, registry commands) onto the current main. Conflict resolution keeps main's auth removal — the branch's deep-link plugin, auth_server command and cookie_store deps stay deleted — and main's reqwest 0.13 / base64 0.23. The "Open folder as collection" button moves into ToolSidebarActions, where main relocated the sidebar header controls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Postman and Insomnia comparison pages stopped conceding mock servers, collections, scripting and the runner — all shipped. They now list REST, GraphQL, WebSocket, native gRPC, the local mock server, runner, vault-backed secrets and import/export formats, and concede only team/cloud workspaces, a CLI runner and Windows/Linux builds. New /compare/mydevtools-vs-bruno and /compare/bruno-alternative pages; api-client metadata description/keywords and the landing capability blurb describe the real scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
The Postman environment importer existed but nothing called it. The import
dialog now detects `{ name, values[] }` payloads and creates the environment;
the environment manager gets a per-environment Postman export button.
i18n key ApiClient.environmentManager.exportPostman in all 27 locales.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…in palette entry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Two importers for the switcher path, both pure libs with tests:
- Insomnia (`lib/import/insomnia.ts`): v4 JSON resources and v5 YAML, nested
groups, every body/auth type we support, `{{ _.VAR }}` → `{{VAR}}`,
pre-request/after-response scripts, environments. Round-trips our own
Insomnia v4 exporter.
- Bruno (`lib/import/bruno.ts`): the legacy `.bru` language and v4
OpenCollection YAML. Folder trees with `folder.bru` defaults, `seq`
ordering, `~key` disabled rows, json/text/xml/form/multipart (`@file(...)`)
/graphql bodies, basic/bearer/apikey/oauth2 auth, scripts, and
`environments/*` (secret var names come in with empty values).
Detection covers both, and the import dialog now creates the environments an
import brings with it instead of dropping them. 22 new tests.
Still folder-only from a paste of a single file: picking a whole Bruno
directory needs a desktop folder-read pass (readCollectionFiles is .yaml-only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| export function importInsomniaWithMeta(text: string): InsomniaImportResult { | ||
| const t = text.trim() | ||
| // yaml.load also parses JSON, so v4 YAML exports come for free. | ||
| const doc = (t.startsWith("{") ? JSON.parse(t) : yaml.load(t)) as |
ThreatCrush Security Scan1 finding(s) MEDIUM: 1
Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this change?
API Client work in four parts: fix the things that lose data or break offline, stop the per-keystroke re-render of the whole editor, land the git-friendly folder collections that were stranded on an unmerged branch, and add Insomnia + Bruno + Postman-environment import.
It started as a gap analysis against Bruno v4.1 and turned into the first two phases of the plan that came out of it.
Closes #
Why?
Three of these were quietly serious:
loader.configanywhere inapps/desktop-uiandcspisnull, so@monaco-editor/loaderused its CDN default. With no network every code editor in the app — request body, scripts, response, and the ~15 other tools usingCodeEditor— sat on "Loading editor..." forever.monaco-editorwas already inpackage.jsonand never imported. "Fully offline" was not true.api-client-tabslocalStorage key; a 2 MB response blew the 5 MB quota and thecatchdeleted the whole key.while (true)in a pre-request script hung the tab permanently. The 3s script budget was only checked insidepm.test(), and Cancel aborted the fetch, not the worker.Plus:
git-friendly folder-backed collections(de5aa0a3) had been sitting onapi-tester-improvements, unmerged, since July — that feature is our answer to Bruno's core pitch.Type of change
What's in it, commit by commit
c50bd6e7— trust blockersscripts/copy-monaco.mjscopiesmonaco-editor/min/vs→public/monaco/vs(gitignored, runs frompre{dev,build,dev:tauri}),lib/monaco-loader.tspoints the loader at it.monaco-editorbumped 0.53 → 0.55.1 — the 0.53 AMD build throwsProperty description must be an objectinlanguage/css/monaco.contribution.js.graphqlSchema(refetchable) always stripped; on quota overflow, retry once with example bodies and uploaded file bytes stripped instead of dropping every tab.ORDER BY … LIMITin SQL instead of loading every row and sorting in Rust; clear/delete now hard-delete (sync is gone, tombstones only accumulated).reqwestclients (was one per request — a TLS handshake every send), 50 MB response cap with an error envelope instead of unbounded buffering.NEXT_PUBLIC_FIREBASE_API_KEYhard-fail frombuild-tauri.mjs— leftover from the removed auth, it blocked builds for a feature that no longer exists.26282f74— render performanceRequestTabsandResponsePanelmemoized; the nine inline(v) => updateActiveTab({...})props replaced with one memoized setter map; stableonMount/onChange/onSaveExample.handleSendandhandleCopyCurlgo through latest-value refs, soRequestPanel's memo holds and the window keydown listener stops being removed/re-added on every keystroke.tryParseJsonwas called inline in three eagerly-evaluated JSX props).ResizablePanelGroupand the response panel joins it conditionally; body/pre-request/tests tabs areforceMount+ hidden.TabBar's measure effect no longer invalidate on every tab-array identity change.sendProxyRequest()returns the Rust envelope directly instead ofstringify→ fakeResponse→.json()(two extra full passes over the body on the UI thread).{params, headers, auth, body}instead ofJSON.stringify(activeTab)— it was dragging the previous response and every saved example through a regex on each send.next/dynamicfor GrpcPanel (protobufjs), WebSocketPanel, ImportDialog (js-yaml), runner, metrics, cookie jar; exporters imported on click.711f3bd4— mergeapi-tester-improvementsFolder-of-YAML collections (
file-store.ts,collection-files.ts,move-to-vault.ts, Rust fs/dialog/persisted-scope plugins). Conflict resolution keepsmain's auth removal — the branch'sdeep-linkplugin,await_browser_authcommand andcookie_storedeps stay deleted — andmain's reqwest 0.13 / base64 0.23. The "Open folder as collection" button moved intoToolSidebarActions, wheremainhad relocated the sidebar header controls.58aaf5bc— marketing copyThe Postman and Insomnia comparison pages were still conceding mock servers, collections and test automation to Postman — all of which ship. Rewritten to what's real, conceding only team/cloud workspaces, a CLI runner and Windows/Linux builds. Added
/compare/mydevtools-vs-brunoand/compare/bruno-alternative.5ce3275a,ab1b1d6b— imports{{ _.VAR }}→{{VAR}}, scripts, environments. Round-trips our own Insomnia v4 exporter..brulanguage and v4 OpenCollection YAML — folder trees withfolder.brudefaults,seqordering,~keydisabled rows, json/text/xml/form/multipart (@file(...))/graphql bodies, basic/bearer/apikey/oauth2, scripts,environments/*.How did you verify it?
cd apps/desktop-ui && pnpm test→ 135 suites, 1267 passed, 2 skipped. 31 of those tests are new (tab slimming, Postman env round-trip, 13 Insomnia, 9 Bruno, 2 detection)../node_modules/.bin/tsc --noEmit -p .in bothapps/desktop-uiandapps/web→ clean.cargo checkandcargo testinapps/desktop/src-tauri→ 54 passed. One existing test (history_limit_and_clear) started failing on the new SQL ordering because three rows land in the same millisecond; fixed with arowid DESCtiebreak and the test now also asserts that clear actually empties./app/format-converter, confirmed the editor renders and that the network log shows/monaco/vs/loader.js,/monaco/vs/editor/editor.main.js, … and zero jsdelivr requests.Screenshots
Not attached — the visible UI is unchanged apart from the sidebar's "Open folder as collection" button and an export icon in the environment manager. The perf work is deliberately invisible.
Checklist
pnpm lintpasses — see note belowcd apps/desktop-ui && pnpm test), and logic changes have a testuseTranslations— the one new string (ApiClient.environmentManager.exportPostman) is in all 27 localesmessages/en.json(inserted into all 27 locales; JSON re-validated)try/catchwithtoast.error(message)On lint: the repo has ~360 pre-existing ESLint errors and lint is deliberately out of CI. I compared the touched files before and after: same three pre-existing errors (two
react-hooks/purityonDate.now()in render, oneset-state-in-effectintabs-context), no new ones. I did fix the two I introduced mid-work (ref writes during render → effects) rather than leave them.Anything reviewers should know?
ResizablePanelGroupchange inapi-client.tsx. Panels now get explicitid/orderbecause the group's membership is conditional; if the response panel's saved size behaves oddly on your machine, that's where it is.readCollectionFilesinlib/desktop/collection-files.tsto read.bru/.yml/.json— it is.yaml-only today.importBrunoFolderalready takes the flat{path, text}[]that pass would produce.git logis not linear —711f3bd4merges a five-week-old branch. Review withgit diff main...HEADrather than commit-by-commit if the merge noise gets in the way.proxy.rs, the GraphQL subscriptions client, the OpenAPI spec watcher, cookie-jar export, and the backend's drag-and-dropmoveop. Not in this PR — worth their own.🤖 Generated with Claude Code
https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH