{"version":3,"file":"base.cjs","names":["getEnvironmentVariable","AsyncCaller","mergeSignals","BytesLineDecoder","SSEDecoder","streamWithRetry"],"sources":["../../src/client/base.ts"],"sourcesContent":["import { AsyncCaller, AsyncCallerParams } from \"../utils/async_caller.js\";\nimport { getEnvironmentVariable } from \"../utils/env.js\";\nimport { mergeSignals } from \"../utils/signals.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../utils/sse.js\";\nimport { streamWithRetry, StreamRequestParams } from \"../utils/stream.js\";\nimport type { StreamProtocol } from \"../types.js\";\n\nexport type HeaderValue = string | undefined | null;\n\nexport function* iterateHeaders(\n  headers: HeadersInit | Record<string, HeaderValue>\n): IterableIterator<[string, string | null]> {\n  let iter: Iterable<(HeaderValue | HeaderValue | null[])[]>;\n  let shouldClear = false;\n\n  // eslint-disable-next-line no-instanceof/no-instanceof\n  if (headers instanceof Headers) {\n    const entries: [string, string][] = [];\n    headers.forEach((value, name) => {\n      entries.push([name, value]);\n    });\n    iter = entries;\n  } else if (Array.isArray(headers)) {\n    iter = headers;\n  } else {\n    shouldClear = true;\n    iter = Object.entries(headers ?? {});\n  }\n\n  for (const item of iter) {\n    const name = item[0];\n    if (typeof name !== \"string\")\n      throw new TypeError(\n        `Expected header name to be a string, got ${typeof name}`\n      );\n    const values = Array.isArray(item[1]) ? item[1] : [item[1]];\n    let didClear = false;\n\n    for (const value of values) {\n      if (value === undefined) continue;\n\n      if (shouldClear && !didClear) {\n        didClear = true;\n        yield [name, null];\n      }\n      yield [name, value];\n    }\n  }\n}\n\nexport function mergeHeaders(\n  ...headerObjects: (\n    | HeadersInit\n    | Record<string, HeaderValue>\n    | undefined\n    | null\n  )[]\n) {\n  const outputHeaders = new Headers();\n  for (const headers of headerObjects) {\n    if (!headers) continue;\n    for (const [name, value] of iterateHeaders(headers)) {\n      if (value === null) outputHeaders.delete(name);\n      else outputHeaders.append(name, value);\n    }\n  }\n  const headerEntries: [string, string][] = [];\n  outputHeaders.forEach((value, name) => {\n    headerEntries.push([name, value]);\n  });\n  return Object.fromEntries(headerEntries);\n}\n\n/**\n * Get the API key from the environment.\n * Precedence:\n *   1. explicit argument (if string)\n *   2. LANGGRAPH_API_KEY\n *   3. LANGSMITH_API_KEY\n *   4. LANGCHAIN_API_KEY\n *\n * @param apiKey - API key provided as an argument. If null, skips environment lookup. If undefined, tries environment.\n * @returns The API key if found, otherwise undefined\n */\nexport function getApiKey(apiKey?: string | null): string | undefined {\n  if (apiKey === null) {\n    return undefined;\n  }\n\n  if (apiKey) {\n    return apiKey;\n  }\n\n  const prefixes = [\"LANGGRAPH\", \"LANGSMITH\", \"LANGCHAIN\"];\n\n  for (const prefix of prefixes) {\n    const envKey = getEnvironmentVariable(`${prefix}_API_KEY`);\n    if (envKey) {\n      return envKey.trim().replace(/^[\"']|[\"']$/g, \"\");\n    }\n  }\n\n  return undefined;\n}\n\nexport type RequestHook = (\n  url: URL,\n  init: RequestInit\n) => Promise<RequestInit> | RequestInit;\n\n/**\n * Configuration for {@link BaseClient} and the exported LangGraph SDK\n * {@link Client}.\n */\nexport interface ClientConfig {\n  /**\n   * Base URL of the LangGraph API server.\n   *\n   * Defaults to `http://localhost:8123`, unless the runtime provides a\n   * `langgraph_api:url` global override.\n   */\n  apiUrl?: string;\n  /**\n   * API key for authentication.\n   * - If a string is provided, that key will be used\n   * - If undefined (default), the key will be auto-loaded from environment variables (LANGGRAPH_API_KEY, LANGSMITH_API_KEY, or LANGCHAIN_API_KEY)\n   * - If null, no API key will be set (skips auto-loading)\n   */\n  apiKey?: string | null;\n  /**\n   * Options forwarded to the internal {@link AsyncCaller}, such as retry,\n   * concurrency, or custom `fetch` behavior.\n   */\n  callerOptions?: AsyncCallerParams;\n  /**\n   * Default timeout, in milliseconds, applied to client requests.\n   *\n   * Per-request `timeoutMs` values override this default. Passing `null`\n   * at the request level disables the configured timeout for that request.\n   */\n  timeoutMs?: number;\n  /**\n   * Headers applied to every request.\n   *\n   * The configured API key, when present, is added as the `x-api-key`\n   * header after these defaults are initialized.\n   */\n  defaultHeaders?: Record<string, HeaderValue>;\n  /**\n   * Hook for inspecting or mutating a request before it is sent.\n   *\n   * Receives the resolved URL and prepared `RequestInit`; return the\n   * original init or a replacement object to continue the request.\n   */\n  onRequest?: RequestHook;\n  /**\n   * Streaming protocol used by stream-capable endpoints.\n   *\n   * Defaults to `\"legacy\"` for backwards compatibility.\n   */\n  streamProtocol?: StreamProtocol;\n}\n\nexport class BaseClient {\n  protected asyncCaller: AsyncCaller;\n\n  protected timeoutMs: number | undefined;\n\n  protected apiUrl: string;\n\n  protected defaultHeaders: Record<string, HeaderValue>;\n\n  protected onRequest?: RequestHook;\n\n  protected streamProtocol: StreamProtocol;\n\n  constructor(config?: ClientConfig) {\n    const callerOptions = {\n      maxRetries: 4,\n      maxConcurrency: 4,\n      ...config?.callerOptions,\n    };\n\n    let defaultApiUrl = \"http://localhost:8123\";\n    if (\n      !config?.apiUrl &&\n      typeof globalThis === \"object\" &&\n      globalThis != null\n    ) {\n      const fetchSmb = Symbol.for(\"langgraph_api:fetch\");\n      const urlSmb = Symbol.for(\"langgraph_api:url\");\n\n      const global = globalThis as unknown as {\n        [fetchSmb]?: typeof fetch;\n        [urlSmb]?: string;\n      };\n\n      if (global[fetchSmb]) callerOptions.fetch ??= global[fetchSmb];\n      if (global[urlSmb]) defaultApiUrl = global[urlSmb];\n    }\n\n    this.asyncCaller = new AsyncCaller(callerOptions);\n    this.timeoutMs = config?.timeoutMs;\n\n    this.apiUrl = config?.apiUrl?.replace(/\\/$/, \"\") || defaultApiUrl;\n    this.defaultHeaders = config?.defaultHeaders || {};\n    this.onRequest = config?.onRequest;\n    this.streamProtocol = config?.streamProtocol ?? \"legacy\";\n    const apiKey = getApiKey(config?.apiKey);\n    if (apiKey) {\n      this.defaultHeaders[\"x-api-key\"] = apiKey;\n    }\n  }\n\n  protected prepareFetchOptions(\n    path: string,\n    options?: RequestInit & {\n      json?: unknown;\n      params?: Record<string, unknown>;\n      timeoutMs?: number | null;\n      withResponse?: boolean;\n    }\n  ): [url: URL, init: RequestInit] {\n    const mutatedOptions = {\n      ...options,\n      headers: mergeHeaders(this.defaultHeaders, options?.headers),\n    };\n\n    if (mutatedOptions.json) {\n      mutatedOptions.body = JSON.stringify(mutatedOptions.json);\n      mutatedOptions.headers = mergeHeaders(mutatedOptions.headers, {\n        \"content-type\": \"application/json\",\n      });\n      delete mutatedOptions.json;\n    }\n\n    if (mutatedOptions.withResponse) {\n      delete mutatedOptions.withResponse;\n    }\n\n    let timeoutSignal: AbortSignal | null = null;\n    if (typeof options?.timeoutMs !== \"undefined\") {\n      if (options.timeoutMs != null) {\n        timeoutSignal = AbortSignal.timeout(options.timeoutMs);\n      }\n    } else if (this.timeoutMs != null) {\n      timeoutSignal = AbortSignal.timeout(this.timeoutMs);\n    }\n\n    mutatedOptions.signal = mergeSignals(timeoutSignal, mutatedOptions.signal);\n    const targetUrl = new URL(`${this.apiUrl}${path}`);\n\n    if (mutatedOptions.params) {\n      for (const [key, value] of Object.entries(mutatedOptions.params)) {\n        if (value == null) continue;\n\n        const strValue =\n          typeof value === \"string\" || typeof value === \"number\"\n            ? value.toString()\n            : JSON.stringify(value);\n\n        targetUrl.searchParams.append(key, strValue);\n      }\n      delete mutatedOptions.params;\n    }\n\n    return [targetUrl, mutatedOptions];\n  }\n\n  protected async fetch<T>(\n    path: string,\n    options: RequestInit & {\n      json?: unknown;\n      params?: Record<string, unknown>;\n      timeoutMs?: number | null;\n      signal: AbortSignal | undefined;\n      withResponse: true;\n    }\n  ): Promise<[T, Response]>;\n\n  protected async fetch<T>(\n    path: string,\n    options?: RequestInit & {\n      json?: unknown;\n      params?: Record<string, unknown>;\n      timeoutMs?: number | null;\n      signal: AbortSignal | undefined;\n      withResponse?: false;\n    }\n  ): Promise<T>;\n\n  protected async fetch<T>(\n    path: string,\n    options?: RequestInit & {\n      json?: unknown;\n      params?: Record<string, unknown>;\n      timeoutMs?: number | null;\n      signal: AbortSignal | undefined;\n      withResponse?: boolean;\n    }\n  ): Promise<T | [T, Response]> {\n    const [url, init] = this.prepareFetchOptions(path, options);\n\n    let finalInit = init;\n    if (this.onRequest) {\n      finalInit = await this.onRequest(url, init);\n    }\n\n    const response = await this.asyncCaller.fetch(url.toString(), finalInit);\n\n    const body = (() => {\n      if (response.status === 202 || response.status === 204) {\n        return undefined as T;\n      }\n      return response.json() as Promise<T>;\n    })();\n\n    if (options?.withResponse) {\n      return [await body, response];\n    }\n\n    return body;\n  }\n\n  protected async *streamWithRetry<\n    T extends { id?: string; event: string; data: unknown },\n  >(config: {\n    endpoint: string;\n    method?: string;\n    signal?: AbortSignal;\n    headers?: Record<string, string>;\n    params?: Record<string, unknown>;\n    json?: unknown;\n    maxRetries?: number;\n    onReconnect?: (options: {\n      attempt: number;\n      lastEventId?: string;\n      cause: unknown;\n    }) => void;\n    onInitialResponse?: (response: Response) => void | Promise<void>;\n  }): AsyncGenerator<T> {\n    const makeRequest = async (reconnectParams?: StreamRequestParams) => {\n      const requestEndpoint = reconnectParams?.reconnectPath || config.endpoint;\n\n      const isReconnect = !!reconnectParams?.reconnectPath;\n      const method = isReconnect ? \"GET\" : config.method || \"GET\";\n\n      const requestHeaders =\n        isReconnect && reconnectParams?.lastEventId\n          ? { ...config.headers, \"Last-Event-ID\": reconnectParams.lastEventId }\n          : config.headers;\n\n      // oxlint-disable-next-line prefer-const -- init is reassigned by onRequest hook\n      let [url, init] = this.prepareFetchOptions(requestEndpoint, {\n        method,\n        timeoutMs: null,\n        signal: config.signal,\n        headers: requestHeaders,\n        params: config.params,\n        json: isReconnect ? undefined : config.json,\n      });\n\n      if (this.onRequest != null) {\n        init = await this.onRequest(url, init);\n      }\n\n      const response = await this.asyncCaller.fetch(url.toString(), init);\n      if (!response.body) {\n        throw new Error(\"Expected response body from stream endpoint\");\n      }\n\n      if (!isReconnect && config.onInitialResponse) {\n        await config.onInitialResponse(response);\n      }\n\n      const stream: ReadableStream<T> = response.body\n        .pipeThrough(BytesLineDecoder())\n        .pipeThrough(SSEDecoder()) as ReadableStream<T>;\n\n      return { response, stream };\n    };\n\n    yield* streamWithRetry(makeRequest, {\n      maxRetries: config.maxRetries ?? 5,\n      signal: config.signal,\n      onReconnect: config.onReconnect,\n    });\n  }\n}\n\nexport const REGEX_RUN_METADATA =\n  /(\\/threads\\/(?<thread_id>.+))?\\/runs\\/(?<run_id>.+)/;\n\nexport function getRunMetadataFromResponse(\n  response: Response\n): { run_id: string; thread_id?: string } | undefined {\n  const contentLocation = response.headers.get(\"Content-Location\");\n  if (!contentLocation) return undefined;\n\n  const match = REGEX_RUN_METADATA.exec(contentLocation);\n\n  if (!match?.groups?.run_id) return undefined;\n  return {\n    run_id: match.groups.run_id,\n    thread_id: match.groups.thread_id || undefined,\n  };\n}\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null;\n"],"mappings":"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"}