import type { TreeNode } from './componentLabTypes' import type { SummaryData } from './componentWorkflowTypes' /** Result schema identifier for file-backed analysis artifacts. */ export const ANALYSIS_FILE_ARTIFACT_SCHEMA = 'mint.analysis_file.v1' /** Known artifact lifecycle states; kept open for forward compatibility. */ export type AnalysisArtifactStatus = 'active' | 'archived' | (string & {}) /** Status filter vocabulary for artifact lists. */ export type AnalysisArtifactStatusFilter = 'active' | 'archived' | 'all' /** Summary row returned by GET /experiments/{id}/analysis-artifacts. */ export interface AnalysisArtifactSummary { id: number experiment_id: number plugin_id: string artifact_key: string display_name: string note?: string | null status: AnalysisArtifactStatus result_keys: string[] created_at?: string | null updated_at?: string | null archived_at?: string | null archived_by?: number | null } /** Full artifact returned by GET /experiments/{id}/analysis-artifacts/{artifactId}. */ export interface AnalysisArtifactDetail extends AnalysisArtifactSummary { result: Record tree: TreeNode[] summary: SummaryData | null } /** Response of GET /experiments/{id}/analysis-artifacts. */ export interface AnalysisArtifactListResponse { artifacts: AnalysisArtifactSummary[] } /** Stable identity used when one plugin opens another plugin's artifact. */ export interface AnalysisArtifactTarget { experimentId: number artifactPluginId: string artifactKey: string /** Optional lookup hint. The stable identity remains the three fields above. */ artifactId?: number } /** Body of PATCH /experiments/{id}/analysis-artifacts/{artifactId}. */ export interface AnalysisArtifactMetadataUpdate { display_name?: string /** undefined = keep, string = set, null = clear. */ note?: string | null } /** How a save payload should be applied by the plugin backend. */ export type ArtifactSaveMode = 'create' | 'upsert' | 'update' /** Where file content comes from when a plugin uses ArtifactSaveDialog. */ export type ArtifactFileSource = 'upload' | 'handler' interface ArtifactSavePayloadBase { experimentId: number artifactKey: string } /** * JSON artifact save — handler maps to save_analysis_artifact, which upserts by key. * That call only assigns display_name/note when they are provided, and cannot clear * either; use the metadata PATCH (useAnalysisArtifacts.updateMetadata) for that. */ export interface JsonArtifactSavePayload extends ArtifactSavePayloadBase { kind: 'json' mode: 'create' | 'upsert' result: Record displayName?: string note?: string } /** File artifact create — handler maps to save_analysis_file_artifact (create-only: a reused key conflicts). */ export interface FileArtifactCreatePayload extends ArtifactSavePayloadBase { kind: 'file' mode: 'create' file: File /** Maps to the Python `kind=` argument (defaults to "file" backend-side). */ fileKind: string filename?: string displayName?: string note?: string contentType?: string metadata?: Record } /** * File artifact replacement — handler maps to update_analysis_file_artifact * (transactional CAS replace). `kind` and `filename` are immutable backend-side, so * `fileKind` echoes the artifact's existing kind and no filename is carried. `note` * is three-state: omitted preserves, null clears, a string sets. `metadata` REPLACES * the previous result metadata rather than merging, so it always carries the full * intended map — omitting it clears whatever was there. */ export interface FileArtifactUpdatePayload extends ArtifactSavePayloadBase { kind: 'file' mode: 'update' file: File fileKind: string note?: string | null contentType?: string metadata?: Record } /** * File artifact create where the plugin handler produces the bytes itself. * This is intended for server-side serialization of in-memory analysis state; * the browser neither fabricates nor uploads a placeholder File. */ export interface HandlerFileArtifactCreatePayload extends ArtifactSavePayloadBase { kind: 'file' mode: 'create' source: 'handler' fileKind: string displayName?: string note?: string metadata?: Record } /** File artifact replacement where the plugin handler produces the bytes itself. */ export interface HandlerFileArtifactUpdatePayload extends ArtifactSavePayloadBase { kind: 'file' mode: 'update' source: 'handler' fileKind: string note?: string | null metadata?: Record } export type ArtifactSavePayload = | JsonArtifactSavePayload | FileArtifactCreatePayload | FileArtifactUpdatePayload /** Payloads accepted when `ArtifactSaveDialog.fileSource` is `handler`. */ export type HandlerArtifactSavePayload = | JsonArtifactSavePayload | HandlerFileArtifactCreatePayload | HandlerFileArtifactUpdatePayload /** Every payload variant the dialog can emit across both file sources. */ export type ArtifactSaveDialogPayload = | ArtifactSavePayload | HandlerFileArtifactCreatePayload | HandlerFileArtifactUpdatePayload /** Optional handler result; cleanupPending surfaces AnalysisFileArtifactUpdate.cleanup_pending. */ export interface ArtifactSaveResult { artifact?: AnalysisArtifactSummary cleanupPending?: boolean } /** * Plugin-supplied save handler. The dialog never writes to the platform; * the handler forwards the payload to the plugin backend, which calls * save_analysis_artifact / save_analysis_file_artifact / update_analysis_file_artifact. */ export type ArtifactSaveHandler = ( payload: ArtifactSavePayload, ) => Promise /** Save handler paired with `ArtifactSaveDialog.fileSource="handler"`. */ export type HandlerArtifactSaveHandler = ( payload: HandlerArtifactSavePayload, ) => Promise /** Handler accepted by ArtifactSaveDialog; `fileSource` selects the payload family. */ export type ArtifactSaveDialogHandler = ArtifactSaveHandler | HandlerArtifactSaveHandler