Spike: Support marking expensive loaders and skip unnecessary revalidation by actions submit #10064
Spike: Support marking expensive loaders and skip unnecessary revalidation by actions submit #10064cwangsmv wants to merge 2 commits into
Conversation
✅ Circular References ReportGenerated at: 2026-06-11T07:21:53.049Z Summary
Click to view all circular references in PR (9)Click to view all circular references in base branch (9)Analysis✅ No Change: This PR does not introduce or remove any circular references. This report was generated automatically by comparing against the |
There was a problem hiding this comment.
Pull request overview
This PR spikes scoped loader revalidation for the Insomnia React Router data routes, by injecting a reserved “revalidate scopes” field into action submissions and adding shouldRevalidate implementations to key loaders to skip unnecessary revalidation.
Changes:
- Add scoped revalidation helpers (
createShouldRevalidateByScopes,addScopeField) inutils/router.ts. - Introduce
shouldRevalidateexports on root/organization/project/workspace/request routes keyed off submission scopes. - Update multiple action submitters to include the reserved scopes field; trim some loader-returned data (e.g. root no longer returns
workspaceCount, workspace loader drops mock server/api spec lookups).
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/insomnia/src/utils/router.ts | Adds scope extraction + shouldRevalidate factory + scope injection helper. |
| packages/insomnia/src/ui/components/settings/import-export.tsx | Stops relying on root workspaceCount, fetches count client-side. |
| packages/insomnia/src/ui/components/modals/workspace-settings-modal.tsx | Removes mockServer prop and loads it lazily via services. |
| packages/insomnia/src/ui/components/dropdowns/workspace-dropdown.tsx | Stops passing activeMockServer into workspace settings modal. |
| packages/insomnia/src/routes/settings.update.tsx | Injects root revalidate scope into settings update submission. |
| packages/insomnia/src/routes/organization.tsx | Adds scoped shouldRevalidate + (debug) loader logging. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.tsx | Adds scoped shouldRevalidate + (debug) loader logging. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.tsx | Adds scoped shouldRevalidate, trims loader payload + (debug) loader logging. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.environment.set-active.tsx | Adds scope field to submission for workspace-level env changes. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.environment.set-active-global.tsx | Adds scope field to global env submission (FormData path). |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.tsx | Adds scoped shouldRevalidate + (debug) loader logging. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send.tsx | Injects request scope into send action submission. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.response.delete.tsx | Injects request scope into response delete submission (JSON). |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.response.delete-all.tsx | Injects request scope, but request encoding is inconsistent. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.connect.tsx | Injects request scope into connect submission. |
| packages/insomnia/src/routes/import.scan.tsx | Injects empty scope list into import scan submission (FormData). |
| packages/insomnia/src/routes/cloud-credentials.create.tsx | Injects root scope into create cloud credential submission. |
| packages/insomnia/src/routes/cloud-credentials.$cloudCredentialId.update.ts | Injects root scope into update cloud credential submission. |
| packages/insomnia/src/routes/cloud-credentials.$cloudCredentialId.delete.ts | Injects root scope into delete cloud credential submission. |
| packages/insomnia/src/routes/auth.validate-vault-key.tsx | Injects root scope into validate vault key submission. |
| packages/insomnia/src/routes/auth.update-vault-salt.tsx | Injects root scope into vault salt update submission. |
| packages/insomnia/src/routes/auth.reset-vault-key.tsx | Injects root scope into reset vault key submission. |
| packages/insomnia/src/routes/auth.logout.tsx | Injects root scope into logout submission (JSON). |
| packages/insomnia/src/routes/auth.login.tsx | Injects root scope into login submission. |
| packages/insomnia/src/routes/auth.create-vault-key.tsx | Injects root scope into create vault key submission. |
| packages/insomnia/src/routes/auth.clear-vault-key.tsx | Injects root scope into clear vault key submission. |
| packages/insomnia/src/routes/auth.authorize.tsx | Injects root scope into authorize submission (JSON). |
| packages/insomnia/src/root.tsx | Adds scoped shouldRevalidate, trims loader payload + (debug) loader logging. |
Comments suppressed due to low confidence (1)
packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.response.delete-all.tsx:57
useRequestResponseDeleteAllActionFetchernow submits a JSON string but does not setencType: 'application/json'. Depending on the underlyingfetcher.submitbehavior, this can lead to incorrect request encoding and (more importantly for this PR) the scope field may not be available toshouldRevalidatein the expectedjson/textshape.
return submit(
JSON.stringify(
addScopeField({
scopes: ['request'],
data: {},
}),
),
{
action: url,
method: 'POST',
},
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (formData && formData.has(SCOPE_TO_REVALIDATE_FIELD)) { | ||
| const scopesJsonStr = formData.get(SCOPE_TO_REVALIDATE_FIELD); | ||
| if (typeof scopesJsonStr === 'string' && isValidJSONString(scopesJsonStr)) { | ||
| const scopes = JSON.parse(scopesJsonStr); | ||
| if (Array.isArray(scopes)) { | ||
| return scopes as submitRevalidateDataScope[]; | ||
| } | ||
| } | ||
| } |
| if (json && typeof json === 'object' && SCOPE_TO_REVALIDATE_FIELD in json) { | ||
| return json[SCOPE_TO_REVALIDATE_FIELD] as submitRevalidateDataScope[]; | ||
| } |
| // JSON-encoded object | ||
| if (typeof text === 'string') { | ||
| try { | ||
| const parsed = JSON.parse(text); | ||
| if (typeof parsed === 'object' && SCOPE_TO_REVALIDATE_FIELD in parsed) { | ||
| return parsed[SCOPE_TO_REVALIDATE_FIELD] as submitRevalidateDataScope[]; | ||
| } | ||
| } catch { | ||
| // Ignore JSON parse errors | ||
| } | ||
| } | ||
|
|
| // organization matches to organization.tsx for new global organization, user and current plan data (by default cached in local storage). | ||
| // project matches to organization.$organizationId.project.$projectId.tsx for all projects and its child workspace data | ||
| // workspace matches to organization.$organizationId.project.$projectId.workspace.$workspaceId.tsx for current workspace data(including meta) and its child data (environment, certificates, api spec, mock server, requests). | ||
| // request matches to organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.tsx for request data (request, request meat, responses, request versions, mock server and routers). |
| export async function clientLoader(_args: Route.ClientLoaderArgs) { | ||
| console.log(`[loader] organization`); | ||
| const { id, accountId } = await services.userSession.get(); |
| globalBaseEnvironments: (Environment & { workspaceName: string })[]; | ||
| globalSubEnvironments: Environment[]; | ||
| activeApiSpec: ApiSpec | null; | ||
| activeMockServer?: MockServer | null; | ||
| // activeApiSpec: ApiSpec | null; | ||
| // activeMockServer?: MockServer | null; | ||
| clientCertificates: ClientCertificate[]; |
| export async function clientLoader({ params }: Route.ClientLoaderArgs) { | ||
| console.log( | ||
| `[loader] organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId`, | ||
| ); | ||
| const { organizationId, projectId, requestId, workspaceId } = params; |
| export async function clientLoader(_args: Route.ClientLoaderArgs) { | ||
| console.log(`[loader] root`); | ||
| const settings = await services.settings.get(); |
| const [workspaceCount, setWorkspaceCount] = useState(0); | ||
| useEffect(() => { | ||
| services.workspace.count().then(setWorkspaceCount); | ||
| }, []); |
| useEffect(() => { | ||
| async function loadMockServer() { | ||
| if (models.workspace.isMockServer(workspace)) { | ||
| const mockServer = await services.mockServer.getByParentId(workspace._id); | ||
| mockServer && setMockServer(mockServer); | ||
| } | ||
| } | ||
|
|
||
| loadMockServer(); | ||
| }, [workspace]); |
Changes:
Add shouldRevalidate with an enforced scope registry:
shouldRevalidatewhich revalidates only when the submission's declared scopes intersect the loader's scope.Design doc: https://konghq.atlassian.net/wiki/spaces/~7120200ecfc756d7d24b19aacb30d7ea8bf917/pages/5620924446/Decoupling+Loaders+from+Action+Revalidation
INS-2735