{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { z, type Action, type ActionContext } from 'genkit';\nimport {\n  getCallableJSON,\n  getHttpStatus,\n  type ContextProvider,\n  type RequestData,\n} from 'genkit/context';\nimport { NextRequest, NextResponse } from 'next/server.js';\nexport { NextRequest, NextResponse, z, type Action, type ActionContext };\n\nconst delimiter = '\\n\\n';\nasync function getContext<C extends ActionContext, T>(\n  request: NextRequest,\n  input: T,\n  provider: ContextProvider<C, T> | undefined\n): Promise<C> {\n  // Type cast is necessary because there is no runtime way to generate a context if C is provided to appRoute\n  // but contextProvider is missing. When I'm less sleepy/busy I'll see if I can make this a type error.\n  const context = {} as C;\n  if (!provider) {\n    return context;\n  }\n\n  const r: RequestData = {\n    method: request.method as RequestData['method'],\n    headers: {},\n    input,\n  };\n  request.headers.forEach((val, key) => {\n    r.headers[key.toLowerCase()] = val;\n  });\n  return await provider(r);\n}\n\nfunction appRoute<\n  C extends ActionContext = ActionContext,\n  I extends z.ZodTypeAny = z.ZodTypeAny,\n  O extends z.ZodTypeAny = z.ZodTypeAny,\n  S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n  action: Action<I, O, S>,\n  opts?: {\n    contextProvider?: ContextProvider<C, I>;\n  }\n) {\n  return async (req: NextRequest): Promise<NextResponse> => {\n    let context: C = {} as C;\n    const { data: input } = await req.json();\n    if (req.headers.get('accept') !== 'text/event-stream') {\n      try {\n        context = await getContext(req, input, opts?.contextProvider);\n      } catch (e) {\n        console.error('Error gathering context for running action:', e);\n        return NextResponse.json(\n          { error: getCallableJSON(e) },\n          { status: getHttpStatus(e) }\n        );\n      }\n      try {\n        const resp = await action.run(input, {\n          context,\n          abortSignal: req.signal,\n        });\n        return NextResponse.json({ result: resp.result });\n      } catch (e) {\n        // For security reasons, log the error rather than responding with it.\n        console.error('Error calling action:', e);\n        return NextResponse.json(\n          { error: getCallableJSON(e) },\n          { status: getHttpStatus(e) }\n        );\n      }\n    }\n\n    try {\n      context = await getContext(req, input, opts?.contextProvider);\n    } catch (e) {\n      console.error('Error gathering context for streaming action:', e);\n      return new NextResponse(\n        `error: ${JSON.stringify(getCallableJSON(e))}${delimiter}END`,\n        { status: getHttpStatus(e) }\n      );\n    }\n    const { output, stream } = action.stream(input, {\n      context,\n      abortSignal: req.signal,\n    });\n    const encoder = new TextEncoder();\n    const { readable, writable } = new TransformStream();\n\n    // Not using a dangling promise causes this closure to block on the stream being drained,\n    // which doesn't happen until the NextResponse is consumed later in the cosure.\n    // TODO: Add ping comments at regular intervals between streaming responses to mitigate\n    // timeouts.\n    (async (): Promise<void> => {\n      const writer = writable.getWriter();\n      try {\n        for await (const chunk of stream) {\n          await writer.write(\n            encoder.encode(\n              `data: ${JSON.stringify({ message: chunk })}${delimiter}`\n            )\n          );\n        }\n        await writer.write(\n          encoder.encode(\n            `data: ${JSON.stringify({ result: await output })}${delimiter}`\n          )\n        );\n        await writer.write(encoder.encode('END'));\n      } catch (err) {\n        console.error('Error streaming action:', err);\n        await writer.write(\n          encoder.encode(\n            `error: ${JSON.stringify(getCallableJSON(err))}` + '\\n\\n'\n          )\n        );\n        await writer.write(encoder.encode('END'));\n      } finally {\n        await writer.close();\n      }\n    })();\n\n    return new NextResponse(readable, {\n      status: 200,\n      headers: {\n        'Content-Type': 'text/event-stream',\n        'Cache-Control': 'no-cache',\n        Connection: 'keep-alive',\n        'Transfer-Encoding': 'chunked',\n      },\n    });\n  };\n}\n\nexport default appRoute;\nexport { appRoute };\n"],"mappings":"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","names":[]}