Files
twenty/packages/twenty-sdk/src/front-component/remote/worker/remote-worker.ts
T
Raphaël Bosi 2b29918bf8 [FRONT COMPONENTS] Navigate from the remote (#17762)
## PR Description

This PR:
- Introduces a `FrontComponentHostCommunicationApi`, which allows us to
pass functions to be executed from the worker
- Exposes a navigate function from the host to the front component
remote workers, enabling SDK components to trigger in-app navigation
- Gets rid of `useSyncExternalStore` and makes the execution context
reactive without it
- Refactors and improves the `esbuild` plugin system

## Video


https://github.com/user-attachments/assets/7b26a1c2-f85f-4898-a71d-f60c70e61711
2026-02-09 14:38:35 +01:00

75 lines
2.3 KiB
TypeScript

import '@remote-dom/core/polyfill';
import '@remote-dom/react/polyfill';
import '../generated/remote-elements';
import { ThreadWebWorker } from '@quilted/threads';
import {
BatchingRemoteConnection,
type RemoteConnection,
type RemoteRootElement,
} from '@remote-dom/core/elements';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { jsx, jsxs } from 'react/jsx-runtime';
import * as TwentySharedTypes from 'twenty-shared/types';
import * as TwentySharedUtils from 'twenty-shared/utils';
import { setFrontComponentExecutionContext } from '@/sdk/front-component-api/context/frontComponentContext';
import { setNavigate } from '@/sdk/front-component-api/functions/navigate';
import * as TwentySdk from '@/sdk';
import { type FrontComponentExecutionContext } from '../../types/FrontComponentExecutionContext';
import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi';
import { type HostToWorkerRenderContext } from '../../types/HostToWorkerRenderContext';
import { type WorkerExports } from '../../types/WorkerExports';
import * as RemoteComponents from '../generated/remote-components';
import { exposeGlobals } from '../utils/exposeGlobals';
exposeGlobals({
React,
RemoteComponents,
jsx,
jsxs,
TwentySdk,
TwentyShared: {
utils: TwentySharedUtils,
types: TwentySharedTypes,
},
});
const render: WorkerExports['render'] = async (
connection: RemoteConnection,
renderContext: HostToWorkerRenderContext,
) => {
const batchedConnection = new BatchingRemoteConnection(connection);
const root = document.createElement('remote-root') as RemoteRootElement;
root.connect(batchedConnection);
document.body.append(root);
/* @vite-ignore */
const componentModule = await import(renderContext.componentUrl);
const reactRoot = createRoot(root);
reactRoot.render(componentModule.default);
};
const initializeHostCommunicationApi: WorkerExports['initializeHostCommunicationApi'] =
async () => {
const hostApi =
ThreadWebWorker.self.import<FrontComponentHostCommunicationApi>();
setNavigate(hostApi.navigate);
};
const updateContext: WorkerExports['updateContext'] = async (
context: FrontComponentExecutionContext,
) => {
setFrontComponentExecutionContext(context);
};
ThreadWebWorker.self.export({
render,
initializeHostCommunicationApi,
updateContext,
});