| 75 | // default, leaving the parameter `any` — a hard error under `strict`. See |
| 76 | // issue #848. |
| 77 | export function injectGeneration< |
| 78 | TInput extends Record<string, any>, |
| 79 | TResult, |
| 80 | TTransformed = void, |
| 81 | >( |
| 82 | options: Omit<InjectGenerationOptions<TInput, TResult>, 'onResult'> & { |
| 83 | onResult?: (result: TResult) => TTransformed |
| 84 | }, |
| 85 | ): InjectGenerationResult< |
| 86 | InferGenerationOutputFromReturn<TResult, TTransformed> |
| 87 | > { |
| 88 | assertInInjectionContext(injectGeneration) |
| 89 | |
| 90 | type TOutput = InferGenerationOutputFromReturn<TResult, TTransformed> |
| 91 | |
| 92 | const destroyRef = inject(DestroyRef) |
| 93 | const injector = inject(Injector) |
| 94 | const clientId = options.id || `injectGeneration-${nextId++}` |
| 95 | |
| 96 | const result = signal<TOutput | null>(null) |
| 97 | const isLoading = signal(false) |
| 98 | const error = signal<Error | undefined>(undefined) |
| 99 | const status = signal<GenerationClientState>('idle') |
| 100 | |
| 101 | const bodySource = |
| 102 | options.body !== undefined ? toReactive(options.body) : undefined |
| 103 | |
| 104 | const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = { |
| 105 | id: clientId, |
| 106 | ...(bodySource !== undefined && { body: bodySource() }), |
| 107 | devtoolsBridgeFactory: createGenerationDevtoolsBridge, |
| 108 | devtools: { |
| 109 | ...options.devtools, |
| 110 | framework: 'angular', |
| 111 | hookName: 'injectGeneration', |
| 112 | }, |
| 113 | // The transform's raw return type (`TTransformed`) and the stored output |
| 114 | // (`TOutput`, with null/void/undefined stripped) are identical at runtime; |
| 115 | // the cast bridges the relationship that the conditional type hides. |
| 116 | onResult: ((r: TResult) => options.onResult?.(r)) as ( |
| 117 | result: TResult, |
| 118 | ) => TOutput | null | void, |
| 119 | onError: (e: Error) => options.onError?.(e), |
| 120 | onProgress: (p: number, m?: string) => options.onProgress?.(p, m), |
| 121 | onChunk: (c: StreamChunk) => options.onChunk?.(c), |
| 122 | onResultChange: (r: TOutput | null) => result.set(r), |
| 123 | onLoadingChange: (l: boolean) => isLoading.set(l), |
| 124 | onErrorChange: (e: Error | undefined) => error.set(e), |
| 125 | onStatusChange: (s: GenerationClientState) => status.set(s), |
| 126 | } |
| 127 | |
| 128 | let client: GenerationClient<TInput, TResult, TOutput> |
| 129 | if (options.connection) { |
| 130 | client = new GenerationClient({ |
| 131 | ...clientOptions, |
| 132 | connection: options.connection, |
| 133 | }) |
| 134 | } else if (options.fetcher) { |