Skip to content

refactor: DNM WIP - context isolation structural example - #8456

Draft
nbbeeken wants to merge 1 commit into
data-service-typesfrom
data-service-split
Draft

refactor: DNM WIP - context isolation structural example#8456
nbbeeken wants to merge 1 commit into
data-service-typesfrom
data-service-split

Conversation

@nbbeeken

Copy link
Copy Markdown
Collaborator

Description

This demonstrates how we begin linking a renderer data service to a utility data service to
get Compass closer to contextIsolation:true. I think its interesting to see the simple example
working in a way that doesn't impact Compass's current implementation so that its apparent
how this could be done progressively.

Motivation and Context

Context isolation and nodeIntegration:false will make the renderer purely a web-only
application and draw an uncrossable line between it and system resources.

Open Questions

Still needs clarity:

  • Current approach shows one to one message and reply, what about parallelism?
  • What about type-checking / Zod / declarative API?
  • What could be abstracted for more utilities? What shouldn't be?

Stack created with GitHub Stacks CLIGive Feedback 💬

@nbbeeken
nbbeeken added this pull request to stack #8457 September 10, 2026 15:45
),
};

globalThis.postMessage(bootMessage, '*', [channel.port2]);
@nbbeeken

Copy link
Copy Markdown
Collaborator Author

How to follow this:

  • First things first we need to build a separate javascript file that Compass can fork. There's now Webpack entry points for two new files: data-service-utility and preload
    • data-service utility is the entry point for a Node.js process that will launch and listen on its parentPort for the compass:data-service:port message. Every time this message arrives it will create a DataService instance and hand it the port that came in that initiating message
    • preload is a script that Electron runs ahead of launching the renderer and has access to APIs not exposed to the renderer. It can add a listener to the window object's message event that can then use ipc to talk to the utility process (delivering that initial message and MessagePort)
  • Now let's trace to the DataServiceRenderer which is a subclass of DataServiceImpl. In its constructor it fires compass:data-service:port to get a DataServiceUtility created on the Node.js-side
  • DataServiceUtility is built given a port that talks directly back to the renderer.

The path from here sort of follows the very minimal non-example in the code:

  • Renderer overrides a method at a time from DataServiceImpl turning what was previously an implementation of say "find" into an RPC call to run a "find" over on DataServiceUtility
  • DataServiceUtility ideally doesn't need much code change, it should be able to reuse the logic in DataServiceImpl as it is also a subclass. The complexity of this class is mostly in the handling of message passing across the IPC boundary.

Copilot AI lite review requested due to automatic review settings September 10, 2026 18:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new mongodb-data-service connect() path is not safely gated to Electron and can break/hang Node-based tests/consumers, and the new IPC bridge needs basic validation/robustness improvements.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR prototypes a split DataService architecture (renderer façade + Electron utility process backend) as a stepping stone toward contextIsolation: true / nodeIntegration: false, while aiming to keep the current Compass behavior largely intact.

Changes:

  • Introduces DataServiceRenderer / DataServiceUtility and a MessagePort-based bootstrapping path between renderer → preload → main → utility process.
  • Wires up a preload script and launches a dedicated utility process for the data service from the main process.
  • Extends build/lint tooling to support .mts and emits additional bundles (preload.js, data-service.mjs).
File summaries
File Description
packages/data-service/src/index.ts Exposes the new utility-side DataService wrapper from the package entrypoint.
packages/data-service/src/data-service-utility.ts Adds a utility-process DataService subclass that listens on a MessagePortMain.
packages/data-service/src/data-service-renderer.ts Adds a renderer-side DataService subclass that communicates via MessageChannel + postMessage.
packages/data-service/src/connect.ts Switches the library connect() helper to use the new renderer implementation.
packages/compass/webpack.config.js Builds additional entrypoints for the utility process and preload script.
packages/compass/src/utilities/data-service/index.mts Adds the Electron utility process entrypoint that hosts DataServiceUtility.
packages/compass/src/preload/index.ts Adds a preload entrypoint that installs the port bridge.
packages/compass/src/preload/data-service-port-bridge.ts Forwards the renderer-posted MessagePort to main via ipcRenderer.postMessage.
packages/compass/src/main/window-manager.ts Configures the BrowserWindow preload script.
packages/compass/src/main/application.ts Launches the utility process and relays the port to it from ipcMain.
configs/webpack-config-compass/src/loaders.ts Updates webpack loader patterns to recognize .mts.
configs/eslint-config-compass/index.js Updates TS overrides so .mts/.cts are linted as TypeScript.
Review details

Suppressed comments (1)

packages/data-service/src/connect.ts:26

  • DataServiceRenderer should not be instantiated unconditionally: in non-Electron environments there is no preload/utility bridge, so the constructor/connect path can throw or hang. Choose the implementation at runtime (e.g., based on the Electron user agent) and fall back to DataServiceImpl.
  const dataService = new DataServiceRenderer(
    connectionOptions,
    logger,
    proxyOptions
  );
  • Files reviewed: 12/12 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +16 to +23
if (
event.source === window &&
event.data?.type === 'compass:data-service:port'
) {
ipcRenderer.postMessage('compass:data-service:port', event.data, [
...event.ports,
]);
}
Comment on lines +26 to +29
setInterval(() => {
// eslint-disable-next-line no-console
console.log(new Date(), 'hello from utility');
}, 5000);
import type { DataService } from './data-service';
import type { DataServiceImplLogger } from './logger';
import { DataServiceImpl } from './data-service';
import { DataServiceRenderer } from './data-service-renderer';
Comment on lines +54 to +59
private async send(message: any) {
const reply = once(this.portToUtility, 'message');
this.portToUtility.postMessage(message);
const [response] = await reply;
return response;
}
outputFilename: '[name].js',
});
// See the comment above `dataServiceUtilityBaseConfig`: this is a second
// `electron-main`-targeted config, so it would otherwise steal the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the target is electron-preload? 🤔 We can probably just adjust the plugin to give it more hints about what to run when we're doing a proper implementation

* ▝▀ ▀ ▀▘▘▀▘▀ ▗▄▘ *
********************/

// There should be no changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ominous, I like it

@gribnoysup gribnoysup left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very neat!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants