Various SDK improvements (#18115)

## Summary

- **Refactor frontend metadata loading architecture**: Split the
monolithic `EagerMetadataLoadEffect` into focused provider effects
(`UserMetadataProviderEffect`, `ObjectMetadataProviderEffect`,
`ViewMetadataProviderEffect`) orchestrated by `MetadataProviderEffects`.
Replaced `UserProvider` + `ObjectMetadataItemsProvider` with a single
`MetadataGater` that gates rendering on `isAppMetadataReadyState`. The
metadata store now validates view-object consistency before promoting
views, and `updateDraft` skips no-op updates via deep equality checks.

- **SDK CLI improvements**: Added `app:typecheck` command, improved
error handling in API sync (extracts GraphQL error messages), added
`serializeError` utility for human-readable error output, added `error`
file status to dev mode orchestrator with UI support, and fixed
ClickHouse migration/seed commands to use `transpile-only`.
This commit is contained in:
Charles Bochet
2026-02-23 19:57:02 +01:00
committed by GitHub
parent ccddd105d8
commit 0d4fe4575b
38 changed files with 675 additions and 450 deletions
@@ -1,4 +1,4 @@
import { metadataStoreState } from '@/app/states/metadataStoreState';
import { useMetadataStore } from '@/metadata-store/hooks/useMetadataStore';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { recordPageLayoutsState } from '@/page-layout/states/recordPageLayoutsState';
import { type PageLayout } from '@/page-layout/types/PageLayout';
@@ -8,8 +8,8 @@ import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { useStore } from 'jotai';
import { useCallback, useEffect } from 'react';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useLocation } from 'react-router-dom';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { AppPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
@@ -29,6 +29,7 @@ export const LazyMetadataLoadEffect = () => {
const store = useStore();
const setLogicFunctions = useSetRecoilState(logicFunctionsState);
const { updateDraft, applyChanges } = useMetadataStore();
const isOnAuthPath =
isMatchingLocation(location, AppPath.Verify) ||
@@ -75,14 +76,8 @@ export const LazyMetadataLoadEffect = () => {
if (!isDeeplyEqual(existingRecordPageLayouts, recordPageLayouts)) {
set(recordPageLayoutsState, recordPageLayouts);
}
store.set(metadataStoreState.atomFamily('pageLayouts'), {
current: recordPageLayouts,
draft: [],
status: 'loaded',
});
},
[store],
[],
);
useEffect(() => {
@@ -102,7 +97,14 @@ export const LazyMetadataLoadEffect = () => {
queryDataRecordPageLayouts.getPageLayouts.map(transformPageLayout);
setRecordPageLayouts(transformedPageLayouts);
}, [queryDataRecordPageLayouts?.getPageLayouts, setRecordPageLayouts]);
updateDraft('pageLayouts', transformedPageLayouts);
applyChanges();
}, [
queryDataRecordPageLayouts?.getPageLayouts,
setRecordPageLayouts,
updateDraft,
applyChanges,
]);
useEffect(() => {
if (!isDefined(logicFunctionsData?.findManyLogicFunctions)) {
@@ -110,13 +112,14 @@ export const LazyMetadataLoadEffect = () => {
}
setLogicFunctions(logicFunctionsData.findManyLogicFunctions);
store.set(metadataStoreState.atomFamily('logicFunctions'), {
current: logicFunctionsData.findManyLogicFunctions,
draft: [],
status: 'loaded',
});
}, [logicFunctionsData?.findManyLogicFunctions, setLogicFunctions, store]);
updateDraft('logicFunctions', logicFunctionsData.findManyLogicFunctions);
applyChanges();
}, [
logicFunctionsData?.findManyLogicFunctions,
setLogicFunctions,
updateDraft,
applyChanges,
]);
return null;
};