/** * One row of the adapter mapping form: it picks ONE of three value sources for a * declared input key — a picked field (from the run's output / input schema), a * hardcoded literal, or a jq expression — and shows the row's own compiled jq on * demand. {@link AdapterMapping} composes one editor per declared key. */ import { type ReactNode, useState } from 'react'; import { Badge } from '../components/badge'; import { Field } from '../components/field'; import { TextInput } from '../components/inputs'; import { Button, Skeleton } from '../components/primitives'; import { Select } from '../components/select'; import { type FieldRoot, type MappingRow, type MappingSource, rowValueJq } from './adapter'; import { BindingJqField, type TemplateJqSuggestion } from './BindingJqField'; import type { BindingSourceSchemas, SchemaFieldPath } from './types'; const ROOT_OPTIONS: readonly { value: FieldRoot; label: string }[] = [ { value: 'output', label: 'Run output' }, { value: 'input', label: 'Run input' }, ]; const SOURCE_OPTIONS = [ { value: 'field', label: 'Field' }, { value: 'literal', label: 'Literal' }, { value: 'jq', label: 'Jq' }, ]; function fieldsFor( root: FieldRoot, sources: BindingSourceSchemas | undefined, ): readonly SchemaFieldPath[] { if (root === 'output') return sources?.output ?? []; return sources?.input ?? []; } /** The value control for one row's picked-field source: a Select where a schema is known, else a path input. */ function FieldSourceControl({ root, path, onRootChange, onPathChange, fields, loading = false, error, }: { readonly root: FieldRoot; readonly path: readonly string[]; readonly onRootChange: (root: FieldRoot) => void; readonly onPathChange: (path: readonly string[]) => void; readonly fields: readonly SchemaFieldPath[]; readonly loading?: boolean; readonly error?: string; }): ReactNode { const pathInput = ( { const text = event.target.value.trim(); onPathChange(text === '' ? [] : text.split('.')); }} /> ); return (
{ onPathChange(next === '' ? [] : (JSON.parse(next) as string[])); }} options={[ { value: '', label: '(whole value)' }, ...fields.map((field) => ({ value: JSON.stringify(field.path), label: field.label, })), ]} /> ) : ( pathInput )}
{error !== undefined && !loading ? ( {error} ) : null}
); } export function MappingRowEditor({ row, onChange, sources, suggestions, }: { readonly row: MappingRow; readonly onChange: (row: MappingRow) => void; readonly sources?: BindingSourceSchemas; readonly suggestions?: readonly TemplateJqSuggestion[]; }): ReactNode { const [showJq, setShowJq] = useState(false); const source = row.source; const preview = rowValueJq(source); const setSource = (next: MappingSource): void => { onChange({ ...row, source: next }); }; const changeKind = (kind: string): void => { if (kind === 'field') setSource({ kind: 'field', root: 'output', path: [] }); else if (kind === 'literal') setSource({ kind: 'literal', json: '' }); else setSource({ kind: 'jq', expr: '' }); }; return (
{row.target}