Add Client Api generation (#17961)
## Add API client generation to SDK dev mode and refactor orchestrator into step-based pipeline ### Why The SDK dev mode lacked typed API client generation, forcing developers to work without auto-generated GraphQL types when building applications. Additionally, the orchestrator was a monolithic class that mixed watcher management, token handling, and sync logic — making it difficult to extend with new steps like client generation. ### How - **Refactored the orchestrator** into a step-based pipeline with dedicated classes: `CheckServer`, `EnsureValidTokens`, `ResolveApplication`, `BuildManifest`, `UploadFiles`, `GenerateApiClient`, `SyncApplication`, and `StartWatchers`. Each step has typed input/output/status, managed by a new `OrchestratorState` class. - **Added `GenerateApiClientOrchestratorStep`** that detects object/field schema changes and regenerates a typed GraphQL client (via `@genql/cli`) into `node_modules/twenty-sdk/generated` for seamless imports. - **Replaced `checkApplicationExist`** with `findOneApplication` on both server resolver and SDK API service, returning the entity data instead of a boolean. - **Added application token pair mutations** (`generateApplicationToken`, `renewApplicationToken`) to the API service, with the server now returning `ApplicationTokenPairDTO` containing both access and refresh tokens. - **Restructured the dev UI** into `dev/ui/components/` with dedicated panel, section, and event log components. - **Simplified `AppDevCommand`** from ~180 lines of watcher management down to ~40 lines that delegate entirely to the orchestrator.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
type DevUiStatus,
|
||||
DEV_UI_STATUS_CONFIG,
|
||||
SPINNER_FRAMES,
|
||||
UPLOAD_FRAMES,
|
||||
} from '@/cli/utilities/dev/ui/dev-ui-constants';
|
||||
|
||||
export const useAnimatedFrame = (frames: string[], interval = 80): string => {
|
||||
const [frameIndex, setFrameIndex] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setFrameIndex((currentIndex) => (currentIndex + 1) % frames.length);
|
||||
}, interval);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [frames, interval]);
|
||||
|
||||
return frames[frameIndex];
|
||||
};
|
||||
|
||||
export const useStatusIcon = (uiStatus: DevUiStatus): string => {
|
||||
const spinnerFrame = useAnimatedFrame(SPINNER_FRAMES, 80);
|
||||
const uploadFrame = useAnimatedFrame(UPLOAD_FRAMES, 200);
|
||||
const config = DEV_UI_STATUS_CONFIG[uiStatus];
|
||||
|
||||
if (config.icon === 'spinner') return spinnerFrame;
|
||||
if (config.icon === 'upload') return uploadFrame;
|
||||
|
||||
return config.icon;
|
||||
};
|
||||
Reference in New Issue
Block a user