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:
+7
-5
@@ -40,7 +40,8 @@ export type OrchestratorStateFileStatus =
|
||||
| 'pending'
|
||||
| 'building'
|
||||
| 'uploading'
|
||||
| 'success';
|
||||
| 'success'
|
||||
| 'error';
|
||||
|
||||
export type OrchestratorStateEntityInfo = {
|
||||
name: string;
|
||||
@@ -81,10 +82,11 @@ const FILE_STATUS_TRANSITION_MATRIX: Record<
|
||||
OrchestratorStateFileStatus,
|
||||
OrchestratorStateFileStatus[]
|
||||
> = {
|
||||
pending: ['building', 'uploading', 'success'],
|
||||
building: ['pending', 'uploading', 'success'],
|
||||
uploading: ['pending', 'success'],
|
||||
success: ['pending', 'building', 'uploading'],
|
||||
pending: ['building', 'uploading', 'success', 'error'],
|
||||
building: ['pending', 'uploading', 'success', 'error'],
|
||||
uploading: ['pending', 'success', 'error'],
|
||||
success: ['pending', 'building', 'uploading', 'error'],
|
||||
error: ['pending', 'building', 'uploading', 'success'],
|
||||
};
|
||||
|
||||
export class OrchestratorState {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '@/cli/utilities/dev/orchestrator/steps/start-watchers-orchestrator-step';
|
||||
import { SyncApplicationOrchestratorStep } from '@/cli/utilities/dev/orchestrator/steps/sync-application-orchestrator-step';
|
||||
import { UploadFilesOrchestratorStep } from '@/cli/utilities/dev/orchestrator/steps/upload-files-orchestrator-step';
|
||||
import { serializeError } from '@/cli/utilities/error/serialize-error';
|
||||
import * as fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { OUTPUT_DIR, type Manifest } from 'twenty-shared/application';
|
||||
@@ -143,10 +144,11 @@ export class DevModeOrchestrator {
|
||||
await this.runSyncPipeline();
|
||||
} catch (error) {
|
||||
this.state.addEvent({
|
||||
message: `Sync failed with error ${JSON.stringify(error, null, 2)}`,
|
||||
message: `Sync failed with error: ${serializeError(error)}`,
|
||||
status: 'error',
|
||||
});
|
||||
this.state.updatePipeline({ status: 'error' });
|
||||
this.state.updateAllEntitiesStatus('error');
|
||||
} finally {
|
||||
this.state.updatePipeline({ isSyncing: false });
|
||||
}
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import {
|
||||
type OrchestratorStateStepEvent,
|
||||
type OrchestratorStateSyncStatus,
|
||||
} from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
|
||||
import { serializeError } from '@/cli/utilities/error/serialize-error';
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
|
||||
export type SyncApplicationOrchestratorStepOutput = {
|
||||
@@ -73,12 +74,13 @@ export class SyncApplicationOrchestratorStep {
|
||||
return;
|
||||
}
|
||||
|
||||
const errorMessage = `Sync failed with error ${JSON.stringify(syncResult.error, null, 2)}`;
|
||||
const errorMessage = `Sync failed with error: ${serializeError(syncResult.error)}`;
|
||||
|
||||
events.push({ message: errorMessage, status: 'error' });
|
||||
step.output = { syncStatus: 'error', error: errorMessage };
|
||||
step.status = 'error';
|
||||
this.state.updatePipeline({ status: 'error', error: errorMessage });
|
||||
this.state.updateAllEntitiesStatus('error');
|
||||
this.state.applyStepEvents(events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,11 @@ export const DevUiEntityLegend = (): React.ReactElement => {
|
||||
<Text color={DEV_UI_STATUS_CONFIG.done.color}>
|
||||
{DEV_UI_STATUS_CONFIG.done.icon}
|
||||
</Text>{' '}
|
||||
success
|
||||
success{' '}
|
||||
<Text color={DEV_UI_STATUS_CONFIG.error.color}>
|
||||
{DEV_UI_STATUS_CONFIG.error.icon}
|
||||
</Text>{' '}
|
||||
error
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -49,6 +49,7 @@ export const mapFileStatusToDevUiStatus = (
|
||||
building: 'in_progress',
|
||||
uploading: 'uploading',
|
||||
success: 'done',
|
||||
error: 'error',
|
||||
};
|
||||
|
||||
return mapping[status];
|
||||
@@ -182,6 +183,7 @@ export const getPipelineRows = (
|
||||
): DevUiPipelineRow[] => {
|
||||
const entities = [...state.entities.values()];
|
||||
|
||||
const hasError = entities.some((entity) => entity.status === 'error');
|
||||
const isBuilding = entities.some((entity) => entity.status === 'building');
|
||||
const allUploaded =
|
||||
entities.length > 0 &&
|
||||
@@ -189,11 +191,13 @@ export const getPipelineRows = (
|
||||
(entity) => entity.status === 'uploading' || entity.status === 'success',
|
||||
);
|
||||
|
||||
const resourcesBuildStatus: OrchestratorStateStepStatus = isBuilding
|
||||
? 'in_progress'
|
||||
: allUploaded
|
||||
? 'done'
|
||||
: 'idle';
|
||||
const resourcesBuildStatus: OrchestratorStateStepStatus = hasError
|
||||
? 'error'
|
||||
: isBuilding
|
||||
? 'in_progress'
|
||||
: allUploaded
|
||||
? 'done'
|
||||
: 'idle';
|
||||
|
||||
return [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user