mirror of
https://github.com/Kong/insomnia.git
synced 2026-11-01 10:41:00 -05:00
## Background Workspace `hasUncommittedChanges` / `hasUnpushedChanges` flags are written only by cloud sync. When a project stopped being a cloud project, those flags were never cleared, so the project overview cards kept showing "Uncommitted changes". Repro: 1. Open a cloud project whose workspaces have uncommitted changes. 2. Remove the project from the remote. 3. Open the project overview. The cards still show "Uncommitted changes" although the project is now local. A project converted from cloud to git carries the same residual flags, because the values come from its former cloud state. ## Changes - Clear both workspace sync flags when a project's `remoteId` becomes `null` in the project service. Remote deletion, conversion to local, cross-organization move, and conversion to git all update the project through this service. - Reuse the cleanup in the startup database repair to fix existing stale records. Only dirty workspace metas are written, and `modified` is preserved. - Add service and repair tests. ## Testing - `npm test -w insomnia-data` (538 tests) - `npm run type-check -w insomnia-data` - `npm run lint -w insomnia-data` ## References - INS-3899
insomnia-data
A runtime-agnostic data layer for Insomnia, based on interface + IoC.
Core idea
src/: runtime-agnostic contracts (IDatabase,Services, model metadata/types)node-src/: Node/main concrete implementations (createNedbDatabase,servicesNodeImpl)- entry points wire once:
initDatabase(impl)initServices(impl)
After wiring, business code always uses the same APIs: database, services, models.
Process flows
Database (main / renderer / inso)
flowchart LR
subgraph Renderer
R0[initDatabase] --> R1[clientDatabase implementation]
R2[feature code] --> R3[database from insomnia-data]
R3 --> R1
R1 --> R4[window.database.invoke]
R4 --> R5[ipcRenderer.invoke database.invoke]
end
subgraph Main
M0[initDatabase] --> M1[mainDatabase implementation]
M1 --> M2[createNedbDatabase impl]
M2 --> M3[(NeDB)]
M4[main feature code] --> M5[database from insomnia-data]
M5 --> M1
M6[ipcMain.handle database.invoke] --> M1
M1 --> M7[webContents.send db.changes]
end
subgraph Inso
I0[initDatabase] --> I1[inso database implementation]
I1 --> I2[createNedbDatabase impl]
I2 --> I3[(NeDB)]
I4[inso feature code] --> I5[database from insomnia-data]
I5 --> I1
end
R5 --> M6
M7 -.notify.-> R2
Services (main / renderer / inso)
flowchart LR
subgraph Renderer
R0[initServices] --> R1[preload servicesProxy implementation]
R2[feature code] --> R3[services from insomnia-data]
R3 --> R1
R1 --> R4[ipcRenderer.invoke services.invoke]
end
subgraph Main
M0[initServices] --> M1[servicesNodeImpl]
M2[feature code] --> M3[services from insomnia-data]
M3 --> M1
M1 --> M4[service logic]
M4 --> M5[database]
M5 --> M6[(NeDB)]
M7[ipcMain.handle services.invoke] --> M1
end
subgraph Inso
I0[initServices] --> I1[servicesNodeImpl]
I2[feature code] --> I3[services from insomnia-data]
I3 --> I1
I1 --> I6[(NeDB)]
end
R4 --> M7
Renderer services path:
services.xxx -> preload proxy -> IPC -> main handler -> servicesNodeImpl -> database.
Why this design
- Same API across runtimes: main, renderer, inso.
- Feature code is decoupled from Electron/IPC/NeDB details.
- Renderer has a safer boundary (bridge + IPC, no direct DB internals and node API access).
- Easy to test or swap implementations by injecting at startup.
Minimal usage
Main
import { initDatabase, initServices } from 'insomnia-data';
import { mainDatabase } from '~/main/database.main';
import { servicesNodeImpl } from 'insomnia-data/node';
await initDatabase(mainDatabase);
initServices(servicesNodeImpl);
Renderer
import { initDatabase, initServices } from 'insomnia-data';
import { clientDatabase } from '~/ui/database.client';
await initDatabase(clientDatabase);
initServices(window._dataServices);
Inso / Node
import { initDatabase, initServices } from 'insomnia-data';
import { createNedbDatabase, servicesNodeImpl } from 'insomnia-data/node';
await initDatabase(createNedbDatabase());
initServices(servicesNodeImpl);
Consuming
import { services, models, type Request } from 'insomnia-data';
const mcpRequest = await services.mcpRequest.create({ url: 'http://localhost:3000' });
const all = await services.mcpRequest.all();
const request: Request = {};
const requestType = models.request.type;