Add SSE for metadata and plug front components (#17998)

Create the necessary tooling to listen to metadata events and plug it to
the front components. Now we have a hot reload like experience when we
edit a component in an app.

## Backend

- Split `EventWithQueryIds` into `ObjectRecordEventWithQueryIds` and
`MetadataEventWithQueryIds`
- Publish metadata event batches to active SSE streams in
`MetadataEventsToDbListener`

## Frontend

- Create a metadata event dispatching pipeline: SSE metadata events are
grouped by metadata name, transformed into
`MetadataOperationBrowserEventDetail` objects, and dispatched as browser
`CustomEvents`
- Add `useListenToMetadataOperationBrowserEvent` hook for consuming
metadata operation events filtered by metadata name and operation type
- Rename `useListenToObjectRecordEventsForQuery` to
`useListenToEventsForQuery`, now accepting both
`RecordGqlOperationSignature` and `MetadataGqlOperationSignature`
- Implement `useOnFrontComponentUpdated` which subscribes to front
component metadata events and updates the Apollo cache when the
component is modified
- Add `builtComponentChecksum` to the front component query and appends
it to the component URL for browser cache invalidation
This commit is contained in:
Raphaël Bosi
2026-02-18 12:26:20 +01:00
committed by GitHub
parent e3753bf822
commit 2455c859b4
76 changed files with 1636 additions and 173 deletions
@@ -1,7 +1,7 @@
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
import { dispatchObjectRecordOperationBrowserEvent } from '@/browser-event/utils/dispatchObjectRecordOperationBrowserEvent';
import { useStopWorkflowRunMutation } from '~/generated/graphql';
export const useStopWorkflowRun = () => {
@@ -1,5 +1,5 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useListenToObjectRecordEventsForQuery } from '@/sse-db-event/hooks/useListenToObjectRecordEventsForQuery';
import { useListenToEventsForQuery } from '@/sse-db-event/hooks/useListenToEventsForQuery';
export const WorkflowRunSSESubscribeEffect = ({
workflowRunId,
@@ -8,7 +8,7 @@ export const WorkflowRunSSESubscribeEffect = ({
}) => {
const queryId = `workflow-run-${workflowRunId}`;
useListenToObjectRecordEventsForQuery({
useListenToEventsForQuery({
queryId,
operationSignature: {
objectNameSingular: CoreObjectNameSingular.WorkflowRun,
@@ -3,8 +3,8 @@ import { useSetRecoilState } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useListenToObjectRecordOperationBrowserEvent } from '@/object-record/hooks/useListenToObjectRecordOperationBrowserEvent';
import { useListenToObjectRecordEventsForQuery } from '@/sse-db-event/hooks/useListenToObjectRecordEventsForQuery';
import { useListenToObjectRecordOperationBrowserEvent } from '@/browser-event/hooks/useListenToObjectRecordOperationBrowserEvent';
import { useListenToEventsForQuery } from '@/sse-db-event/hooks/useListenToEventsForQuery';
import { shouldWorkflowRefetchRequestFamilyState } from '@/workflow/states/shouldWorkflowRefetchRequestFamilyState';
export const WorkflowSSESubscribeEffect = ({
@@ -23,7 +23,7 @@ export const WorkflowSSESubscribeEffect = ({
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
});
useListenToObjectRecordEventsForQuery({
useListenToEventsForQuery({
queryId,
operationSignature: {
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,