(
options: Omit<UseGenerationOptions<TInput, TResult>, 'onResult'> & {
onResult?: (result: TResult) => TTransformed
},
)
| 95 | // default, leaving the parameter `any` — a hard error under `strict`. See |
| 96 | // issue #848. |
| 97 | export function useGeneration< |
| 98 | TInput extends Record<string, any>, |
| 99 | TResult, |
| 100 | TTransformed = void, |
| 101 | >( |
| 102 | options: Omit<UseGenerationOptions<TInput, TResult>, 'onResult'> & { |
| 103 | onResult?: (result: TResult) => TTransformed |
| 104 | }, |
| 105 | ): UseGenerationReturn<InferGenerationOutputFromReturn<TResult, TTransformed>> { |
| 106 | type TOutput = InferGenerationOutputFromReturn<TResult, TTransformed> |
| 107 | const hookId = useId() |
| 108 | const clientId = options.id || hookId |
| 109 | |
| 110 | const [result, setResult] = useState<TOutput | null>(null) |
| 111 | const [isLoading, setIsLoading] = useState(false) |
| 112 | const [error, setError] = useState<Error | undefined>(undefined) |
| 113 | const [status, setStatus] = useState<GenerationClientState>('idle') |
| 114 | |
| 115 | const optionsRef = useRef(options) |
| 116 | optionsRef.current = options |
| 117 | |
| 118 | const client = useMemo(() => { |
| 119 | const opts = optionsRef.current |
| 120 | |
| 121 | // Conditional spread for `body` (strict-optional in target; |
| 122 | // local source is `Record<string, any> | undefined`). Callbacks |
| 123 | // wrap optional ones in non-returning bodies so `?.()`'s |
| 124 | // implicit `undefined` doesn't pollute the function return type. |
| 125 | const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = { |
| 126 | id: clientId, |
| 127 | body: opts.body, |
| 128 | devtoolsBridgeFactory: createGenerationDevtoolsBridge, |
| 129 | devtools: { |
| 130 | hookName: 'useGeneration', |
| 131 | framework: 'react', |
| 132 | ...opts.devtools, |
| 133 | }, |
| 134 | // The transform's raw return type (`TTransformed`) and the stored output |
| 135 | // (`TOutput`, with null/void/undefined stripped) are identical at runtime; |
| 136 | // the cast bridges the relationship that the conditional type hides. |
| 137 | onResult: ((r: TResult) => optionsRef.current.onResult?.(r)) as ( |
| 138 | result: TResult, |
| 139 | ) => TOutput | null | void, |
| 140 | onError: (e: Error) => { |
| 141 | optionsRef.current.onError?.(e) |
| 142 | }, |
| 143 | onProgress: (p: number, m?: string) => { |
| 144 | optionsRef.current.onProgress?.(p, m) |
| 145 | }, |
| 146 | onChunk: (c: StreamChunk) => { |
| 147 | optionsRef.current.onChunk?.(c) |
| 148 | }, |
| 149 | onResultChange: setResult, |
| 150 | onLoadingChange: setIsLoading, |
| 151 | onErrorChange: setError, |
| 152 | onStatusChange: setStatus, |
| 153 | } |
| 154 |
no test coverage detected