import { ExtendedPatch } from "@noya-app/noya-multiplayer-react"; import type { ParsedError, PipelineState, RemoteEvaluateParameters, } from "@noya-app/noya-pipeline"; import type { Asset, CreateResourceParametersWithoutAccessibleByFileId, DeleteResourceParameters, EncodedSchema, Input, OutputTransform, Resource, UploadableAsset, } from "@noya-app/noya-schemas"; import type { Secret } from "../SecretManager"; import { SerializableRequest, SerializableResponse, TypedBody } from "./types"; export type WorkspaceMemberUser = { id: string; name: string | null; email: string | null; image: string | null; }; type BaseTranscriptionRequest = { fileName?: string; contentType?: string; endpoint?: string; apiKey?: string; }; export type TranscriptionRequest = BaseTranscriptionRequest & ({ type: "data"; data: string } | { type: "assetId"; assetId: string }); export type TranscriptionSegment = { id: number; seek: number; start: number; end: number; text: string; tokens: number[]; temperature: number; avg_logprob: number; compression_ratio: number; no_speech_prob: number; }; export type TranscriptionMetadata = { filename?: string; originalSize?: number; originalSizeMB?: string; duration?: number; language?: string; processingTimeSeconds?: number; segmentCount?: number; [key: string]: unknown; }; export type TranscriptionResponse = { success: boolean; transcription: string; segments: TranscriptionSegment[]; metadata: TranscriptionMetadata; }; export type AIGenerateImage = | { type: "data"; data: string; mediaType: string } | { type: "url"; url: string }; export type AIGenerateRequest = { prompt: string; system?: string; model?: string; schema: Record; encodedSchema?: EncodedSchema; images?: AIGenerateImage[]; }; export type AIGenerateResponse = { object: unknown; }; export type AIGenerateTextRequest = { prompt: string; system?: string; model?: string; images?: AIGenerateImage[]; }; export type AIGenerateTextResponse = { text: string; }; export type AIGenerateImageRequest = { prompt: string; model?: string; }; export type AIGenerateImageResponse = { base64: string; mediaType: string; }; export type ScriptEvaluatorFile = string | { content: string }; type ScriptEvaluatorRequestBase = { args?: unknown[]; env?: Record; secrets?: Record; includeFileEnv?: boolean; }; export type ScriptEvaluatorRequest = ScriptEvaluatorRequestBase & ( | { type: "code"; code: string } | { type: "url"; url: string; cid?: string } | { type: "files"; files: Record; entryPoint?: string; } ); export type ScriptEvaluatorResponse = | { status: "success"; result: unknown; logs: unknown[][] } | { status: "error"; error: ParsedError; logs: unknown[][] }; export type ResourcePatchUpdateParameters = { id: string; path?: string; type?: Resource["type"]; assetId?: string; asset?: UploadableAsset; fileId?: string; fileVersionId?: string; resourceId?: string; }; export type ResourcePatchRequestBody = { create?: CreateResourceParametersWithoutAccessibleByFileId[]; update?: ResourcePatchUpdateParameters[]; delete?: DeleteResourceParameters[]; }; // Git file operations export type GitFileCreateOperation = { path: string; content: string; }; export type GitFileRenameOperation = { oldPath: string; newPath: string; }; export type GitFileDeleteOperation = { path: string; }; export type GitFilePatchRequestBody = { create?: GitFileCreateOperation[]; rename?: GitFileRenameOperation[]; delete?: GitFileDeleteOperation[]; }; export type GitInitRepoRequestBody = { initialFiles?: GitFileCreateOperation[]; }; export type GitFilePatchResponseBody = { oid: string; files: string[]; ref: string; /** Present when includeContent query param is true */ fileContents?: GitFileInfo[]; }; export type GitFileInfo = { path: string; /** Base64-encoded file content */ content: string; }; export type GitListFilesResponseBody = { files: string[]; ref: string; oid: string; /** Present when includeContent query param is true */ fileContents?: GitFileInfo[]; }; export type RouteDefinition< TRequest extends SerializableRequest, TResponse extends SerializableResponse, TSearchParams extends Record = Record< string, never >, > = { request: TRequest; response: TResponse; searchParams: TSearchParams; }; export type Routes = { // Asset routes "GET /api/assets": RouteDefinition< { url: "/api/assets"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "POST /api/assets": RouteDefinition< { url: "/api/assets"; options: { method: "POST"; headers?: { "Content-Type"?: string; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; }, { mode?: "immutable" | "mutable" } >; "PUT /api/assets/:id": RouteDefinition< { url: `/api/assets/${string}`; options: { method: "PUT"; headers?: { "Content-Type"?: string; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "DELETE /api/assets/:id": RouteDefinition< { url: `/api/assets/${string}`; options: { method: "DELETE"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ success: true }>; } >; "GET /api/secrets": RouteDefinition< { url: "/api/secrets"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "POST /api/secrets": RouteDefinition< { url: "/api/secrets"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ name: string; value: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "DELETE /api/secrets": RouteDefinition< { url: "/api/secrets"; options: { method: "DELETE"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ name: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ success: true }>; } >; "PUT /api/file": RouteDefinition< { url: "/api/file"; options: { method: "PUT"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ name: string; changeId?: string }>; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ name: string; changeId?: string }>; } >; "GET /api/file/members": RouteDefinition< { url: "/api/file/members"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; // Workflow routes "POST /api/runWorkflow": RouteDefinition< { url: "/api/runWorkflow"; options: { method: "POST"; }; body: TypedBody<{ pipelineState?: PipelineState }>; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ result: any; snapshot: any }>; } >; "POST /api/transcriptions": RouteDefinition< { url: "/api/transcriptions"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; } >; // Evaluate routes "POST /api/evaluate": RouteDefinition< { url: "/api/evaluate"; options: { method: "POST"; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ result: any }>; } >; "POST /api/scripts/evaluate": RouteDefinition< { url: "/api/scripts/evaluate"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; } >; // AI routes "POST /api/ai": RouteDefinition< { url: "/api/ai"; streaming: true; options: { method: "POST"; headers?: Record; body?: string; }; }, { status: 200; statusText: "OK"; headers: {}; body: string; } >; "POST /api/ai/generate": RouteDefinition< { url: "/api/ai/generate"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; } >; "POST /api/ai/generate/text": RouteDefinition< { url: "/api/ai/generate/text"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; } >; "POST /api/ai/generate/image": RouteDefinition< { url: "/api/ai/generate/image"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; } >; // IO routes "GET /api/inputs": RouteDefinition< { url: "/api/inputs"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "GET /api/outputTransforms": RouteDefinition< { url: "/api/outputTransforms"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "POST /api/inputs": RouteDefinition< { url: "/api/inputs"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody & { id?: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "PATCH /api/inputs": RouteDefinition< { url: "/api/inputs"; options: { method: "PATCH"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "POST /api/outputTransforms": RouteDefinition< { url: "/api/outputTransforms"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody & { id?: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "DELETE /api/inputs": RouteDefinition< { url: `/api/inputs`; options: { method: "DELETE"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ id: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ success: true }>; } >; "DELETE /api/outputTransforms": RouteDefinition< { url: `/api/outputTransforms`; options: { method: "DELETE"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ id: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ success: true }>; } >; "PATCH /api/outputTransforms": RouteDefinition< { url: "/api/outputTransforms"; options: { method: "PATCH"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; // Resource routes "GET /api/resources": RouteDefinition< { url: "/api/resources"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; "PATCH /api/resources": RouteDefinition< { url: "/api/resources"; options: { method: "PATCH"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody<{ created: Resource[]; updated: Resource[]; deleted: Resource[]; }>; } >; "POST /api/snapshots/restore": RouteDefinition< { url: "/api/snapshots/restore"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ fileId: string; fileVersionId: string }>; }; }, { status: 200; statusText: "OK"; headers: {}; body: TypedBody; } >; // Git routes "POST /api/git/repos": RouteDefinition< { url: "/api/git/repos"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body?: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ id: string }>; } >; "GET /api/git/repos/:id/files": RouteDefinition< { url: `/api/git/repos/${string}/files`; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }, { ref?: string; includeContent?: string } >; "GET /api/git/repos/:id/branches": RouteDefinition< { url: `/api/git/repos/${string}/branches`; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ branches: Array<{ name: string; oid: string }> }>; } >; "PATCH /api/git/repos/:id/files": RouteDefinition< { url: `/api/git/repos/${string}/files`; options: { method: "PATCH"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody; }, { ref?: string; includeContent?: string } >; // Sandbox routes "GET /api/sandbox/files": RouteDefinition< { url: "/api/sandbox/files"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ files: Array<{ name: string; path: string; type: "file" | "directory"; }>; error?: string; }>; } >; "POST /api/sandbox/preview": RouteDefinition< { url: "/api/sandbox/preview"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ previewUrl?: string; error?: string }>; } >; "POST /api/sandbox/exec": RouteDefinition< { url: "/api/sandbox/exec"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ command: string; cwd?: string }>; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ success: boolean; stdout: string; stderr: string; exitCode: number; }>; } >; "GET /api/sandbox/build-files": RouteDefinition< { url: "/api/sandbox/build-files"; options: { method: "GET"; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ success: boolean; zip?: string; error?: string; }>; } >; "POST /api/sandbox/checkpoint": RouteDefinition< { url: "/api/sandbox/checkpoint"; options: { method: "POST"; headers: { "Content-Type": "application/json"; }; body?: TypedBody<{ message?: string }>; }; }, { status: 200; statusText: "OK"; headers: { "Content-Type": "application/json"; }; body: TypedBody<{ success: boolean; error?: string }>; } >; }; // Type helpers for route definitions export type RouteKey = keyof Routes; export type ExtractRequestBody = Routes[K]["request"]["options"] extends { body: TypedBody } ? T : Routes[K]["request"]["options"] extends { body?: TypedBody } ? T | undefined : string; export type ExtractResponseBody = Routes[K]["response"]["body"] extends TypedBody ? T : never; export type ExtractSearchParams = Routes[K]["searchParams"];