{"version":3,"file":"a2a-mock.cjs","names":["createJsonRpcDispatcher","createA2AMethods","buildAgentCard","readBody","flattenHeaders","http","extractText","findStreamingMatch","generateId","TERMINAL_STATES"],"sources":["../src/a2a-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n  A2AAgentDefinition,\n  A2AArtifact,\n  A2AMockOptions,\n  A2APart,\n  A2AStreamEvent,\n  A2ATask,\n} from \"./a2a-types.js\";\nimport type { PatternEntry } from \"./a2a-handler.js\";\nimport {\n  buildAgentCard,\n  createA2AMethods,\n  extractText,\n  findStreamingMatch,\n  TERMINAL_STATES,\n} from \"./a2a-handler.js\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport { generateId, flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class A2AMock implements Mountable {\n  private agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }> = new Map();\n  private tasks: Map<string, A2ATask> = new Map();\n  private server: http.Server | null = null;\n  private journal: Journal | null = null;\n  private registry: MetricsRegistry | null = null;\n  private options: A2AMockOptions;\n  private baseUrl = \"\";\n  private dispatcher: ReturnType<typeof createJsonRpcDispatcher>;\n\n  constructor(options?: A2AMockOptions) {\n    this.options = options ?? {};\n    this.dispatcher = this.buildDispatcher();\n  }\n\n  private buildDispatcher() {\n    const methods = createA2AMethods(this.agents, this.tasks);\n    return createJsonRpcDispatcher({ methods });\n  }\n\n  // ---- Agent registration ----\n\n  registerAgent(def: A2AAgentDefinition): this {\n    this.agents.set(def.name, { def, patterns: [] });\n    return this;\n  }\n\n  // ---- Pattern registration ----\n\n  onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this {\n    const agent = this.agents.get(agentName);\n    if (!agent) {\n      throw new Error(`Agent \"${agentName}\" not registered`);\n    }\n    agent.patterns.push({ kind: \"message\", pattern, agentName, parts });\n    return this;\n  }\n\n  onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this {\n    const agent = this.agents.get(agentName);\n    if (!agent) {\n      throw new Error(`Agent \"${agentName}\" not registered`);\n    }\n    agent.patterns.push({ kind: \"task\", pattern, agentName, artifacts });\n    return this;\n  }\n\n  onStreamingTask(\n    agentName: string,\n    pattern: string | RegExp,\n    events: A2AStreamEvent[],\n    delayMs?: number,\n  ): this {\n    const agent = this.agents.get(agentName);\n    if (!agent) {\n      throw new Error(`Agent \"${agentName}\" not registered`);\n    }\n    agent.patterns.push({ kind: \"streamingTask\", pattern, agentName, events, delayMs });\n    return this;\n  }\n\n  // ---- Mountable interface ----\n\n  async handleRequest(\n    req: http.IncomingMessage,\n    res: http.ServerResponse,\n    pathname: string,\n  ): Promise<boolean> {\n    // Agent card endpoint\n    if (req.method === \"GET\" && pathname === \"/.well-known/agent-card.json\") {\n      if (this.registry) {\n        this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: \"GetAgentCard\" });\n      }\n      const card = buildAgentCard(this.agents, this.baseUrl);\n      res.writeHead(200, {\n        \"Content-Type\": \"application/json\",\n        \"A2A-Version\": \"1.0\",\n      });\n      res.end(JSON.stringify(card));\n      return true;\n    }\n\n    // JSON-RPC endpoint\n    if (req.method === \"POST\" && (pathname === \"/\" || pathname === \"\")) {\n      const body = await readBody(req);\n\n      // Check for SendStreamingMessage before dispatching\n      let parsed: unknown;\n      try {\n        parsed = JSON.parse(body);\n      } catch {\n        res.writeHead(200, {\n          \"Content-Type\": \"application/json\",\n          \"A2A-Version\": \"1.0\",\n        });\n        res.end(\n          JSON.stringify({\n            jsonrpc: \"2.0\",\n            id: null,\n            error: { code: -32700, message: \"Parse error\" },\n          }),\n        );\n        return true;\n      }\n\n      // Record A2A method metric\n      if (this.registry) {\n        const rpcMethod =\n          typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n            ? String((parsed as Record<string, unknown>).method)\n            : \"unknown\";\n        this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: rpcMethod });\n      }\n\n      if (isStreamingRequest(parsed)) {\n        await this.handleStreamingMessage(parsed as Record<string, unknown>, req, res);\n        return true;\n      }\n\n      // Regular JSON-RPC dispatch\n      // Add A2A-Version header before dispatching\n      res.setHeader(\"A2A-Version\", \"1.0\");\n\n      await this.dispatcher(req, res, body);\n\n      // Journal the request after the handler completes\n      if (this.journal) {\n        this.journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: null,\n          service: \"a2a\",\n          response: { status: res.statusCode, fixture: null },\n        });\n      }\n\n      return true;\n    }\n\n    return false;\n  }\n\n  health(): { status: string; agents: number; tasks: number } {\n    return {\n      status: \"ok\",\n      agents: this.agents.size,\n      tasks: this.tasks.size,\n    };\n  }\n\n  setJournal(journal: Journal): void {\n    this.journal = journal;\n  }\n\n  setRegistry(registry: MetricsRegistry): void {\n    this.registry = registry;\n  }\n\n  // ---- Standalone mode ----\n\n  async start(): Promise<string> {\n    if (this.server) {\n      throw new Error(\"A2AMock server already started\");\n    }\n\n    const host = this.options.host ?? \"127.0.0.1\";\n    const port = this.options.port ?? 0;\n\n    return new Promise<string>((resolve, reject) => {\n      const srv = http.createServer(async (req, res) => {\n        const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n        await this.handleRequest(req, res, url.pathname).catch((err) => {\n          console.error(\"A2AMock request error:\", err);\n          if (!res.headersSent) {\n            res.writeHead(500);\n            res.end(\"Internal server error\");\n          } else if (!res.writableEnded) {\n            res.end();\n          }\n        });\n      });\n\n      srv.on(\"error\", reject);\n\n      srv.listen(port, host, () => {\n        const addr = srv.address();\n        if (typeof addr === \"object\" && addr !== null) {\n          this.baseUrl = `http://${host}:${addr.port}`;\n        }\n        this.server = srv;\n        resolve(this.baseUrl);\n      });\n    });\n  }\n\n  async stop(): Promise<void> {\n    if (!this.server) {\n      throw new Error(\"A2AMock server not started\");\n    }\n    const srv = this.server;\n    await new Promise<void>((resolve, reject) => {\n      srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n    });\n    this.server = null;\n  }\n\n  get url(): string {\n    if (!this.server) {\n      throw new Error(\"A2AMock server not started\");\n    }\n    return this.baseUrl;\n  }\n\n  // ---- Reset ----\n\n  reset(): this {\n    this.agents.clear();\n    this.tasks.clear();\n    return this;\n  }\n\n  // ---- Internal: set base URL when mounted ----\n\n  setBaseUrl(url: string): void {\n    this.baseUrl = url;\n  }\n\n  // ---- Private: streaming handler ----\n\n  private async handleStreamingMessage(\n    parsed: Record<string, unknown>,\n    req: http.IncomingMessage,\n    res: http.ServerResponse,\n  ): Promise<void> {\n    const params = parsed.params as Record<string, unknown> | undefined;\n    const id = parsed.id as string | number;\n    const text = extractText(params);\n    const entry = findStreamingMatch(text, this.agents);\n\n    if (!entry) {\n      res.writeHead(200, {\n        \"Content-Type\": \"application/json\",\n        \"A2A-Version\": \"1.0\",\n      });\n      res.end(\n        JSON.stringify({\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32000, message: \"No matching pattern for message\" },\n        }),\n      );\n      return;\n    }\n\n    // Create task for the streaming response\n    const taskId = generateId(\"task\");\n    const contextId = generateId(\"ctx\");\n    const userParts: A2APart[] = params?.message\n      ? (((params.message as Record<string, unknown>).parts as A2APart[]) ?? [{ text }])\n      : [{ text }];\n\n    const task: A2ATask = {\n      id: taskId,\n      contextId,\n      status: { state: \"TASK_STATE_WORKING\", timestamp: new Date().toISOString() },\n      artifacts: [],\n      history: [\n        {\n          messageId: generateId(\"msg\"),\n          role: \"ROLE_USER\",\n          parts: userParts,\n        },\n      ],\n    };\n    this.tasks.set(taskId, task);\n\n    // Write SSE response\n    res.writeHead(200, {\n      \"Content-Type\": \"text/event-stream\",\n      \"Cache-Control\": \"no-cache\",\n      Connection: \"keep-alive\",\n      \"A2A-Version\": \"1.0\",\n    });\n\n    const delayMs = entry.delayMs ?? 0;\n\n    for (const event of entry.events) {\n      if (delayMs > 0) {\n        await delay(delayMs);\n      }\n\n      let resultPayload: Record<string, unknown>;\n\n      if (event.type === \"status\") {\n        task.status = { state: event.state, timestamp: new Date().toISOString() };\n        resultPayload = {\n          task: {\n            id: task.id,\n            contextId: task.contextId,\n            status: task.status,\n          },\n        };\n      } else {\n        // artifact event\n        const artifact = {\n          parts: event.parts,\n          name: event.name,\n          append: event.append,\n          lastChunk: event.lastChunk,\n        };\n        task.artifacts.push({ parts: event.parts, name: event.name });\n        resultPayload = {\n          task: {\n            id: task.id,\n            contextId: task.contextId,\n            status: task.status,\n          },\n          artifact,\n        };\n      }\n\n      const envelope = JSON.stringify({\n        jsonrpc: \"2.0\",\n        id,\n        result: resultPayload,\n      });\n\n      res.write(`data: ${envelope}\\n\\n`);\n    }\n\n    // Final completion — only set COMPLETED if the task is not already in a terminal state\n    if (!TERMINAL_STATES.has(task.status.state)) {\n      task.status = { state: \"TASK_STATE_COMPLETED\", timestamp: new Date().toISOString() };\n    }\n\n    res.end();\n\n    // Journal\n    if (this.journal) {\n      this.journal.add({\n        method: \"POST\",\n        path: \"/\",\n        headers: flattenHeaders(req.headers),\n        body: null,\n        service: \"a2a\",\n        response: { status: res.statusCode, fixture: null },\n      });\n    }\n  }\n}\n\n// ---- Helpers ----\n\nfunction isStreamingRequest(parsed: unknown): boolean {\n  if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n  const obj = parsed as Record<string, unknown>;\n  return obj.method === \"SendStreamingMessage\";\n}\n\nfunction delay(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}